Blog icon indicating copy to clipboard operation
Blog copied to clipboard

Symbol数据类型简介

Open JingshunYang opened this issue 6 years ago • 0 comments

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

  • Symbol.iterator是内置的迭代symbol,用来访问对象的@@iterator方法,该方法是对象默认迭代器的方法,可用于for...of循环,也可以说@@iterator为我们提供了重载of的方法。可迭代对象(String, Array, TypedArray, Map, Set, arguments对象等)拥有默认的@@iterator方法。对于不可迭代的Object,通过实现Object@@iterator方法,就可以把不可迭代的对象转变为可迭代的对象。
const iterable1 = new Object();

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方法来实现自定义的匹配方法。
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']
  • Symbol.matchAll是内置的正则symbol,用于访问对象的@@matchAll方法,该方法返回一个包括了所有匹配值的迭代器,在使用String.prototype.matchAll()时被调用。
  • Symbol.replace是内置的正则symbol,用于访问对象的@@replace方法,该方法用来替换和某个字符串的子串匹配的部分,在使用String.prototype.replace()时被调用。
class MyReplacer {
    constructor(value) {
        this.value = value;
    }
    [Symbol.replace](string, replacer) {
        var index = string.indexOf(this.value);
        if (index === -1) {
            return string;
        }
        if (typeof replacer === 'function') {
            replacer = replacer.call(undefined, this.value, string);
        }
        return `${string.slice(0, index)}${replacer}${string.slice(index + this.value.length)}`;
    }
}
var fooReplaced = 'foobar'.replace(new MyReplacer('foo'), 'baz'); // 'bazbar'
var barMatcher = 'foobar'.replace(new MyReplacer('bar'), function () { return 'baz' }); // 'foobaz'
  • Symbol.search用于访问对象的@@search方法,在使用String.prototype.search()时被调用。
  • Symbol.split用于访问对象的@@split方法,在使用String.prototype.split()时被调用。 正则symbol允许我们自定义类似于正则表达式的类。

Other Symbols

  • Symbol.hasInstance用于判断某个对象是否为某个构造器的实例,通过instanceof被调用。
class Array1 {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
}

console.log([] instanceof Array1);
// expected output: true
  • Symbol.isConcatSpreadable用来访问对象的@@isConcatSpreadable属性,该属性为布尔值,用来配置一个数组对象在作为Array.prototype.concat()方法的参数时是否应该展开它的元素。
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] ]
  • Symbol.unscopables用来访问对象的@@unscopables属性,该属性为对象值,通过设置某个对象的这个属性,就可以把对象的自有属性及继承属性排除在with作用域之外。
const object1 = {
  property1: 42
};

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方法可以达到目的。
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
  • Symbol.toPrimitive用来访问对象的@@toPrimitive方法,当对象需要转化为基本数据类型的时候就会调用该方法。举个例子,在做+obj运算时js会调用obj[Symbol.toPrimitive]('number');在做'obj'运算时js会调用obj[Symbol.toPrimitive]('string')
var obj = {
  [Symbol.toPrimitive](hint) {
    if (hint == 'number') {
      return 10;
    }
    if (hint == 'string') {
      return 'hello';
    }
    return true;
  }
};
console.log(+obj);     // 10        -- hint is "number"
console.log(`${obj}`); // "hello"   -- hint is "string"
console.log(obj + ''); // "true"    -- hint is "default"
  • Symbol.toStringTag用来访问对象的@@toStringTag属性,该属性返回一个字符串,用于设置对象默认的字符串描述。在使用Object.prototype.toString()方法时,Object.prototype.toString()方法首先会检查作为参数传入的对象是否存在@@toStringTag属性,如果有则用于最后返回的字符串中。
class Collection {
  get [Symbol.toStringTag]() {
    return 'Collection';
  }
}
var x = new Collection();
Object.prototype.toString.call(x) === '[object Collection]' // true

global symbol registry

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

  • Symbol.for(key)函数,在全局symbol注册表中根据key值查询对应的symbol并返回;查询不到则创建一个可在全局symbol注册表中访问到的symbol。
  • Symbol.keyFor(sym)函数,返回symbol在全局symbol注册表中对应的key值;没有对应的key值则返回undefined

浏览器兼容性

 symbol的浏览器兼容性

总结

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

JingshunYang avatar May 08 '19 02:05 JingshunYang