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

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

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

  • Symbol

创建Symbol

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

Symbol的辨识方法

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

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

Symbol的使用方法

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

let firstName = Symbol("first name");

let person = {
[firstName]: "JiaJia"
}; // 将属性设置为只读
Object.defineProperty(person, firstName, { writable: false }); let lastName = Symbol("last name"); Object.defineProperties(person, {
[lastName]: {
value: "Liu",
writable: false
}
}); console.log(person[firstName]); // "JiaJia"
console.log(person[lastName]); // "Liu"

Symbol共享体系

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

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

let uid = Symbol.for("uid");
let object = {}; object[uid] = "12345"; console.log(object[uid]); // "12345"
console.log(uid); // "Symbol(uid)"

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

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

let uid = Symbol.for("uid");
let object = {
[uid]: "12345";
}; console.log(object[uid]); // "12345"
console.log(uid); // "Symbol(uid)" let uid2 = Symbol.for("uid"); console.log(uid === uid2); // true
console.log(object[uid2]); // "12345"
console.log(uid2); // "Symbol(uid)"

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

let uid = Symbol.for("uid");
console.log(Symbol.keyFor(uid)); // "uid" let uid2 = Symbol.for("uid");
console.log(Symbol.keyFor(uid2)); // "uid" let uid3 = Symbol("uid");
console.log(Symbol.keyFor(uid3)); // undefined

Symbol与类型强制转换

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

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

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

Symbol属性检索

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

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

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

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

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

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

let uid = Symbol("uid");
let object = {
[uid]: "12345"
}; let symbols = Object.getOwnPropertySymbols(object); console.log(symbols.length); // 1
console.log(symbols[0]); // "Symbol(uid)"
console.log(object[symbols[0]]); // "12345"

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

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

Symbol.hasInstance方法

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

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

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

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

obj instanceof Array;

上述代码等价于

Array[Symbol.hasInstance](obj);

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

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

function MyObject() {

}

let obj = new MyObject();

console.log(obj instanceof MyObject); // true

Object.defineProperty(MyObject, Symbol.hasInstance, {
value: function(v) {
return false;
}
}); console.log(obj instanceof MyObject); // false

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

Symbol.isConcatSpreadable 属性

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

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

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

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

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

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

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

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

let collection = {
0: "Hello",
1: "World",
length: 2,
[Symbol.isConcatSpreadable]: false
}; let message = [ "Hi" ].concat(collection); console.log(message.length); // 2
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

    接受一个字符串参数,根据匹配内容将字符串分解,并返回一个包含分解后片段的数组
// 实际上等价于 /^.{10}$/
let hasLengthOf10 = {
[Symbol.match]: function(value) {
return value.length === 10 ? [value.substring(0, 10)] : null;
},
[Symbol.replace]: function(value, replacement) {
return value.length === 10 ? replacement : value;
},
[Symbol.search]: function(value) {
return value.length === 10 ? 0 : -1;
},
[Symbol.split]: function(value) {
return value.length === 10 ? ["", ""] : [value];
}
}; let message1 = "Hello world", // 11个字符
message2 = "Hello Dlph"; // 10个字符 let match1 = message1.match(hasLengthOf10),
match2 = message2.match(hasLengthOf10); console.log(match1); // null
console.log(match2); // ["Hello Dlph"] let replace1 = message1.replace(hasLengthOf10),
replace2 = message2.replace(hasLengthOf10); console.log(replace1); // "Hello world"
console.log(replace2); // "Hello Dlph" let search1 = message1.search(hasLengthOf10),
search2 = message2.search(hasLengthOf10); console.log(search1); // -1
console.log(search2); // 0 let split1 = message1.split(hasLengthOf10),
split2 = message2.split(hasLengthOf10); console.log(split1); // ["Hello world"]
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构造函数传递一个参数时。

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

function Temperature(degrees) {
this.degrees = degrees;
} Temperature.prototype[Symbol.toPrimitive] = function(hint) {
switch (hint) {
case "string":
return this.degrees + "\u00b0"; // degrees symbol
case "number":
return this.degrees;
case "default":
return this.degrees + " degrees";
}
}; var freezing = new Temperature(32); console.log(freezing + "!"); // "32 degrees!"
console.log(freezing / 2); // 16
console.log(String(freezing)); // "32°"
  • + 操作符触发的是默认模式;
  • / 操作符触发的是数字模式;
  • String() 函数触发字符串模式。

Note

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

Symbol.toStringTag属性

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

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

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

function Person(name) {
this.name = name;
} var me = new Person("JiaJia"); console.log(me.toString()); // "[object Object]"
console.log(Object.prototype.toString.call(me)); // "[object Object]" // 为对象定义自己的 Symbol.toStringTag 值
Person.prototype[Symbol.toStringTag] = "Person"; // toString() 方法默认返回 Symbol.toStringTag 的值
console.log(me.toString()); // "[object Person]"
console.log(Object.prototype.toString.call(me)); // "[object Person]" // 自定义 toString 方法
Person.prototype.toString = function() {
return this.name;
} console.log(me.toString()); // "JiaJia"
// 自定义 toString() 方法后,不会影响 Object.prototype.toString.call() 方法的值
console.log(Object.prototype.toString.call(me)); // "[object Person]"
  • toString() 方法默认返回 Symbol.toStringTag 的值。
  • 自定义 toString() 方法后,不会影响 Object.prototype.toString.call() 方法的值

Symbol.unscopables属性

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

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

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

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

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

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

// 已默认内置到ES6中
Array.prototype[Symbol.unscopables] = Object.assign(Object.create(null), {
copyWithin: true
entries: true
fill: true
find: true
findIndex: true
includes: true
keys: true
});

【读书笔记】【深入理解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. myecplise自带的tomcat问题

    今天做一个项目时候,发现myecplise自带的tomcat上面部署了是可以运行的,可是当部署到自己下载的tomcat时候,就报错,tomcat可以启动,项目无法启动,查了问题,发现是web,xml中 ...

  2. MySQL Group Replication 动态添加成员节点

    前提: MySQL GR 3节点(node1.node2.node3)部署成功,模式定为多主模式,单主模式也是一样的处理. 在线修改已有GR节点配置 分别登陆node1.node2.node3,执行以 ...

  3. .net 连接SqlServer数据库及基本增删改查

    一.写在前面 因为这学期选修的 .net 课程就要上机考试了,所以总结下.net 操作 SqlServer 数据的方法.(因为本人方向是 Java,所以对.net 的了解不多,但以下所写代码均是经过测 ...

  4. iis部署php项目

    今天跟着学习了如何在IIS下部署php项目,操作很简单,记录一下步骤! 1.启动iis服务器 最后点击确定就完成了 2.打开iis 点击进入即可 3.创建网站 进入添加网站. 添加注意事项如图所示! ...

  5. 设计模式之 - 策略模式(Strategy Pattern)

    引入:项目中涉及到工作流,当然这个工作流的实现是由用户根据不同的策略或者说方式传入处理这个事件的人的审批链,后台在存储过程中进行解析,然后生成最终的审批链,在系统中流转进行审批. 比如审批链: 张三 ...

  6. 什么是AJAX? AJAX:”Asynchronous JavaScript and XML”中文意思:异步JavaScript和XML。

    指一种创建交互式网页应用的网页开发技术. AJAX并非缩写词,而是由Jesse James Gaiiett创造的名词. 不是指一种单一的技术,而是有机地利用了一系列相关的技术: web标准( Stan ...

  7. Android模拟器

    一.Genymotion 1.下载安装:https://www.genymotion.com/download/  (注:安装前需要先注册) 因为Genymotion运行需要VirtualBox,如果 ...

  8. Linux下安装破解JIRA 6.3.6 并连接MYSQL5

    序言 JIRA是澳大利亚 Atlassian 公司开发的一款优秀的问题跟踪管理软件工具,可以对各种类型的问题进行跟踪管理,包括缺陷.任务.需求.改进等.JIRA采用J2EE技术,能够跨平台部署.它正被 ...

  9. js数组操作-添加,删除

    js 数组操作常用方法. push():在数组后面加入元素,并返回数组的长度 unshift():在数组前面加入元素,并返回数组的长度 pop()删除最后一个元素 shift()删除第一个元素 var ...

  10. flex基础示例

    flex的一些基础用法: <!-- Flex布局已经得到了所有浏览器的支持:chrome21+.Opera12.1+.Firefox22+.safari6.1+.IE10+ Webkit内核浏览 ...