ES6简述
ES6(ECMAScript 2015)是JavaScript语言的一个重要版本,它引入了许多新的特性和语法,旨在改善代码的可读性和可维护性。虽然用了多年了有一些知识点还是比较隐匿的,并且容易忽略掉。。但是又比较实用。。。es6 ruanyifeng、MDN web docs
Reflect
Reflect
是 ES6 引入的一个内置对象,提供了多种方法用于操作对象。它的设计目的是为了简化某些操作并提供更一致的行为。Reflect API 参考
<script>
// Reflect
const obj = {name: 'bob', age: 25};
// obj.name = 'alice';
// 设置属性
Reflect.set(obj, 'name', 'alice');
console.log(obj); // {name: 'alice', age: 25}
// 获取属性
console.log(Reflect.get(obj, 'name')); // alice
// 遍历对象
const keys = Reflect.ownKeys(obj);
console.log(keys); // ['name', 'age']
// 调用方法
const result = Reflect.apply(Math.pow, null, [2, 3]);
console.log(result); // 8
// 实例属性
const person = {
name: 'alice',
sayName() {
console.log(this.name);
}
};
const sayName = person.sayName;
sayName(); // alice
// 绑定方法
const boundSayName = sayName.bind(person);
boundSayName(); // alice
// 实例属性
const person2 = {
name: 'alice',
sayName() {
console.log(this.name);
}
};
const sayName2 = person2.sayName;
sayName2(); // alice
// 绑定方法
const boundSayName2 = sayName2.bind(person2);
boundSayName2(); // alice
// 删除属性
Reflect.deleteProperty(obj, 'age');
console.log(obj); // {name: 'alice'}
// 判断对象中是否有属性
console.log(Reflect.has(obj, 'name')); // true
console.log(Reflect.has(obj, 'age')); // false
// 构造函数
const Person = function(name, age) {
this.name = name;
this.age = age;
};
const person3 = new Person('bob', 25);
console.log(person3); // {name: 'bob', age: 25}
// 继承
const person4 = Reflect.construct(Person, ['bob', 25]);
console.log(person4); // {name: 'bob', age: 25}
// 劫持属性
const handler = {
get(target, key) {
if (key === 'name') {
return 'alice';
}
return target[key];
}
};
const proxy = new Proxy(obj, handler);
console.log(proxy.name); // alice
// Reflect 劫持属性
const proxy2 = new Proxy(obj, {
get(target, key) {
if (key === 'name') {
return 'alice';
}
return Reflect.get(target, key);
}
});
console.log(proxy2.name); // alice
</script>
class & Proxy
利用Proxy
和Reflect
特性,使得构造函数的使用更加灵活和简洁
// 简化构造函数 使用 new isProxy
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
// 简化
class Student {
}
// 函数简化构造函数的调用
function createProxy(className, ...propsNames){
return new Proxy(className, {
construct(target, args) {
const obj = Reflect.construct(target, args);
propsNames.forEach((propName, index) => {
obj[propName] = args[index];
});
return obj;
}
})
}
const PersonProxy = createProxy(Person, 'name', 'age');
const student = new PersonProxy('bob', 24);
console.log(student); // {name: "bob", age: 24}
迭代器(Iterator) 和 生成器(Generator)
1. 迭代器(Iterator)
定义: 迭代器是一种对象,它实现了迭代协议(iteration protocol),即具有一个next()
方法,该方法返回一个包含value
和done
两个属性的对象。
value
:当前迭代的值。done
:一个布尔值,表示迭代是否结束。
实现迭代器: 要创建一个迭代器,你需要定义一个具有next()
方法的对象。例如:
const myIterator = {
current: 0,
last: 5,
next() {
if (this.current < this.last) {
return { value: this.current++, done: false };
} else {
return { done: true };
}
}
};
console.log(myIterator.next()); // { value: 0, done: false }
console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: false }
console.log(myIterator.next()); // { value: 4, done: false }
console.log(myIterator.next()); // { done: true }
使用的场景: 迭代器常用于需要惰性求值的场景,例如遍历数据结构(如数组、集合)等。通过迭代器,可以逐步读取数据,而不需要一次性加载全部数据。
2. 生成器(Generator)
定义: 生成器是一种特殊的函数,使用function*
语法定义,可以通过yield
语句来暂停和恢复函数的执行。调用生成器函数会返回一个迭代器。
特性:
- 生成器可以多次调用,内部状态会被保存,可以随时恢复执行。
- 每次遇到
yield
时,函数会返回一个值,并且保留执行状态,下次调用next()
时从yield
处继续执行。
实现生成器: 可以通过如下方式定义并使用生成器:
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = myGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
3. 迭代器与生成器的联系与区别
-
联系:
- 生成器本质上就是一个迭代器,它实现了迭代器协议。
- 所有生成器返回的都是迭代器对象。
-
区别:
- 迭代器是一个普通对象,有
next()
方法;而生成器函数则用function*
定义,可以通过yield
控制执行流。 - 使用生成器可以更方便地创建复杂的迭代逻辑,而不必手动维护状态。
- 迭代器是一个普通对象,有
4. 异步生成器(Async Generator)
定义: 异步生成器是一种特殊的生成器,可以使用async function*
语法定义,它允许使用await
关键字来处理异步操作。
特点:
- 处理异步操作时,可以逐步返回值。
- 使用
await
可以等待异步操作完成后再执行生成器的下一步。
示例: 下面是一个简单的异步生成器示例:
async function* asyncGen() {
const values = [1, 2, 3];
for (const value of values) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
yield value;
}
}
const ag = asyncGen();
5. 异步迭代器(Async Iterator)
定义: 异步迭代器是一种对象,它实现了异步迭代协议,包含一个next()
方法。该方法返回一个Promise,解析后包含value
和done
两个属性。
使用方法: 异步迭代器常用于遍历需要异步处理的数据源,如网络请求、数据库访问等。
示例: 下面是如何使用异步迭代器的示例:
let asyncArr = [1, 2, 3, 4, 5];
// 创建异步迭代器
let asyncIterator = asyncArr[Symbol.asyncIterator]();
// 使用 async/await 逐步获取值
(async () => {
let result = await asyncIterator.next();
while (!result.done) {
console.log(result.value); // 输出当前值
result = await asyncIterator.next(); // 获取下一个值
}
})();
生成器(Generator)异步任务控制
生成器(Generator)在JavaScript中不仅可以用于同步任务的控制,还可以结合yield
和Promise
来实现异步任务的控制。通过这种方式,可以更优雅地管理复杂的异步流程,避免“回调地狱”或过多的Promise
链式调用。
生成器与异步任务的基本原理
生成器通过yield
关键字可以暂停函数的执行,并在需要时恢复执行。结合Promise
,可以在yield
处等待异步任务完成,然后再继续执行后续代码。
核心思想:
- 使用
yield
暂停生成器函数的执行。 - 使用
Promise
处理异步任务。 - 通过生成器的
next()
方法恢复执行,并传递异步任务的结果。
实现异步任务控制的步骤
步骤 1:定义一个生成器函数
生成器函数使用function*
定义,并在需要等待异步任务的地方使用yield
。
步骤 2:编写一个任务运行器(Runner)
任务运行器的作用是自动执行生成器函数,处理yield
返回的Promise
,并将结果传递回生成器。
步骤 3:使用生成器控制异步任务
通过生成器函数和任务运行器,可以清晰地表达异步任务的执行顺序。
示例代码
以下是一个完整的示例,展示如何使用生成器控制异步任务:
// 模拟异步任务
function asyncTask(value, delay) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`Task ${value} completed after ${delay}ms`);
resolve(value);
}, delay);
});
}
// 生成器函数,定义异步任务的执行顺序
function* asyncGenerator() {
const result1 = yield asyncTask(1, 1000); // 等待第一个任务完成
console.log(`Result of task 1: ${result1}`);
const result2 = yield asyncTask(2, 500); // 等待第二个任务完成
console.log(`Result of task 2: ${result2}`);
const result3 = yield asyncTask(3, 800); // 等待第三个任务完成
console.log(`Result of task 3: ${result3}`);
return "All tasks completed!";
}
// 任务运行器
function runGenerator(generator) {
const iterator = generator(); // 获取生成器迭代器
function handle(iteration) {
if (iteration.done) {
return Promise.resolve(iteration.value); // 如果生成器结束,返回最终结果
}
return Promise.resolve(iteration.value) // 等待 yield 返回的 Promise 完成
.then((result) => {
return handle(iterator.next(result)); // 将结果传递回生成器,继续执行
})
.catch((error) => {
return handle(iterator.throw(error)); // 处理错误
});
}
return handle(iterator.next()); // 启动生成器
}
// 运行生成器
runGenerator(asyncGenerator)
.then((finalResult) => {
console.log(finalResult); // 输出最终结果
})
.catch((error) => {
console.error("Error:", error);
});
输出结果
运行上述代码后,输出如下:
Task 1 completed after 1000ms
Result of task 1: 1
Task 2 completed after 500ms
Result of task 2: 2
Task 3 completed after 800ms
Result of task 3: 3
All tasks completed!
实际应用场景
- 分步加载数据:例如,先加载用户信息,再加载用户的订单列表,最后加载订单详情。
- 任务队列:按顺序执行一系列异步任务。
- 复杂流程控制:例如,游戏中的任务流程或动画序列。
Set
和Map
在ES6中,引入了Set
和Map
这两种新的数据结构,它们在数据存储和操作上各具特点。
1. 定义
-
Set:
Set
是一种集合,表示一个不重复的值的无序集合。它允许存储任何类型的唯一值,包括原始值和对象引用。
-
Map:
Map
是一种键值对集合,允许使用对象作为键。与普通对象相比,Map
的键可以是任何类型的值。
2. 特性对比
特性 | Set | Map |
---|---|---|
存储结构 | 无序集合,存储唯一值 | 有序集合,存储键值对 |
键/值类型 | 只有值,没有键 | 键和值都可以是任何类型 |
允许重复 | 不允许重复值 | 键不允许重复,值可以重复 |
插入顺序 | 保留插入的顺序 | 保留插入顺序 |
大小 | 可以通过 size 属性获取集合的大小 | 可以通过 size 属性获取映射的大小 |
常用方法 | add(value) 、has(value) 、delete(value) 、clear() | set(key, value) 、get(key) 、has(key) 、delete(key) 、clear() |
迭代方法 | forEach 、for...of | forEach 、for...of |
性能 | 对于某些操作,性能较好(如去重、查找) | 键的查找性能较优 |
3. 常用操作
3.1 Set 的操作示例
const mySet = new Set();
// 添加元素
mySet.add(1);
mySet.add(2);
mySet.add(2); // 重复的值不会被添加
mySet.add('hello');
mySet.add({ a: 1 }); // 可以添加对象
console.log(mySet.size); // 4
// 检查元素是否存在
console.log(mySet.has(1)); // true
console.log(mySet.has(3)); // false
// 删除元素
mySet.delete(2);
console.log(mySet.size); // 3
// 遍历
mySet.forEach(value => {
console.log(value);
});
// 可以使用 for...of 进行遍历
for (let item of mySet) {
console.log(item);
}
3.2 Map 的操作示例
const myMap = new Map();
// 添加键值对
myMap.set('name', 'Alice');
myMap.set('age', 30);
myMap.set(1, 'One'); // 可以使用数字作为键
myMap.set(true, 'Boolean'); // 可以使用布尔值作为键
console.log(myMap.size); // 4
// 获取值
console.log(myMap.get('name')); // Alice
// 检查键是否存在
console.log(myMap.has('age')); // true
console.log(myMap.has(2)); // false
// 删除键值对
myMap.delete(1);
console.log(myMap.size); // 3
// 遍历
myMap.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// 使用 for...of 遍历
for (let [key, value] of myMap) {
console.log(`${key}: ${value}`);
}
4. 使用场景
-
Set:
- 用于存储唯一值的场景,例如去重操作。
- 可用于跟踪某些元素是否存在(如用户ID、标签等)。
-
Map:
- 存储关联数据的场景,例如构建对象属性(对象的键值对)。
- 应用于需要快速查找的情境,例如缓存或索引。
WeakSet
和WeakMap
WeakSet
和WeakMap
是ES6中引入的两个新的数据结构,它们与Set
和Map
的主要区别在于对存储的对象引用的处理。二者通过弱引用(weak reference)来避免对对象的强引用,从而在某些情况下帮助实现垃圾回收。
1. 定义
-
WeakSet:
WeakSet
是一个只存储对象的集合,其中的对象是弱引用。换句话说,如果没有其他强引用指向WeakSet
中的对象,垃圾回收机制可以回收这些对象。
-
WeakMap:
WeakMap
是一个键值对集合,其中的键是弱引用。它的键只能是对象,并且如果没有其他强引用指向这些键,则它们会被垃圾回收。
2. 特性对比
特性 | WeakSet | WeakMap |
---|---|---|
存储内容 | 只能存储对象,不能存储原始值 | 键是对象,值可以是任何类型 |
引用类型 | 弱引用,只有对象引用 | 弱引用,只能是对象作为键 |
垃圾回收 | 如果集合中没有其他引用,存储的对象可以被回收 | 如果没有其他引用,存储的键值对可以被回收 |
可用方法 | add(value) 、has(value) 、delete(value) | set(key, value) 、get(key) 、has(key) 、delete(key) |
遍历 | 不能被遍历(没有遍历方法) | 不能被遍历(没有遍历方法) |
大小 | 不支持获取大小(没有 size 属性) | 不支持获取大小(没有 size 属性) |
性能 | 通常在垃圾回收时更加高效 | 通常在垃圾回收时更加高效 |
3. 常用操作示例
3.1 WeakSet 的操作示例
const weakSet = new WeakSet();
// 创建对象
let obj1 = { name: "Alice" };
let obj2 = { name: "Bob" };
// 添加对象
weakSet.add(obj1);
weakSet.add(obj2);
// 检查对象是否存在
console.log(weakSet.has(obj1)); // true
console.log(weakSet.has({ name: "Charlie" })); // false
// 删除对象
weakSet.delete(obj1);
console.log(weakSet.has(obj1)); // false
// 注意:不能获取 WeakSet 的大小或进行遍历
3.2 WeakMap 的操作示例
const weakMap = new WeakMap();
// 创建对象
let key1 = {};
let key2 = {};
// 将对象作为键,存储值
weakMap.set(key1, "Value for key1");
weakMap.set(key2, "Value for key2");
// 获取值
console.log(weakMap.get(key1)); // Value for key1
console.log(weakMap.get(key2)); // Value for key2
// 检查键是否存在
console.log(weakMap.has(key1)); // true
console.log(weakMap.has({})); // false
// 删除键值对
weakMap.delete(key1);
console.log(weakMap.has(key1)); // false
// 注意:不能获取 WeakMap 的大小或进行遍历
4. 使用场景
-
WeakSet:
- 可用于存储需要在垃圾回收时被自动清理的对象,使得在对象生命周期内的引用不会妨碍垃圾回收。
- 常见场景包括跟踪对象的元数据,或动态创建某些对象的状态。
-
WeakMap:
- 适合用于将附加信息关联到对象,且不希望阻止这些对象的垃圾回收。
- 常见场景包括实现私有属性、缓存数据、或以对象作为键的映射。
属性描述符 (js自带有利于理解es6关联内容)
在JavaScript中,属性描述符是用于描述对象属性的一组特性,它们包括了属性的行为及其特性。通过属性描述符,我们可以控制对象属性的可写性、可枚举性和可配置性。
1. 属性描述符的类型
JavaScript中有两种类型的属性描述符:
- 数据描述符(Data Descriptor):包含值的属性。
- 访问器描述符(Accessor Descriptor):使用 getter 和 setter 方法来控制属性的访问。
2. 数据描述符
数据描述符是具有以下特性的对象:
- value: 属性的值。
- writable: 一个布尔值,表示该属性是否可以被修改。
- enumerable: 一个布尔值,表示是否可以通过
for...in
或Object.keys()
等方法枚举该属性。 - configurable: 一个布尔值,表示该属性是否可以被删除,或者如果该属性是数据描述符,是否可以改变它的特性(如
writable
)。
示例:数据描述符
const obj = {};
// 定义一个数据描述符
Object.defineProperty(obj, 'name', {
value: 'Alice',
writable: true,
enumerable: true,
configurable: true
});
console.log(obj.name); // Alice
// 修改值
obj.name = 'Bob';
console.log(obj.name); // Bob
// 删除属性
delete obj.name;
console.log(obj.name); // undefined
3. 访问器描述符
访问器描述符是具有以下特性的对象:
- get: 一个函数,当读取属性时调用。
- set: 一个函数,当写入属性时调用。
- enumerable: 一个布尔值,表示是否可以枚举该属性。
- configurable: 一个布尔值,表示该属性是否可以被删除,或是否可以改变描述符特性。
示例:访问器描述符
const obj = {
firstName: 'John',
lastName: 'Doe',
};
// 定义访问器描述符
Object.defineProperty(obj, 'fullName', {
get: function() {
return `${this.firstName} ${this.lastName}`;
},
set: function(value) {
const parts = value.split(' ');
this.firstName = parts[0];
this.lastName = parts[1];
},
enumerable: true,
configurable: true
});
// 读 get()
console.log(obj.fullName); // John Doe
// 修改 fullName (写)set(xx)
obj.fullName = 'Jane Smith';
console.log(obj.firstName); // Jane
console.log(obj.lastName); // Smith
4. 查询属性描述符
可以使用 Object.getOwnPropertyDescriptor
方法来获取对象属性的描述符。
示例:
const obj = {};
Object.defineProperty(obj, 'name', {
value: 'Alice',
writable: false,
enumerable: true,
configurable: false
});
// 获取属性描述符
const descriptor = Object.getOwnPropertyDescriptor(obj, 'name');
console.log(descriptor);
// 输出:
// {
// value: 'Alice',
// writable: false,
// enumerable: true,
// configurable: false
// }
var
和 let
和 const
ES6 之前 var声明变量造成的问题
- 允许重复的变量声明,导致数据被覆盖
- 变量提升(例如闭包)
- 全局变量挂在到到全局对象:全局对象成员被污染 (例如
window.name
)
1. 函数作用域(Function Scope)
使用var
声明的变量具有函数作用域,这意味着变量的作用域限于其所在的函数内部。如果在函数外部使用var
声明变量,则该变量会成为全局变量,但这可能会导致意外的命名冲突。
示例:
function example() {
var x = 10; // x 在这个函数内部可用
}
example();
console.log(x); // ReferenceError: x is not defined
如果在函数外部声明var
,则可能会引发意外问题:
var globalVar = 'I am global';
function scopeExample() {
var localVar = 'I am local';
}
console.log(globalVar); // I am global
console.log(localVar); // ReferenceError: localVar is not defined
2. 变量提升(Hoisting)
使用var
声明的变量会被提升到其作用域的顶部。这意味着在声明之前可以引用变量,但它的值为undefined
,这可能导致意想不到的错误。
示例:
console.log(a); // undefined
var a = 5;
console.log(a); // 5
因为var a
会被提升,所以上述代码输出undefined
。许多新手可能会认为在console.log(a)
时a
已被赋值,但实际上只有声明被提升。
3. 重复声明
在同一作用域内,使用var
可以重复声明同一变量,可能会导致意外的重写,增加了代码的复杂性及出错的风险。
示例:
var b = 10;
var b = 20; // 没有报错
console.log(b); // 20
重复声明会覆盖原有值,这样可能导致逻辑错误。
4. 闭包中的问题
当使用var
在循环中创建函数时,所有函数共享同一个变量的引用,因为var
是函数作用域的,因此会导致在异步回调中引用到最后一次循环的值。
示例:
var funcs = [];
for (var i = 0; i < 5; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(func => func()); // 输出 5,五次
在这里,所有的函数都引用了同一个i
,因此在所有回调中输出的是i
的最终值(5)。
5. 解决方案
为了解决var
带来的问题,JavaScript在ES6中引入了let
和const
这两个关键字,它们具备以下优势:
- 块级作用域:
let
和const
具有块级作用域,只在其所在的代码块内有效。 - 不提升(Hoisting) :虽然它们也具有提升特性,但不会初始化为
undefined
,引用前必须声明。 - 禁止重复声明:在相同作用域内,使用
let
和const
声明变量会抛出错误。
示例:
for (let j = 0; j < 5; j++) {
setTimeout(() => {
console.log(j); // 输出 0 1 2 3 4
}, 100);
}
var const let 对比和区别
特性 | var | let | const |
---|---|---|---|
作用域 | 函数作用域;如果在函数外,则全局作用域 | 块级作用域 | 块级作用域 |
变量提升 | 提升;未初始化为undefined | 提升;在定义前不可访问 | 提升;在定义前不可访问 |
可变性 | 可重新赋值 | 可重新赋值 | 不可重新赋值(声明常量) |
重复声明 | 允许 | 不允许 | 不允许 |
箭头函数
历史问题 this指向(当前执行上下文的一个特殊对象)
- 通过对象调用函数,this指向对象
- 直接调用函数,this指向全局对象 window
- 通过new调用函数,this指向新创建的对象
- 通过apply、call、bind调用函数 this指向指定的数据
- 如果是dom事件函数,this指向事件源
普通函数与箭头函数的区别
this
绑定的差异
-
普通函数:
this
在普通函数中是动态绑定的:它依赖于函数调用的上下文。可以通过不同的方式(如call
、apply
、bind
)改变this
的值。function greet() { console.log(this.name); } const person = { name: 'Alice' }; greet.call(person); // 输出 'Alice'
箭头函数: 箭头函数没有自己的this
,它从定义时的外部作用域中继承this
的值。这就意味着在箭头函数中,this
的值是固定的,不能被动态改变。
const person = {
name: 'Alice',
greet: function() {
const innerGreet = () => {
console.log(this.name);
};
innerGreet();
}
};
person.greet(); // 输出 'Alice'
其他差异
-
构造函数: 普通函数可以作为构造函数,但是箭头函数不能。
function Person(name) { this.name = name; } const alice = new Person('Alice'); // 可以 const ArrowPerson = (name) => { this.name = name; }; // const bob = new ArrowPerson('Bob'); // TypeError: ArrowPerson is not a constructor
-
arguments
对象: 普通函数有自己的arguments
对象,但箭头函数没有。
function showArgs() {
console.log(arguments);
}
showArgs(1, 2, 3); // 输出 { '0': 1, '1': 2, '2': 3 }
const arrowShowArgs = () => {
console.log(arguments); // ReferenceError: arguments is not defined
};
// arrowShowArgs(1, 2, 3);
小结
this
关键字的值依赖于函数的调用方式,普通函数的this
是动态变化的,而箭头函数的this
是静态绑定的,继承自外部作用域。- 普通函数可以作为构造函数,拥有
arguments
对象;而箭头函数无法作为构造函数,并且没有arguments
对象。 - 对于需要根据调用上下文动态绑定的函数,使用普通函数;对于需要保持外部上下文
this
的情况,使用箭头函数。
理解this
的行为以及普通函数与箭头函数之间的区别,能够帮助你在编写JavaScript时避免常见的陷阱和错误。
普通符号(Symbol)&& 共享符号(Global Symbols)&& 知名符号(Well-known Symbols)
在JavaScript中,普通符号(Symbol)是一种独特的数据类型,主要用于创建匿名和唯一的值,以避免属性名称冲突。根据你的描述,“普通符号”、“共享符号”和“知名符号”可以理解为对Symbols的不同分类或用法。
1. 符号(Symbol)概述
- Symbol 是 ES6 新增的一种数据类型,用于创建唯一的标识符。
- 使用
Symbol()
函数可以创建一个新的符号,每个符号都是唯一的。
示例:
const sym1 = Symbol('description');
const sym2 = Symbol('description');
console.log(sym1 === sym2); // false,因为每个Symbol都是唯一的
2. 共享符号(Global Symbols)
- JavaScript 允许使用
Symbol.for()
创建共享符号。在全局注册表中,如果提供相同的键名,将返回相同的符号。 - 这适用于需要跨不同模块或文件共享符号的场景。
示例:
const globalSym1 = Symbol.for('shared');
const globalSym2 = Symbol.for('shared');
console.log(globalSym1 === globalSym2); // true,因为它们是共享的符号
-
Symbol.for(key)
:- 如果指定的键已注册,则返回该符号。
- 如果键没有注册,则创建一个新的符号并将其注册。
3. 知名符号(Well-known Symbols)
- 知名符号是 JS 语言定义的特定符号,通常用于对象的默认操作,可以在内置对象中使用,例如
Symbol.iterator
、Symbol.toStringTag
等。
示例:
// 使用 Symbol.iterator
const myArray = [1, 2, 3];
myArray[Symbol.iterator] = function() {
let index = 0;
const data = this; // 保存对数组的引用
return {
next: function() {
return {
value: data[index++],
done: index > data.length
};
}
};
};
const iterator = myArray[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
常见的知名符号:
Symbol.iterator
: 定义对象的默认迭代器。Symbol.asyncIterator
: 定义对象的默认异步迭代器。Symbol.toStringTag
: 使对象有一个自定义的toString
标签。Symbol.hasInstance
: 定义instanceof
的行为。
符号小结
- 普通符号:使用
Symbol()
创建的唯一值。 - 共享符号:使用
Symbol.for()
创建或获取全局共享的符号。 - 知名符号:预定义的特定符号,供语言内部使用,如
Symbol.iterator
,用于实现特定的语言功能。
ES6总结
哈哈哈,看到这是不是很慌,用了这么久这么多年的ES6你不懂的细节还有知识点是不是地动山摇了。。。别慌个后面我们一点点详细仔细梳理继续分享。。。