基础类型之 Symbol(三)

导读:上一节呢,我们剖析了 Symbol 的两个方法与其中的差异。。所以,接下来,我们继续学习 Symbol 类型自身的方法。

(1)Symbol.hasInstance

概述:该方法确定构造函数对象是否将对象识别为构造函数实例之一。 由 instanceof 操作符进行调用。

该属性(property)的属性(attribute)如下:

{ [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }

那么该方法有什么用呢?我们看到了,是由 instanceof 来调用它。那么它存在的意义是什么?

如下所示:

class MyClass { 
    static [Symbol.hasInstance](lho) { 
        return Array.isArray(lho); 
    }
}
console.log( [] instanceof MyClass );  // true

我相信,如果没有接触过 ES6 Class 类的话,肯定是不理解的。没关系,我说人话,嘿嘿。

lho instanceof rho;  // lho = left hand operator; rho = right hand operator
在ES6 所支持的引擎中,等价于==>
lho [Symbol.hasInstance] rho;

目前知道这一点就足够啦。未来我们再扩展。

(2)Symbol.isConcatSpreadable

概述:该值为 Boolean 属性,如果为 true,则表示对象可以通过 Array.prototype.concat 展开(flatten)到其数组元素上。

看起来比较奇怪的一句话,什么叫做展开到数组上???举个例子:

// 展开
[1,2,3,4,5];
// 不展开
[1,2,[3,4],5]

就这么一个例子,相信大家都理解了吧。原来这么简单!

Array.prototype.concat 方法默认值为 true,如果我们设置了该值为 false,那么在使用 Array.prototype.concat 方法时将不再做展开处理。

详细的例子可以见这里:ECMAScript 入门 - Symbol。

(3)Symbol.iterator

概述:返回对象的默认迭代器的方法。 由 for-of 语句进行调用。

这个迭代器(iterator),相信听说过 ES6 的同学应该知道有这么个东西,这个放在以后的章节中叙述。

通常来说,我们调用 for-of 循环的时候,内部就会调用 @@iterator 方法,并进行值的输出(这一节内容放在后续阐述)。

(4)Symbol.match

概述:将正则表达式与字符串匹配的正则表达式方法。 由 String.prototype.match 方法调用。

(5)Symbol.prototype

概述:The initial value of Symbol.prototype is the intrinsic object %SymbolPrototype%.
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

这里是我想要重点讲的一个地方。为什么我这里把原文放了上来,我就是想说明一个问题。

惯例,简单翻译下:

Symbol.prototype 的初始值是内在对象 %SymbolPrototype%。

那么问题就来了?这个 %SymbolPrototype% 是什么?

简单的一句话就是:前后加了 % 代表着这是 ECMAScript 内部抽象化的语言表现形式。例如:我们看到的 Object.prototype 在语言内部抽象后所表现的就是 %ObjectPrototype%。其实所代表的就是 Object 的 原型方法。当然,我们也不需要知道它的写法是什么,因为我们不能这么写。

为什么我要把它写在这里呢?原因其实很简单,本系列开篇就提到过 —— JavaScript 中一切都是对象。我们所看到的 Symbol.prototype 下的方法其实内部指向的是 Object.prototype 。这也就是我们大家熟知的原型链。

如果想要深入了解到这门语言的实质,那么接下来的话就很重要了哦。相信听说过 ES6 的同学,都应该听到过迭代器(Iterator),这是个什么东西?放心我会在接下来把这门语言所有的内在都会剖析清楚的。暂时大家只需要知道,%IteratorPrototype% 也是指向 %ObjectPrototype% ,而 %IteratorPrototype% 内部却又托管了 %StringPrototype%。是不是有些绕?我画了个图给大家看:

基础类型之 Symbol(三)_第1张图片
内部原型链

我们可以看到,Object 在语言内部掌握了一切。它的原型指向的为 null 哦,谨记谨记!

官方文档原文中写着这么一句话:

The value of the [[Prototype]] internal slot of the Symbol prototype object is the intrinsic object %ObjectPrototype%.

也就是说,ECMAScript 语言是基于对象与原型的语言!我们所写的 function() {} 其实就是 Function 的一个实例,然后会保存在内存的某一地址中;而我们在 ES6 之前所写的构造函数 function Name() {} 的原型继承函数 Name.prototype.add = function() {} 的关系如下:

基础类型之 Symbol(三)_第2张图片
函数原型链

也就是说,ObjectFunction 的构造函数(constructor);Function 又是 function Name() 的构造函数。

现在同学们清楚了吗?这算是复习一遍 ECMAScript 最重要的概念 —— 原型与原型链 的知识。不清楚的同学可以私信我或者留言,我会一一进行解答。

可能你们看到这里有疑问,为什么要说这这个知识点呢?和 Symbol.prototype 有什么关系?想必你看到这个词的时候脑海中已经知道了吧,为什么我开篇就说到 Symbol 是个特殊的基本类型呢?原因就在这里:Symbol.prototype 作为一个基本类型却与 Function.prototype 这个引用类型一样,由 Obejct 的原型链直接继承;而不像 String.prototype 那般,由 Iterator.prototype 作为中间层继承。那么我们就知道了,这背后一定是有原因的。(具体细节我们放到 Iterator 那节再做详细的解答)

不仅如此,ECMAScript 委员会“妥协”地加入了 Class(类)的概念,目的是为了使我们所写的类的构造函数更加清晰明确。为什么呢?因为 ECMAScript 这门语言本身是没有类的概念的。而我们平时所用的首字母大写写法不过是一种约定俗成的写法,对于背后的解析与存储并无实质性的改变。

如果你已经理解了我在这里解释的原型和继承的概念的话,那么在后面的系列中讲到 Class 的概念你就会发现,这不是什么新奇的东西。仅仅是基于类(class-based)写法的语法糖(指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用)。所以,不要害怕学习新知识,我们需要做的是理解背后的逻辑,这会帮助我们学的更快更清晰。

(6)Symbol.replace

概述:该方法是替换字符串的匹配子字符串的正则表达式方法。 由 String.prototype.replace 方法调用。

(7)Symbol.search

概述:该方法是正则表达式方法,返回与正则表达式匹配的字符串中的索引。 由 String.prototype.search 方法调用。

(8)Symbol.species

概述:用于创建扩展对象的构造函数的函数值的属性。

该方法我在 MDN 上做了翻译工作(我也希望大家能在上面积极地为大家进行翻译哦):Symbol.species。这里就不做介绍了。

(9)Symbol.split

概述:该方法是正则表达式方法,用于在匹配正则表达式的索引处拆分字符串。 由 String.prototype.split 方法调用。

MDN 也有该方法的应用示例:Symbol.split

(10)Symbol.toPrimitive

概述:将对象转换为相应原始值的方法。 由 ToPrimitive 抽象操作调用。

MDN 上我也进行了翻译工作:Symbol.toPrimitive

(12)Symbol.toStringTag

概述:该方法是 String 值属性,用于创建对象的默认字符串描述。 通过内置方法 Object.prototype.toString 访问。

那么,就此机会,我介绍下 Object.prototype.toString ( ) 方法:

如果传进来的值为 undefined,则返回 "[object Undefined]";如果值为 null,在返回 "[object Null]";否则,根据各自的内在特征,返回 "Array""String""Arguments""Function""Error""Boolean""Number""Date""RegExp" 中的一种;如果值为 "String" 或 "Symbol",则调用内部的 @@toStringTag 方法,返回 "[object String]""[object Symbol]";否则将返回 "[object Object]"

代码形式:

let a = 'name';
console.log(Object.prototype.toString(a)); // [object Object]

let b = Symbol('name');
console.log(Object.prototype.toString(b)); // [object Object]

纳尼!?为什么是 [object Object] 呢?放心,不管你传入什么类型的值,都会显示 [object Object] 的。

为什么是这个样子?!永远要记住一句话,ECMAScript 语言中一切都是对象(划重点)!我们使用对象的原型方法,它们的内在最终都是指向 Object 呀,所以该方法就会被覆盖。

该文档原文中有这么一句话:

The above definition of toString preserves compatibility for legacy code that uses toString as a test for those specific kinds of built-in objects. It does not provide a reliable type testing mechanism for other kinds of built-in or program defined objects. In addition, programs can use @@toStringTag in ways that will invalidate the reliability of such legacy type tests.

讲的就是这个道理:

toString 的上述定义保留了对使用 toString 作为这些特定种类的内置对象的测试的传统代码的兼容性。 它不为其他类型的内置或程序定义的对象提供可靠的类型测试机制。 此外,程序可以使用 @@ toStringTag,这将使这种传统类型测试的可靠性无效。

因此,我们可以使用 Object.prototype.toString.call() 来辨别类型。这样就会正确显示了,示例如下:

let a = [1,2,3];
console.log( Object.prototype.toString.call(a) );  // [object Array]

let b = Symbol('name'); 
console.log( Object.prototype.toString.call(b) );  // [object Symbol]

当然了,我们也不必使用这种方式,我在这里讲的目的也是为了我们的初衷,深入这门语言的实质。详情可以参阅 Obecjt.prototype.toString。

如果我们想判断类型,可以使用例如 isArray 和我们熟知的 typeofinstanceof 就好了。

是不是很开心?我们今天学习和扩展了这么多知识呢!马上就结束啦~再耐心地忍耐一小下。

(13)Symbol.unscopables

概述:该方法是对象值属性,其自身和继承的属性名称是从 with 关联对象的环境绑定中排除的属性名称。

这个语法有关于 with 关键字,官方都不推荐我们用啦。。我们就不要使用,如果感兴趣,可以看 MDN 上我翻译的解释:Symbol.unscopables。

总结

本节篇幅较长,主要是介绍了 Symbol 的方法。其中穿插了原型和原型链的概念作为复习和扩展。同时我们理解了 Object.prototype.toString() 方法的使用与实质。

如果本节对你有任何帮助的话,不妨点一个爱心哦~你的支持是我不断创作的动力;同时,如果你有任何的疑问,欢迎在下方留言,我会进行解答哦。


下一节

基础类型之 Symbol(四)

你可能感兴趣的:(基础类型之 Symbol(三))