【ES6 教程】第六章 symbol:JavaScript Symbol 终极指南

英文 | https://www.javascripttutorial.net

翻译 | 杨小爱

在本教程中,我们将学习了解JavaScript符号原始类型以及如何有效地使用Symbol。

创建Symbol

ES6 添加了 Symbol 作为新的原始类型。与数字、布尔值、空值、未定义和字符串等其他基本类型不同,symbol类型没有文字形式。

要创建Symbol,我们可以使用全局 Symbol() 函数,如本例所示:

let s = Symbol(‘foo’);

每次调用 Symbol() 函数都会创建一个新的唯一值:

console.log(Symbol() === Symbol()); // false

Symbol() 函数接受描述作为可选参数。description 参数将使您的符号更具描述性。

以下示例创建两个符号:firstName 和 lastName。

let firstName = Symbol(‘first name’), lastName = Symbol(‘last name’);

我们可以使用 toString() 方法访问符号的描述属性。console.log() 方法隐式调用符号的 toString() 方法,如下例所示:

console.log(firstName); // Symbol(first name)console.log(lastName); // Symbol(last name)

由于符号是原始值,我们可以使用typeof运算符来检查变量是否是symbol。当我们传入一个symbol变量时,ES6 扩展了 typeof 以返回符号字符串:

console.log(typeof firstName); // symbol

由于symbol是原始值,如果我们尝试使用 new 运算符创建符号,则会出现错误:

let s = new Symbol(); // error

共享symbol

ES6为我们提供了一个全局符号注册表,允许我们在全球范围内共享symbol。如果我们想创建一个将被共享的symbol,我们可以使用 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() 方法返回未定义。

let systemID = Symbol(‘sys’);console.log(Symbol.keyFor(systemID)); // undefined

symbol用法

A)使用symbols作为唯一值

每当我们在代码中使用字符串或数字时,都应该改用symbol。例如,我们必须在任务管理应用程序中管理状态。

在ES6之前,我们可以使用诸如打开、进行中、完成、取消和暂停等字符串来表示任务的不同状态。在ES6中,你可以像下面这样使用symbol:

let statuses = { OPEN: Symbol(‘Open’), IN_PROGRESS: Symbol(‘In progress’), COMPLETED: Symbol(‘Completed’), HOLD: Symbol(‘On hold’), CANCELED: Symbol(‘Canceled’)};// complete a tasktask.setStatus(statuses.COMPLETED);

B)使用symbol作为对象的计算属性名称

我们可以使用符号作为计算属性名称。请参阅以下示例:

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() 方法返回对象自身属性符号的数组。

众所周知的symbol

ES6 提供了预定义的符号,称为众所周知的符号。众所周知的符号代表 JavaScript 中的常见行为。每个众所周知的symbols都是 Symbol 对象的静态属性。

symbol.hasInstance

Symbol.hasInstance 是一个改变 instanceof 运算符行为的符号。通常,当我们使用 instanceof 运算符时:

obj instanceof type;

JavaScript 将调用 Symbol.hasIntance 方法,如下所示:

type[Symbol.hasInstance](obj);

然后,它取决于方法来确定 obj 是否是类型对象的实例。请参见以下示例。

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.迭代器

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 引擎首先调用数字数组的 Symbol.iterator 方法来获取迭代器对象。然后,它调用 iterator.next() 方法并将迭代器对象的 value 属性复制到 num 变量中。迭代3次后,result对象的done属性为真,退出循环。

我们可以通过 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.iteratorfor (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’] 数组。但是,它的各个元素并没有传播。

要使列表对象的元素在传递给concat()方法时单独添加到数组中,我们需要将 Symbol.isConcatSpreadable 属性添加到列表对象中,如下所示:

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 并将列表对象传递给 concat() 方法,它将作为整个对象连接到数组。

Symbol.toPrimitive

Symbol.toPrimitive 方法确定将对象转换为原始值时应该发生什么。

JavaScript 引擎在每个标准类型的原型上定义了 Symbol.toPrimitive 方法。

Symbol.toPrimitive 方法采用具有三个值之一的提示参数:“number”、“string”和“default”。提示参数指定返回值的类型。提示参数由 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 799USDconsole.log(+price + 1); // 800console.log(String(price)); // 799USD

总结

在本教程中,我们了解了JavaScript symbol 以及如何将symbol用于唯一值和对象属性。此外,我们还学习了如何使用众所周知的symbol来修改对象行为。

如果你觉得今天的教程对你有用的话,请记得点赞我,关注我,并将这篇教程分享给你的朋友,也许能够帮助到他。

学习更多技能

请点击下方公众号

d315e56b4366d5a2442e7e2c8add973e.png


比丘资源网 » 【ES6 教程】第六章 symbol:JavaScript Symbol 终极指南

发表回复

提供最优质的资源集合

立即查看 了解详情