在ES5及早期版本中,JS语言包含5中原始类型:

  • 字符串型
  • 数字型
  • 布尔型
  • null
  • undefined

ES6引入了第六种原始类型:

  • Symbol

创建Symbol

  1. let firstName = Symbol();
  2. let person = {};
  3. person[firstName] = "JiaJia";
  4. console.log(person[firstName]); // "JiaJia"

Symbol的辨识方法

使用 typeof 来检测辨识是否为Symbol。

  1. let symbol = Symbol("test symbol");
  2. console.log(typeof symbol); // "symbol"

Symbol的使用方法

所有使用可计算属性名的地方,都可以使用Symbol。

  1. let firstName = Symbol("first name");
  2. let person = {
  3. [firstName]: "JiaJia"
  4. };
  5. // 将属性设置为只读
  6. Object.defineProperty(person, firstName, { writable: false });
  7. let lastName = Symbol("last name");
  8. Object.defineProperties(person, {
  9. [lastName]: {
  10. value: "Liu",
  11. writable: false
  12. }
  13. });
  14. console.log(person[firstName]); // "JiaJia"
  15. console.log(person[lastName]); // "Liu"

Symbol共享体系

ES6提供了一个可以随时访问的全局Symbol注册表。

使用 Symbol.for() 方法创建可共享的Symbol,它只接受一个参数,也就是即将创建的Symbol的字符串标识符,这个参数同样也被用作Symbol的描述。

  1. let uid = Symbol.for("uid");
  2. let object = {};
  3. object[uid] = "12345";
  4. console.log(object[uid]); // "12345"
  5. console.log(uid); // "Symbol(uid)"

Symbol.for() 方法首先在全局Symbol注册表中搜索键为“uid”的Symbol是否存在,如果存在,直接返回已有的Symbol;否则,创建一个新的Symbol,并使用这个键在Symbol全局注册表中注册,随即返回新创建的Symbol。

随后如果再传入同样的键调用 Symbol.for() 方法会返回相同的Symbol。

  1. let uid = Symbol.for("uid");
  2. let object = {
  3. [uid]: "12345";
  4. };
  5. console.log(object[uid]); // "12345"
  6. console.log(uid); // "Symbol(uid)"
  7. let uid2 = Symbol.for("uid");
  8. console.log(uid === uid2); // true
  9. console.log(object[uid2]); // "12345"
  10. console.log(uid2); // "Symbol(uid)"

可以使用 Symbol.keyFor() 方法在 Symbol 全局注册表中检索与Symbol有关的键。

  1. let uid = Symbol.for("uid");
  2. console.log(Symbol.keyFor(uid)); // "uid"
  3. let uid2 = Symbol.for("uid");
  4. console.log(Symbol.keyFor(uid2)); // "uid"
  5. let uid3 = Symbol("uid");
  6. console.log(Symbol.keyFor(uid3)); // undefined

Symbol与类型强制转换

其它类型没有与Symbol逻辑等价的值。

可以使用Symbol的toString()方法返回Symbol描述里的内容,但是直接与字符串拼接或者参与数值计算,则会抛出错误。

  1. let uid = Symbol.for("uid"),
  2. desc = String(uid);
  3. console.log(desc); // "Symbol(uid)"
  4. desc = uid + ""; // 报错
  5. // Cannot convert a Symbol value to a string
  6. let sum = uid / 1; // 报错
  7. // Cannot convert a Symbol value to a number

Symbol属性检索

Object.keys() 方法和 Object.getOwnPropertyNames() 方法可以检索对象中所有的属性名。

Object.keys() 方法返回所有可枚举的属性名;

Object.getOwnPropertyNames() 方法不考虑属性的可枚举性一律返回。

为了保持ES5函数的原有功能,这两个方法都不支持Symbol属性。

ES6中添加了一个 Object.getOwnPropertySymbols() 方法来检索Symbol属性。

该方法返回一个包含所有Symbol自有属性的数组。

  1. let uid = Symbol("uid");
  2. let object = {
  3. [uid]: "12345"
  4. };
  5. let symbols = Object.getOwnPropertySymbols(object);
  6. console.log(symbols.length); // 1
  7. console.log(symbols[0]); // "Symbol(uid)"
  8. console.log(object[symbols[0]]); // "12345"

通过 well-known Symbol 暴露内部操作

通过在原型链上定义与Symbol相关的属性来暴露更多的语言内部逻辑。

Symbol.hasInstance方法

每个函数都要一个 Symbol.hasInstance 方法,用于确定对象是否为函数的实例。

该方法在 Function.prototype 中定义,所以所有函数都继承了 instanceof 属性的默认行为。

为了确保 Symbol.hasInstance 不会被意外重写,该方法被定义为不可写、不可配置并且不可枚举。

Symbol.hasInstance 方法只接受一个参数,即要检查的值。如果传入的值时函数的实例,则返回 true。

  1. obj instanceof Array;

上述代码等价于

  1. Array[Symbol.hasInstance](obj);

本质上,ES6只是将 instanceof 操作符重新定义为此方法的简写语法。

现在引入方法调用后,就可以随意改变 instanceof 的运行方式了。

  1. function MyObject() {
  2. }
  3. let obj = new MyObject();
  4. console.log(obj instanceof MyObject); // true
  5. Object.defineProperty(MyObject, Symbol.hasInstance, {
  6. value: function(v) {
  7. return false;
  8. }
  9. });
  10. console.log(obj instanceof MyObject); // false

只有通过 Object.defineProperty() 才能改写一个不可写属性。

Symbol.isConcatSpreadable 属性

JS数组的 concat() 方法被设计用于拼接两个数组,但也可以接受非数组参数。

  1. let colors1 = [ "red", "green" ],
  2. colors2 = colors1.concat([ "blue", "black" ], "brown");
  3. console.log(colors2.length); // 5
  4. console.log(colors2); // ["red", "green", "blue", "black", "brown"]

JS规范声明,凡是传入数组参数,就会自动将它们分解为独立元素。ES6之前无法调整这个特性。

Symbol.isConcatSpreadable 属性是一个布尔值,如果该属性值为true,则表示对象有 length 属性和数字键,故它的数值型属性值应该被独立添加到 concat() 调用的结果中。

这个 Symbol.isConcatSpreadable 属性默认情况下不会出现在标准对象中,它只是一个可选属性,用于增强作用于特定对象类型的 concat() 方法的功能,有效简化其默认特性。

下面方法自定义了一个在concat()调用中与数组类型的新类型:

  1. let collection = {
  2. 0: "Hello",
  3. 1: "World",
  4. length: 2,
  5. [Symbol.isConcatSpreadable]: true
  6. };
  7. let message = [ "Hi" ].concat(collection);
  8. console.log(message.length); // 3
  9. console.log(message); // ["Hi", "Hello", "World"]

也可以将 Symbol.isConcatSpreadable 设置false,来防止元素在调用 concat() 方法时被分解。

  1. let collection = {
  2. 0: "Hello",
  3. 1: "World",
  4. length: 2,
  5. [Symbol.isConcatSpreadable]: false
  6. };
  7. let message = [ "Hi" ].concat(collection);
  8. console.log(message.length); // 2
  9. console.log(message); // ["Hi", {0: "Hello", 1: "World", length: 2, Symbol(Symbol.isConcatSpreadable): false}]

Symbol.match、Symbol.replace、Symbol.search和Symbol.split属性

字符串类型的几个方法可以接受正则表达式作为参数:

  • match(regex)

    确定给定字符串是否匹配正则表达式regex
  • replace(regex, replacement)

    将字符串中匹配正则表达式regex的部分替换为replacement
  • search(regex)

    在字符串中定位匹配正则表达式regex的位置索引
  • split(regex)

    按照匹配正则表达式regex的元素将字符串分切,并将结果存入数组中

在ES6中,可以使用对应的4个Symbol,自定义对象来替换正则表达式来进行匹配。

  • Symbol.match

    接受一个字符串类型的参数,如果匹配成功则返回匹配元素的数组,否则返回null
  • Symbol.replace

    接受一个字符串类型的参数和一个替换用的字符串,最终依然返回一个字符串
  • Symbol.search

    接受一个字符串参数,如果匹配到内容,则返回数字类型的索引位置,否则返回-1
  • Symbol.split

    接受一个字符串参数,根据匹配内容将字符串分解,并返回一个包含分解后片段的数组
  1. // 实际上等价于 /^.{10}$/
  2. let hasLengthOf10 = {
  3. [Symbol.match]: function(value) {
  4. return value.length === 10 ? [value.substring(0, 10)] : null;
  5. },
  6. [Symbol.replace]: function(value, replacement) {
  7. return value.length === 10 ? replacement : value;
  8. },
  9. [Symbol.search]: function(value) {
  10. return value.length === 10 ? 0 : -1;
  11. },
  12. [Symbol.split]: function(value) {
  13. return value.length === 10 ? ["", ""] : [value];
  14. }
  15. };
  16. let message1 = "Hello world", // 11个字符
  17. message2 = "Hello Dlph"; // 10个字符
  18. let match1 = message1.match(hasLengthOf10),
  19. match2 = message2.match(hasLengthOf10);
  20. console.log(match1); // null
  21. console.log(match2); // ["Hello Dlph"]
  22. let replace1 = message1.replace(hasLengthOf10),
  23. replace2 = message2.replace(hasLengthOf10);
  24. console.log(replace1); // "Hello world"
  25. console.log(replace2); // "Hello Dlph"
  26. let search1 = message1.search(hasLengthOf10),
  27. search2 = message2.search(hasLengthOf10);
  28. console.log(search1); // -1
  29. console.log(search2); // 0
  30. let split1 = message1.split(hasLengthOf10),
  31. split2 = message2.split(hasLengthOf10);
  32. console.log(split1); // ["Hello world"]
  33. console.log(split2); // ["", ""]

Symbol.toPrimitive 方法

在JS引擎中,当执行特定操作时,经常会尝试将对象转换到相应的原始值。

在ES6中,可以通过 Symbol.toPrimitive 方法更改这个原始值。

Symbol.toPrimitive 方法被定义在每一个标准类型的原型上,并且规定了当对象被转换为原始值时应当执行的操作。

该方法接受一个参数 类型提示hint),该值只有三种选择:"number"、"string"和"default"。根据参数返回值分别为 数字、字符和无类型偏好的值。

数字模式

  1. 调用 valueOf() 方法,如果结果为原始值,则返回;
  2. 否则,调用 toString() 方法,如果结果为原始值,则返回;
  3. 如果再无可选值,则抛出错误。

字符串模式

  1. 调用 toString() 方法,如果结果为原始值,则返回;
  2. 否则,调用 valueOf() 方法,如果结果为原始值,则返回;
  3. 如果再无可选值,则抛出错误。

默认模式

  1. 在大多数情况下,标准对象会将默认模式按数字模式处理(除了 Date 对象,在这种情况下,会将默认模式按照字符串模式处理)。

如果自定义了 Symbol.toPrimitive 方法,则可以覆盖这些默认的强制转换类型。

Note

默认模式只用于 == 运算、+ 运算及给Date构造函数传递一个参数时。

在大多数的操作中,使用的都是字符串模式或数字模式。

  1. function Temperature(degrees) {
  2. this.degrees = degrees;
  3. }
  4. Temperature.prototype[Symbol.toPrimitive] = function(hint) {
  5. switch (hint) {
  6. case "string":
  7. return this.degrees + "\u00b0"; // degrees symbol
  8. case "number":
  9. return this.degrees;
  10. case "default":
  11. return this.degrees + " degrees";
  12. }
  13. };
  14. var freezing = new Temperature(32);
  15. console.log(freezing + "!"); // "32 degrees!"
  16. console.log(freezing / 2); // 16
  17. console.log(String(freezing)); // "32°"
  • + 操作符触发的是默认模式;
  • / 操作符触发的是数字模式;
  • String() 函数触发字符串模式。

Note

针对三种模式返回不同的值是可行的,但更常见的做法是,将默认模式设置设置成与字符串模式或数字模式相同的处理逻辑。

Symbol.toStringTag属性

Symbol.toStringTag 所代表的属性在每一个对象中都存在,其定义了调用对象的 Object.prototype.toString.call() 方法时返回的值。

对于数组,调用该函数返回的值通常是“Array”,它正是存储在对象的 Symbol.toStringTag 属性中的。

同样的,也可以为自己的对象定义 Symbol.toStringTag 的值。

  1. function Person(name) {
  2. this.name = name;
  3. }
  4. var me = new Person("JiaJia");
  5. console.log(me.toString()); // "[object Object]"
  6. console.log(Object.prototype.toString.call(me)); // "[object Object]"
  7. // 为对象定义自己的 Symbol.toStringTag 值
  8. Person.prototype[Symbol.toStringTag] = "Person";
  9. // toString() 方法默认返回 Symbol.toStringTag 的值
  10. console.log(me.toString()); // "[object Person]"
  11. console.log(Object.prototype.toString.call(me)); // "[object Person]"
  12. // 自定义 toString 方法
  13. Person.prototype.toString = function() {
  14. return this.name;
  15. }
  16. console.log(me.toString()); // "JiaJia"
  17. // 自定义 toString() 方法后,不会影响 Object.prototype.toString.call() 方法的值
  18. console.log(Object.prototype.toString.call(me)); // "[object Person]"
  • toString() 方法默认返回 Symbol.toStringTag 的值。
  • 自定义 toString() 方法后,不会影响 Object.prototype.toString.call() 方法的值

Symbol.unscopables属性

with 语句的初衷是可以免于编写重复的代码。但加入 with 语句后,代码变的难以理解,执行性能很差且容易导致程序出错。最终,标准固定,在严格模式下不可以使用 with 语句。

尽管未来不会使用 with 语句,但是 ES6 仍在非严格模式下提供了向后兼容性。

  1. var values = [1, 2, 3],
  2. colors = ["red", "green", "blue"],
  3. color = "black";
  4. with(colors) {
  5. // 相当于调用了 colors.push 方法
  6. push(color);
  7. push(...values);
  8. }
  9. console.log(colors); // ["red", "green", "blue", "black", 1, 2, 3]

Symbol.unscopables 通常用于 Array.prototype,以在 with 语句中标识出不创建绑定的属性名。

Symbol.unscopables 是以对象的形式出现的,它的键是在with语句中要忽略的标识符,其对应的值必须是true。

这里是一个为数组添加默认的 Symbol.unscopables 属性的示例:

  1. // 已默认内置到ES6中
  2. Array.prototype[Symbol.unscopables] = Object.assign(Object.create(null), {
  3. copyWithin: true
  4. entries: true
  5. fill: true
  6. find: true
  7. findIndex: true
  8. includes: true
  9. keys: true
  10. });

【读书笔记】【深入理解ES6】#6-Symbol和Symbol属性的更多相关文章

  1. 20150206读书笔记<深入理解计算机系统>

    ●第一章 C是系统级编程的首选.C++显示支持抽象,属于应用级程序设计语言. 简单例子: 一个典型系统的硬件组成: 存储器的层次结构: 注:存储器层次结构的设计思想是,该层存储器作为下一层存储器的高速 ...

  2. 深入理解ES6之—符号与符号属性

    在js已有的基本类型(字符串,数值,布尔型,null和undefined)之外,es6引入了一种新的基本类型:==符号(Symbol)==.符号起初被设计用于创建对象私有成员. 符号没有字面量形式,你 ...

  3. python 进阶读书笔记1 -- 理解python一切皆对象

    理解python一切皆对象: 1.所有的类都是由type创建的 2.所有的类的基类都是object 3.type是类,也是实例,type的基类是object,type对象是由type创建的 4.obj ...

  4. 【读书笔记::深入理解linux内核】内存寻址【转】

    转自:http://www.cnblogs.com/likeyiyy/p/3837272.html 我对linux高端内存的错误理解都是从这篇文章得来的,这篇文章里讲的 物理地址 = 逻辑地址 – 0 ...

  5. 【读书笔记::深入理解linux内核】内存寻址

    我对linux高端内存的错误理解都是从这篇文章得来的,这篇文章里讲的 物理地址 = 逻辑地址 – 0xC0000000:这是内核地址空间的地址转换关系. 这句话瞬间让我惊呆了,根据我的CPU的知识,开 ...

  6. 20150207读书笔记<深入理解计算机系统2-1>

    第二章 信息存储 (1)  多数计算机以一个字节作为最小可寻址的存储器单元. 机器级程序将存储器看成一个非常大的字节数组,称为虚拟存储器. 存储器的每个字节都由唯一的数字标识,称为它的地址. 所有可能 ...

  7. 《Linux命令行与shell脚本编程大全》- 读书笔记3 - 理解shell

    当用户登录终端的时候,通常会启动一个默认的交互式shell.系统究竟启动哪个shell,这取决于用户配置.一般这个shell都是/bin/shell.默认的系统shell(/bin/sh)用于系统sh ...

  8. 读书笔记<深入理解JVM>01 关于OutOfMemoryError 堆空间的溢出

    代码片段如下: package com.gosaint.shiro; import java.util.ArrayList; import java.util.List; public class H ...

  9. 读书笔记-深入理解JVM虚拟机-1.OOM初探

    Java堆OOM(Out-Of-Memory)异常 执行例如以下程序,爆出异常 java.lang.OutOfMemoryError: Java heap space /** * VM Args:-X ...

随机推荐

  1. ZOJ ACM 1204 (JAVA)

    毕业好几年了,对算法还是比較有兴趣,所以想又一次開始做ACM题.俺做题比較任意,一般先挑通过率高的题来做. 第1204题,详细描写叙述请參考,ZOJ ACM 1204 1)难度分析 这个题目,基本的难 ...

  2. 在企业和应用市场发布Office Add-in

    作者:陈希章 发表于 2017年12月20日 我已经写了很多关于Office Add-in的内容,而且我相信你已经尝试过创建一两个Add-in了吧.作为一个开发人员,你有多种方式在自己的机器上使用你的 ...

  3. [UI列表]LoopScrollRect无限滑动不卡顿

    应用场景 对于背包界面,排行榜列表,聊天消息,等有大量的UI列表的界面,常规做法是为每一条数据生成一个格子,在数据量越大的情况下,会生成越来越多的Gameobject,引起卡顿. 这篇文章讲述的就是解 ...

  4. 自学Zabbix3.8.2-可视化Visualisation-maps网络地图

    自学Zabbix3.8.2-可视化Visualisation-maps网络地图 可以简单的理解为动态网络拓扑图,可以针对业务来配置zabbix map,通过map可以了解应用的整体状况:服务器是否异常 ...

  5. Shell编程之文本处理

    cut 截取自定列 可以按照某个字符进行分割,然后取出其中的指定列: [root@iz8vbbqbnh4ug2q9so5jflz logs]# --.txt /Dec/::: +] - /Dec/:: ...

  6. 虚拟机修改静态ip

    1.设置虚拟机的网络选择方式,使用NAT模式,选择这个模式后网段与主机的网段不是一个网段,一般选择桥接模式 2.选择VMnet8, 去掉 使用本地DHCP服务将ip分配给虚拟机 这个选项,不然设置ip ...

  7. cron任务解释

    cron本来是在linux下的一个定时任务执行工具,现在很多语言都支持cron,本文参考https://en.wikipedia.org/wiki/Cron,解释一下cron配置. 概述 cron配置 ...

  8. 【java设计模式】【行为模式Behavioral Pattern】迭代器模式Iterator Pattern

    package com.tn.pattern; public class Client { public static void main(String[] args) { Object[] objs ...

  9. ES6之Set方法与Map方法

    ES6提供了新的数据结构--Set与Map,Set本身是一个构造函数且成员的值是唯一的,没有重复的值!!!Set()是一个存储已排序的无重复元素的数据而Map()是一对数据Map()使用关键值Key来 ...

  10. PE格式第三讲扩展,VA,RVA,FA(RAW),模块地址的概念

    PE格式第三讲扩展,VA,RVA,FA的概念 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶VA概念 VA (vi ...