JavaScript Symbol 符号终极指南教程
source link: https://www.myfreax.com/symbol/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
JavaScript Symbol 符号终极指南教程
在本教程中,您将了解 JavaScript Symbol 符号原始类型以及如何有效地使用符号
JavaScript Symbol 符号终极指南教程
在本教程中,您将了解 JavaScript Symbol 符号原始类型以及如何有效地使用符号。
ES6 添加 Symbol
作为新的原始类型。与 number、boolean、null、undefined 和 string 等其他基本类型不同,符号类型没有字面量形式。
要创建新的符号,您可以使用全局 Symbol()
函数,如本例所示:
let s = Symbol('foo');
每次调用 Symbol()
函数时都会创建一个新的唯一值:
console.log(Symbol() === Symbol()); // false
Symbol()
函数接受一个 description
作为可选参数。 description
参数将使您的符号更具描述性。
以下示例创建两个符号:firstName
和 lastName
。
let firstName = Symbol('first name');
let lastName = Symbol('last name');
您可以使用 toString()
方法访问符号的描述属性 description
。 console.log()
方法隐式调用符号的 toString()
方法,如下例所示:
console.log(firstName); // Symbol(first name)
console.log(lastName); // Symbol(last name)
由于符号是原始值,您可以使用 typeof
运算符来检查变量是否是符号。当你传入一个符号变量时,ES6 扩展了 typeof
返回 symbol
字符串:
console.log(typeof firstName); // symbol
由于符号是原始值,如果您尝试使用 new
运算符创建符号,则会抛出错误:
let s = new Symbol(); // error
ES6 为您提供一个全局符号注册表,允许您在全球范围内共享符号。如果你想创建一个将被共享的符号,你可以使用 Symbol.for()
方法而不是调用 Symbol()
函数。
Symbol.for()
方法接受可用于描述符号的单个参数,如以下示例所示:
let ssn = Symbol.for('ssn');
Symbol.for()
方法首先使用 ssn
键在全局符号注册表中的搜索符号。如果有一个,它返回现有的符号。否则,Symbol.for()
方法创建一个新的符号,使用指定的键将其注册到全局符号注册表,然后返回该符号。
稍后,如果您使用相同的键调用 Symbol.for()
方法,Symbol.for()
方法将返回现有符号。
let citizenID = Symbol.for('ssn');
console.log(ssn === citizenID); // true
在这个例子中,我们使用 Symbol.for()
方法来查找带有 ssn
键的符号 。由于全局符号注册表已包含它,因此 Symbol.for()
方法返回现有符号。
要获取与符号关联的键,您可以使用 Symbol.keyFor()
方法,如下所示:
console.log(Symbol.keyFor(citizenID)); // 'ssn'
如果全局符号注册表中不存在符号,则 System.keyFor()
方法返回 undefined
。
let systemID = Symbol('sys');
console.log(Symbol.keyFor(systemID)); // undefined
符号作为唯一值
每当您在代码中使用字符串或数字作为标识时,都应该改用符号。例如,您必须在任务管理应用程序中管理状态。
在 ES6 之前,您会使用诸如 open
, in progress
, completed
, canceled
, 和 on hold
之类的字符串来表示任务的不同状态。在 ES6 ,你可以像下面这样使用符号:
let statuses = {
OPEN: Symbol('Open'),
IN_PROGRESS: Symbol('In progress'),
COMPLETED: Symbol('Completed'),
HOLD: Symbol('On hold'),
CANCELED: Symbol('Canceled')
};
// complete a task
task.setStatus(statuses.COMPLETED);
符号作为对象的计算属性名称
您可以使用符号作为计算属性名称。请阅读以下示例:
let status = Symbol('status');
let task = {
[status]: statuses.OPEN,
description: 'Learn ES6 Symbol'
};
console.log(task);
要获取对象的所有可枚举属性,可以使用 Object.keys()
方法。
console.log(Object.keys(task)); // ["description"]
要获取对象的所有属性,无论这些属性是否可枚举,都可以使用 Object.getOwnPropertyNames()
方法。
console.log(Object.getOwnPropertyNames(task)); // ["description"]
要获取对象的所有符号属性,可以使用 ES6 添加的 Object.getOwnPropertySymbols()
方法。
console.log(Object.getOwnPropertySymbols(task)); //[Symbol(status)]
Object.getOwnPropertySymbols()
方法返回对象自身属性符号的数组。
内置通用(well-known)symbol
ES6 提供预定义的符号,称为well-known 符号。well-known 符号代表 JavaScript 中的常见行为。每个 well-known 符号都是 Symbol
对象的静态属性。
symbol.hasInstance
Symbol.hasInstance
是改变 instanceof
运算符行为的符号。通常,当您在使用instanceof
运算符时:
obj instanceof type;
JavaScript 将按如下方式调用 Symbol.hasIntance
方法:
type[Symbol.hasInstance](obj);
然后它依赖此方法来确定 obj
是否是 type
对象的实例。请参见以下示例。
class Stack {
}
console.log([] instanceof Stack); // false
[]
数组不是 Stack
类的实例,因此,instanceof
运算符在此示例中返回 false
。
假设你想让 []
数组是 Stack
类的一个实例,你可以添加 Symbol.hasInstance
方法如下:
class Stack {
static [Symbol.hasInstance](obj) {
return Array.isArray(obj);
}
}
console.log([] instanceof Stack); // true
Symbol.iterator
Symbol.iterator
函数是否将返回指定对象的迭代器。具有 Symbol.iterator
属性的对象称为可迭代对象。
在 ES6 ,所有集合对象 Array、Set和Map 和字符串都是可迭代对象。ES6 提供用于可迭代对象的 for...of 循环,如下例所示。
var numbers = [1, 2, 3];
for (let num of numbers) {
console.log(num);
}
// 1
// 2
// 3
在内部,JavaScript 引擎首先调用 numbers
数组的 Symbol.iterator
方法来获取迭代器对象。
然后,它调用 iterator.next()
方法并将迭代器对象的值属性复制到 num
变量中。迭代三次后,done
结果对象的属性为 true
,退出循环。
您可以通过 System.iterator
访问默认迭代器对象如下所示:
var iterator = numbers[Symbol.iterator]();
console.log(iterator.next()); // Object {value: 1, done: false}
console.log(iterator.next()); // Object {value: 2, done: false}
console.log(iterator.next()); // Object {value: 3, done: false}
console.log(iterator.next()); // Object {value: undefined, done: true}
默认情况下,集合是不可迭代的。但是,您可以使 用Symbol.iterator
以下示例中所示的方法使其可迭代:
class List {
constructor() {
this.elements = [];
}
add(element) {
this.elements.push(element);
return this;
}
*[Symbol.iterator]() {
for (let element of this.elements) {
yield element;
}
}
}
let chars = new List();
chars.add('A')
.add('B')
.add('C');
// because of the Symbol.iterator
for (let c of chars) {
console.log(c);
}
// A
// B
// C
Symbol.isConcatSpreadable
要连接两个数组,请使用以下示例中所示的 concat()
方法:
let odd = [1, 3],
even = [2, 4];
let all = odd.concat(even);
console.log(all); // [1, 3, 2, 4]
在此示例中,生成的数组包含两个数组的单个元素。此外,concat()
方法还接受非数组参数,如下所示。
let extras = all.concat(5);
console.log(extras); // [1, 3, 2, 4, 5]
数字 5 成为数组的第五个元素。
正如您在上面的示例中看到的,当我们将数组传递给方法时 concat()
, concat()
方法将数组展开为单个元素。
但是,它以不同的方式对待单个原始参数。在 ES6 之前,您无法更改此行为。这就是 Symbol.isConcatSpreadable
符号发挥作用的原因。
Symbol.isConcatSpreadable
属性是一个布尔值,用于确定是否将对象单独添加到 concat()
函数的结果中。
考虑以下示例:
let list = {
0: 'JavaScript',
1: 'Symbol',
length: 2
};
let message = ['Learning'].concat(list);
console.log(message); // ["Learning", Object]
列表对象连接到数组['Learning']
。但是,它的各个元素并没有传播。
要使 list
对象的元素在传递给 concat()
方法时单独添加到数组中,需要将Symbol.isConcatSpreadable
属性添加到 list
对象中,如下所示:
let list = {
0: 'JavaScript',
1: 'Symbol',
length: 2,
[Symbol.isConcatSpreadable]: true
};
let message = ['Learning'].concat(list);
console.log(message); // ["Learning", "JavaScript", "Symbol"]
请注意,如果您设置 Symbol.isConcatSpreadable
的值为 false
并将 list
对象传递给 concat()
方法,它将作为整个对象连接到数组。
Symbol.toPrimitive
Symbol.toPrimitive
方法确定将对象转换为原始值时应该做什么。JavaScript 引擎在每个标准类型的原型上定义 Symbol.toPrimitive
方法。
Symbol.toPrimitive
方法接受一个 hint
参数, hint
参数的值可以是:number 、 string 和 default 。
hint
参数指定返回值的类型。hint
参数由 JavaScript 引擎根据使用对象的上下文填充。
下面是使用 Symbol.toPrimitive
方法的示例。
function Money(amount, currency) {
this.amount = amount;
this.currency = currency;
}
Money.prototype[Symbol.toPrimitive] = function(hint) {
var result;
switch (hint) {
case 'string':
result = this.amount + this.currency;
break;
case 'number':
result = this.amount;
break;
case 'default':
result = this.amount + this.currency;
break;
}
return result;
}
var price = new Money(799, 'USD');
console.log('Price is ' + price); // Price is 799USD
console.log(+price + 1); // 800
console.log(String(price)); // 799USD
在本教程中,您了解了 JavaScript 符号以及如何将符号用于唯一值和对象属性。此外,您还学习了如何使用well-known的符号来修改对象行为。
微信公众号
支付宝打赏
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK