8

JavaScript Symbol 符号终极指南教程

 1 year ago
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.
neoserver,ios ssh client

JavaScript Symbol 符号终极指南教程

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

JavaScript Symbol 符号终极指南教程

JavaScript Symbol 符号终极指南教程

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

ES6 添加 Symbol 作为新的原始类型。与 numberbooleannullundefinedstring 等其他基本类型不同,符号类型没有字面量形式。

要创建新的符号,您可以使用全局 Symbol() 函数,如本例所示:

let s = Symbol('foo');

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

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

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

以下示例创建两个符号:firstNamelastName

let firstName = Symbol('first name');
let lastName = Symbol('last name');

您可以使用 toString() 方法访问符号的描述属性 descriptionconsole.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 ,所有集合对象 ArraySetMap 和字符串都是可迭代对象。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的符号来修改对象行为。

微信公众号

支付宝打赏

myfreax 淘宝打赏

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK