Reflect
概述
Reflect对象与Proxy对象一样,也是 ES6
为了操作对象而提供的新
API。Reflect对象的设计目的有这样几个。
(1)
将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。
(2)
修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
1234567891011121314// 老写法try { Object.defineProperty(target, property, attributes); // success} catch (e) { // failure ...
Proxy
概述
Proxy
用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta
programming),即对编程语言进行编程。
Proxy
可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy
这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
12345678910var obj = new Proxy({}, { get: function (target, propKey, receiver) { console.log(`getting ${propKey}!`); return Reflect.get(target, propKey, receiver); }, set: function (target, propKey, value, receiver) { console.log(`setting $ ...
Set 和 Map 数据结构
Set
基本用法
ES6 提供了新的数据结构
Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构。
12345678const s = new Set();[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));for (let i of s) { console.log(i);}// 2 3 5 4
上面代码通过add()方法向 Set 结构加入成员,结果表明 Set
结构不会添加重复的值。
Set函数可以接受一个数组(或者具有 iterable
接口的其他数据结构)作为参数,用来初始化。
12345678910111213141516171819// 例一const set = new Set([1, 2, 3, 4, 4]);[...set]// [1, 2, 3, 4]// 例二const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);items.size // 5// 例三const ...
Symbol
概述
ES5
的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin
模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是
ES6 引入Symbol的原因。
ES6
引入了一种新的原始数据类型Symbol,表示独一无二的值。它是
JavaScript
语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
Symbol
值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的
Symbol 类型。凡是属性名属于 Symbol
类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
1234let s = Symbol();typeof s// "symbol"
上面代码中,变量s就是一个独一无二的值。typeof运算符的结果,表明变量s是 ...
运算符的扩展
本章介绍 ES6 后续标准添加的一些运算符。
指数运算符
ES2016 新增了一个指数运算符(**)。
122 ** 2 // 42 ** 3 // 8
这个运算符的一个特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。
123// 相当于 2 ** (3 ** 2)2 ** 3 ** 2// 512
上面代码中,首先计算的是第二个指数运算符,而不是第一个。
指数运算符可以与等号结合,形成一个新的赋值运算符(**=)。
1234567let a = 1.5;a **= 2;// 等同于 a = a * a;let b = 4;b **= 3;// 等同于 b = b * b * b;
链判断运算符
编程实务中,如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。比如,读取message.body.user.firstName这个属性,安全的写法是写成下面这样。
12345678// 错误的写法const firstName = message.body.user.firstName || 'default ...
对象的新增方法
本章介绍 Object 对象的新增方法。
Object.is()
ES5
比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript
缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
ES6 提出“Same-value
equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
1234Object.is('foo', 'foo')// trueObject.is({}, {})// false
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
12345+0 === -0 //trueNaN === NaN // falseObject.is(+0, -0) // falseObject.is(NaN, NaN) // true
...
对象的扩展
对象(object)是 JavaScript 最重要的数据结构。ES6
对它进行了重大升级,本章介绍数据结构本身的改变,下一章介绍Object对象的新增方法。
属性的简洁表示法
ES6
允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
123456const foo = 'bar';const baz = {foo};baz // {foo: "bar"}// 等同于const baz = {foo: foo};
上面代码中,变量foo直接写在大括号里面。这时,属性名就是变量名,
属性值就是变量值。下面是另一个例子。
1234567891011function f(x, y) { return {x, y};}// 等同于function f(x, y) { return {x: x, y: y};}f(1, 2) // Object {x: 1, ...
数组的扩展
扩展运算符
含义
扩展运算符(spread)是三个点(...)。它好比 rest
参数的逆运算,将一个数组转为用逗号分隔的参数序列。
12345678console.log(...[1, 2, 3])// 1 2 3console.log(1, ...[2, 3, 4], 5)// 1 2 3 4 5[...document.querySelectorAll('div')]// [<div>, <div>, <div>]
该运算符主要用于函数调用。
12345678910function push(array, ...items) { array.push(...items);}function add(x, y) { return x + y;}const numbers = [4, 38];add(...numbers) // 42
上面代码中,array.push(...items)和add(...numbers)这两行,都是函数的调用,它们都使用了扩展运算符。该运 ...
函数的扩展
函数参数的默认值
基本用法
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
12345678function log(x, y) { y = y || 'World'; console.log(x, y);}log('Hello') // Hello Worldlog('Hello', 'China') // Hello Chinalog('Hello', '') // Hello World
上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。
为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。
123if (typeof y === 'undefined') { y = ...
数值的扩展
二进制和八进制表示法
ES6
提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。
120b111110111 === 503 // true0o767 === 503 // true
从 ES5
开始,在严格模式之中,八进制就不再允许使用前缀0表示,ES6
进一步明确,要使用前缀0o表示。
12345678910// 非严格模式(function(){ console.log(0o11 === 011);})() // true// 严格模式(function(){ 'use strict'; console.log(0o11 === 011);})() // Uncaught SyntaxError: Octal literals are not allowed in strict mode.
如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。
12Number('0b111') // 7Number('0o10 ...
