大数据

基础类型之 Symbol(四)

导读:Symbol 类型我们快要讲完了,其实我们可以看出来,这个新加入的基本类型作用就是创建唯一的不予其它类型冲突的值。最后一节我们主要讲述 Symbol 的 prototype

Symbol 原型对象的属性

The Symbol prototype object is the intrinsic object %SymbolPrototype%. The Symbol prototype object is an ordinary object. It is not a Symbol instance and does not have a [[SymbolData]] internal slot.
The value of the [[Prototype]] internal slot of the Symbol prototype object is the intrinsic object %ObjectPrototype%.

翻译:

Symbol 原型对象是内在对象 %SymbolPrototype%。 Symbol 原型对象是一个普通对象。 它不是一个 Symbol 的实例,并且没有 [[SymbolData]] 内部插槽。
Symbol 原型对象的 [[Prototype]] 内部插槽的值是内在对象 %ObjectPrototype%

看过前面小节的同学会不会感到很亲切呀?一下就知道了我们的 %ObjectPrototype%[[Prototype]] 是什么了。

接下来我们解释一下这段话。为什么 Symbol 不是一个实例呢?首先,我们要知道什么是实例?相信大家都知道啦:let obj = new Object(),我们就可以称 obj 是 Object 的一个实例。那么,为什么说 Symbol 不是实例呢?因为 Symbol 是基本类型,不可以使用 new 关键字创建。

但是不对!哪里就不对呀!你肯定会说,那么我们就来研究下:

let num = new Number(123);
console.log(num);

同学们认为会输出什么?

正解如下:

num 的输出值

不知道同学们有没有看到奇怪的东西?我们说七大基本类型,不是引用类型,怎么可以用 new 创建呢?我们可以看到 num 内部的构造函数是 function Number() ,并且指向了 Number。这也就说明了 num 可以作为 Number 的一个实例显示。

那么接下来我们看看 new Symbol 的结果:

let name = new Symbol('name');
console.log(name);

毫无疑问,报错了。。。

name 的输出值

该错误很直观地就告诉我们:Symbol 不是一个构造函数,因此不可以使用 new 关键字创建。大家一定要记住哦。

第二点就是 Symbol 的 [[prototype]] 指向的是 %ObjectPrototype%

在上一节中,我们进行了叙述。但是这里有个问题,为什么 Symbol 原型指向的是 Obeject 呢?我不知道大家注意到了没有,在前面的地方,我们使用了 new Number 创建了一个 num,那里写的是 function Number() (忘记了的同学可以回去看一眼),说明了其实我们使用 new Number 的时候其实是调用了 Function ,也就是说我们创建的 num 其实也是 Function 的一个实例!先剧透下:没错!Number 类型内部的 [[prototype]] 指向的就是 %FunctionPrototype%。(关于这一点后续会详细说明)

好啦,解决了上述的问题后,我们接下来看下 Symbol 的原型方法吧。

Symbol 的原型

(1)Symbol.prototype.constructor

The initial value of Symbol.prototype.constructor is the intrinsic object %Symbol%.

现在同学们恐怕一下就清楚啦,代码表示下:

console.log( Symbol === Symbol.prototype.constructor );  // true

(2)Symbol.prototype.toString

toString() 方法返回当前 symbol 对象的字符串表示。

其内部的抽象判断过程如下:

如果传入的值为 Symbol 类型,则返回 “Symbol(“value“)”(” “代表以字符串形式表示);如果传入的值为 undefined,那么则返回空字符串,形如 “Symbol(“”)” ;如果值不为 Object 类型,那么则抛出 TypeError 错误;如果没有内部的 [[SymbolData]] 插槽,也抛出 TypeError 错误;否则将它用 [[SymbolData]] 来表示。

不是很理解后面的没关系,我们用代码来解释:

// Symbol 类型
let a = Symbol('name');
console.log(a.toString());  // Symbol(name)
// Symbol 为空
let b = Symbol();
console.log(b.toString());  // Symbol()
// Object 类型
let c = Symbol({    
    good: 'good', 
    bad: 'bad'
});
console.log(c.toString());  // Symbol([object Object])
// Function 类型
let d = Symbol(function (arg) { 
    return arg;
});
console.log(d.toString()); // Symbol(function (arg) {return arg;})

这下我们就可以理解啦~

(3)Symbol.prototype.valueOf

Symbol.prototype.toString 方法的判断差不多;如果传入的值为 Symbol 类型,则返回它的值;否则,如果是 Object 类型,则返回使用该值的 [[SymbolData]] 表示形式。

代码表示:

// Symbol 类型
let a = Symbol('name');
console.log(a.toString());  // Symbol(name)
// Symbol 为空
let b = Symbol();
console.log(b.toString());  // Symbol()
// Object 类型
let c = Symbol({    
    good: 'good', 
    bad: 'bad'
});
console.log(c.toString());  // Symbol([object Object])
// Function 类型
let d = Symbol(function (arg) { 
    return arg;
});
console.log(d.toString()); // Symbol(function (arg) {return arg;})

我们可以对比下,其实并没有看出有任何区别。

它们之间最大的区别在于:toString 方法返回的是该值的字符串表示;而 valueOf 返回的是该值最合适(preferred)的原始值表示形式。取决于我们用什么方式调用以及它所处的环境是什么样的。

let arr = [1,2,3];
console.log( Array.isArray(arr.valueOf()) );  // true
console.log( Array.isArray(arr.toString()) );   // false

以上的例子我们可以看出来,我们调用的方式不同,它的结果也会有做不同。
参考地址:http://blog.csdn.net/cct418/article/details/50889987

(4) Symbol.prototype [ @@toPrimitive ] ( hint )

概述:此函数由 ECMAScript 语言运算符调用,以将 Symbol 对象转换为原始值。 允许的 hint 值为“default”,“number” 和 “string”。

今天一天我翻译了多少文章啊~~详情可见 MDN: Symbol.prototype[@@toPrimitive]

简单来说:我们不需要自行调用(called)该方法,ECMAScript 会在需要的时候自动地调用(invoked)该方法。

这里做一个小小的扩展知识:同样都是“调用”,为什么有 call 和 invoke 的区别呢?我的理解是 “call” 表示我们人为地、显式地调用;而 “invoke” 表示内部地、隐式地调用。(当大家看官方文档的时候,就会更加清楚啦~)

(5)Symbol.prototype [ @@toStringTag ]

概述:@@toStringTag 属性的初始值为 String 值 “Symbol”。

PS:哎~感慨一下,我们前端的积极性不高啊。。为了写作的正确性查阅了许多资料,结果还都要我自己翻译一遍,宝宝心里苦呀,不过收获也很大。

详情请参见 MDN:Symbol.toStringTag

Symbol 的实例属性

Symbol instances are ordinary objects that inherit properties from the Symbol prototype object. Symbol instances have a [[SymbolData]] internal slot. The [[SymbolData]] internal slot is the Symbol value represented by this Symbol object.

翻译如下:

Symbol 实例是从 Symbol 原型对象继承属性的普通对象。Symbol 实例具有 [[SymbolData]] 内部插槽。 [[SymbolData]] 内部插槽是由此 Symbol 对象表示的 Symbol 值。

如果你是个有心人,你可能回疑惑,本文一开头就提到了 “Symbol 原型对象是一个普通对象。 它不是一个 Symbol 的实例,并且没有 [[SymbolData]] 内部插槽。” 。那么这里是什么意思呢?

解释一下:我们所写的 Symbol 值是 Symbol 类型的实例,不可使用 new 关键字声明 Symbol 类型。但这里的意思是指 Symbol 的实例继承了 Symbol.prototype 的属性,Symbol 的实例拥有 [[SymbolData]] 内部插槽;而 Symbol.prototype 却无 [[SymbolData]] 内部插槽。

现在清楚了吗同学?如果不清楚,还请麻烦翻到最上方再读一遍哦~

总结

我们用了四个小节来叙述 Symbol 类型,接下来我们还有大家熟悉而又不深刻的六大基本类型。至于为什么说是不深刻呢?当我写到那里的时候,你就会知道啦,自己哪里还欠缺。

接下来我会花时间整理下,明天开始我们最常用的 String 类型咯~~~

如果对同学你有任何帮助的话,还请喜欢我哦。你的小小支持是我前进的动力呢(づ ̄3 ̄)づ╭❤~


传送门

  1. 基础类型之 Symbol(一)
  2. 基础类型之 Symbol(二)
  3. 基础类型之 Symbol(三)