8

ES6 中的 Symbol 是什么?

 3 years ago
source link: https://my.oschina.net/wangzhijie/blog/5088833
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

ES6 中的 Symbol 是什么?

记得刚找工作那会,几种数据类型是必问题,当时的答案一般都是七种——字符串(String)、数字(Number)、布尔(Boolean)、数组(Array)、对象(Object)、空(Null)、未定义(Undefined),时至今日,某些网络教程上还是这样的分类:

不完整的分类

其实,随着 ECMAScript 的发展和完善,在 ES6(2015) 和 ES11(2020) 中,又分别增加了 Symbol 和 BigInt 两种类型,所以,完整的分类应该是下面这样的:

完整的数据类型

今天,我们就来看看 Symbol 到底是什么类型,为何要引入这样一个类型。

我们都应该有个清晰的认识:任何新技术或者新概念的出现,必然是为了解决某一痛点的。

想想吧,我们为了起一个漂亮的、符合语义规则的属性名而绞尽脑汁时的痛苦,还要承受属性名可能冲突的折磨,那是一段不堪回首的往事!

而 Symbol 的出现正是为了拯救我们的头发,让它们不至于牺牲在这些琐碎的小事上,它们每一根都是那么珍贵,它们的归宿应该在更具价值的地方!

头发证的会掉完

symbol 是一种基本数据类型。Symbol() 函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的 symbol 注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。

直接使用 Symbol() 创建新的 symbol 类型,并用一个可选的字符串作为其描述。

Symbol([description])
  • description (可选) 字符串类型。对symbol的描述,可用于调试但不是访问symbol本身。请注意,即使传入两个相同的字符串,得到的 symbol 也不相等。
const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');

console.log(typeof symbol1);
// expected output: "symbol"

console.log(symbol2 === 42);
// expected output: false

console.log(symbol3.toString());
// expected output: "Symbol(foo)"

console.log(Symbol('foo') === Symbol('foo'));
// expected output: false

上面的代码创建了三个新的 symbol 类型。 注意,Symbol("foo") 不会强制将字符串 “foo” 转换成 symbol 类型。它每次都会创建一个新的 symbol 类型。

下面带有 new 运算符的语法将抛出 TypeError 运算符的语法将抛出错误:

var sym = new Symbol(); // TypeError

正如歌词“每个人都有他的脾气”所说,Symbol 也有它自己的特性:

  1. 没有两个 Symbol 的值是相等的。就像“世上没有两片相同的叶子”一样,任何两个 Symbol 数据的值都不会相等。
  2. Symbol 数据值可以作为对象属性名。高手一出手,就知有没有。这一下子就奠定了 Symbol 的江湖地位。要知道,在之前,对象的属性名是字符串的专属权利,就连数字也会被同化为字符串,可现在居然被 Symbol 虎口夺食,字符串大概也只能黯然伤神了吧。

根据 Symbol 的特性,它有以下通途。

JavaScript 内置了一个 symbol ,那就是 ES6 中的 Symbol.iterator。拥有 Symbol.iterator 函数的对象被称为 可迭代对象 ,就是说你可以在对象上使用 for/of 循环。

const fibonacci = {
    [Symbol.iterator]: function* () {
        let a = 1;
        let b = 1;
        let temp;

        yield b;

        while (true) {
            temp = a;
            a = a + b;
            b = temp;
            yield b;
        }
    }
};

// Prints every Fibonacci number less than 100
for (const x of fibonacci) {
    if (x >= 100) {
        break;
    }
    console.log(x);
}

为什么这里要用 Symbol.iterator 而不是字符串?假设不用 Symbol.iterator ,可迭代对象需要有一个字符串属性名 'iterator',就像下面这个可迭代对象的类:

class MyClass {
    constructor (obj) {
        Object.assign(this, obj);
    }

    iterator() {
        const keys = Object.keys(this);
        let i = 0;
        return (function* () {
            if (i >= keys.length) {
                return;
            }
            yield keys[i++];
        })();
    }
}

MyClass 的实例是可迭代对象,可以遍历对象上面的属性。但是上面的类有个潜在的缺陷,假设有个恶意用户给 MyClass 构造函数传了一个带有 iterator 属性的对象:

const obj = new MyClass({ iterator: 'not a function' });

这样你在 obj 上使用 for/of 的话,JavaScript 会抛出 TypeError: obj is not iterable 异常。

可以看出,传入对象的 iterator 函数覆盖了类的 iterator 属性。

这有点类似原型污染的安全问题,无脑复制用户数据会对一些特殊属性,比如 proto 和 constructor 带来问题。

这里的核心在于,symbol 让对象的内部数据和用户数据井水不犯河水。

由于 sysmbol 无法在 JSON 里表示,因此不用担心给 Express API 传入带有不合适的 Symbol.iterator 属性的数据。另外,对于那种混合了内置函数和用户数据的对象,你可以用 symbol 来确保用户数据不会跟内置属性冲突。

由于任何两个 symbol 都是不相等的,在 JavaScript 里可以很方便地用来模拟私有属性。symbol` 不会出现在 Object.keys() 的结果中,因此除非你明确地 export 一个 symbol,或者用 Object.getOwnPropertySymbols() 函数获取,否则其他代码无法访问这个属性。

function getObj() {
    const symbol = Symbol('test');
    const obj = {};
    obj[symbol] = 'test';
    return obj;
}

const obj = getObj();

Object.keys(obj); // []

// 除非有这个 symbol 的引用,否则无法访问该属性
obj[Symbol('test')]; // undefined

// 用 getOwnPropertySymbols() 依然可以拿到 symbol 的引用
const [symbol] = Object.getOwnPropertySymbols(obj);
obj[symbol]; // 'test'

还有一个原因是 symbol 不会出现在 JSON.stringify() 的结果里,确切地说是JSON.stringify()会忽略symbol属性名和属性值:

const symbol = Symbol('test');
const obj = { [symbol]: 'test', test: symbol };

JSON.stringify(obj); // "{}"

symbol 具有以下特性:

  • 每个 symbol 都是独一无二的。
  • symbol 可用作对象名称。

~ 本文完,感谢阅读!

学习有趣的知识,结识有趣的朋友,塑造有趣的灵魂!

我是〖编程三昧〗的作者 隐逸王,我的公众号是『编程三昧』,欢迎关注,希望大家多多指教!

你来,怀揣期望,我有墨香相迎! 你归,无论得失,唯以余韵相赠!

知识与技能并重,内力和外功兼修,理论和实践两手都要抓、两手都要硬!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK