mengtuifrontend / Blog

芦叶满汀洲,寒沙带浅流。二十年重过南楼。柳下系船犹未稳,能几日,又中秋。 黄鹤断矶头,故人今在否?旧江山浑是新愁。欲买桂花同载酒,终不似,少年游。
18 stars 5 forks source link

Symbol数据类型简介 #21

Open JingshunYang opened 5 years ago

JingshunYang commented 5 years ago

Symbol数据类型

symbol是js中7种基础数据类型之一(基础数据类型包括:number, string, boolean, null, undefined, bigint, symbol),symbol是在ECMAScript 2015 / ES6中新加入的。在JavaScript运行环境中,调用函数Symbol()可以动态地返回一个匿名且唯一的symbol类型的值。接下来,这篇文章会简单介绍一下Symbol构造函数,well-known symbols,全局symbol注册表这三部分内容。

Symbol构造函数

Symbol()函数:构造一个symbol类型值并返回,该值唯一。 返回值:symbol类型值 它类似于内置的Object类,但不完全是一个构造器,因为它不支持new Symbol()这样的语法。 每一个从Symbol()函数返回的symbol值都是独一无二的,因为其唯一性,适合用作对象属性的标识符,使用Object.getOwnPropertySymbols() 以symbol数组的形式返回某个对象的所有symbol属性。

// Symbol()函数使用
const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');

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

console.log(symbol3.toString());
// expected output: "Symbol(foo)"
// 唯一性
console.log(Symbol('foo') === Symbol('foo'));
// expected output: false

const object1 = {};
// symbol用作对象属性的标识
object1[symbol1] = 'this is symbol1.';
object1[symbol2] = 'this is symbol2.';
object1[symbol3] = 'this is symbol3.';
// 获取某对象中所有的symbol属性
const objectSymbols = Object.getOwnPropertySymbols(object1);
console.log(objectSymbols.length);
// expected output: 3

well-known symbols

即内置symbol,用来表示内部语言行为,分为3种:迭代symbol,正则symbol,其他symbol。

Iteration Symbols

iterable1[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; };

console.log([...iterable1]); // [1, 2, 3]

- `Symbol.asyncIterator`是内置的迭代symbol,用来访问对象的`@@asyncIterator`方法,该方法实现的是对象默认异步迭代器的方法,可用于`for await...of`循环。
#### Regular Expression Symbols
- `Symbol.match`是内置的正则symbol,用于访问对象的`@@match`方法,该方法用来判断给定的值是否和某个字符串匹配,在使用`String.prototype.match()`时被调用。也就是说我们可以用`@@match`方法来实现自定义的匹配方法。
```js
class MyMatcher {
    constructor(value) {
        this.value = value;
    }
    [Symbol.match](string) {
        var index = string.indexOf(this.value);
        if (index === -1) {
            return null;
        }
        return [this.value];
    }
}
var fooMatcher = 'foobar'.match(new MyMatcher('foo')); // ['foo']
var barMatcher = 'foobar'.match(new MyMatcher('bar')); // ['bar']

console.log([] instanceof Array1); // expected output: true

- `Symbol.isConcatSpreadable`用来访问对象的`@@isConcatSpreadable`属性,该属性为布尔值,用来配置一个数组对象在作为`Array.prototype.concat()`方法的参数时是否应该展开它的元素。
```js
var alpha = ['a', 'b', 'c'], 
    numeric = [1, 2, 3]; 

var alphaNumeric = alpha.concat(numeric); 
console.log(alphaNumeric); // 结果: ['a', 'b', 'c', 1, 2, 3]

numeric[Symbol.isConcatSpreadable] = false;
alphaNumeric = alpha.concat(numeric);
console.log(alphaNumeric); // 结果: ['a', 'b', 'c', [1, 2, 3] ]

object1[Symbol.unscopables] = { property1: true };

with (object1) { console.log(property1); // expected output: Error: property1 is not defined }

- `Symbol.species`用来访问对象的`@@species`方法,该属性值为函数,该函数会在创建派生对象 (derived object) 时作为其构造函数。派生对象是相对于原始对象而言的,原始对象在某些具体的操作(如`map`)之后得到的对象被称为派生对象。一般情况下,派生对象和原始对象有着相同的构造器。而如果我们想为派生对象自定义一个构造器时,`@@species`方法就派上用场了。举个例子,我希望给`Array`类添加一些其他的方法,那么我可以基于父类`Array`继承得到子类`MyArray`。之后,`MyArray`的某个实例使用了`map()`方法,如果我希望`map()`方法返回的新对象是`Array`类的实例而不是`MyArray`类的实例,通过实现`MyArray`类的`@@species`方法可以达到目的。
```js
class MyArray extends Array {  
  isEmpty() {
    return this.length === 0;
  }
  static get [Symbol.species]() {
    return Array;
  }
}
let array = new MyArray(3, 5, 4);  
array.isEmpty(); // => false  
let odds = array.filter(item => item % 2 === 1);  
odds instanceof Array;   // => true  
odds instanceof MyArray; // => false

global symbol registry

即全局symbol注册表,表中的每一项称之为Record,每个record包含两个字段:key(字符串类型,用作symbol的标识), symbol(symbol类型,存储的symbol值)。在全局symbol注册表中的symbol可在全局访问到。

浏览器兼容性

 symbol的浏览器兼容性

总结

symbol是ES6中新引入的基本数据类型,可通过Symbol构造函数创建得到唯一的symbol值,well-known symbols可以让开发人员自定义类及对象的行为,如迭代、字符串匹配查找等。