导读:上一节呢,我们剖析了 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%
。是不是有些绕?我画了个图给大家看:
我们可以看到,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() {}
的关系如下:
也就是说,Object
是 Function
的构造函数(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
和我们熟知的 typeof
和 instanceof
就好了。
是不是很开心?我们今天学习和扩展了这么多知识呢!马上就结束啦~再耐心地忍耐一小下。
(13)Symbol.unscopables
概述:该方法是对象值属性,其自身和继承的属性名称是从 with 关联对象的环境绑定中排除的属性名称。
这个语法有关于 with
关键字,官方都不推荐我们用啦。。我们就不要使用,如果感兴趣,可以看 MDN 上我翻译的解释:Symbol.unscopables。
总结
本节篇幅较长,主要是介绍了 Symbol 的方法。其中穿插了原型和原型链的概念作为复习和扩展。同时我们理解了 Object.prototype.toString()
方法的使用与实质。
如果本节对你有任何帮助的话,不妨点一个爱心哦~你的支持是我不断创作的动力;同时,如果你有任何的疑问,欢迎在下方留言,我会进行解答哦。
下一节
基础类型之 Symbol(四)