ECMAScript 6笔记(Symbol, Proxy 和 Reflect)

一、Symbol

ES陆引进了1种新的原来数据类型Symbol,表示无比的值。它是JavaScript语言的第多样数据类型,前五种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

1.1 概述

Symbol值通过Symbol函数生成。那正是说,对象的习性名后天得以有三种档次,一种是原先就部分字符串,另壹种正是骤增的Symbol类型。凡是属性名属于Symbol类型,就都是绝无仅有的,能够保障不会与别的属性名产生争辩。接受一个字符串作为参数,表示对Symbol实例的叙述,首假如为着在控制台展现,也许转为字符串时,比较易于区分

var s1 = Symbol('foo');
var s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

Symbol函数的参数只是意味着对当前Symbol值的讲述,因而等同参数的Symbol函数的重临值是不等于的。

// 没有参数的情况
var s1 = Symbol();
var s2 = Symbol();

s1 === s2 // false

// 有参数的情况
var s1 = Symbol("foo");
var s2 = Symbol("foo");

s1 === s2 // false

一.2 作为属性名的Symbol

var mySymbol = Symbol();

// 第一种写法
var a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
var a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });//ES5写法

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

瞩目,Symbol值作为对象属性名时,不可能用点运算符。同理,在对象的里边,使用Symbol值定义属性时,Symbol值必须放在方括号内部。

var mySymbol = Symbol();
var a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"

let s = Symbol();

let obj = {
  [s]: function (arg) { ... }
};

obj[s](123);

一.3 消除魔术字符串

魔术字符串指的是,在代码之中数十次面世、与代码形成强耦合的某1个实际的字符串只怕数值。作风非凡的代码,应该尽量解决魔术字符串,该由含义清晰的变量代替。

function getArea(shape, options) {
  var area = 0;

  switch (shape) {
    case 'Triangle': // 魔术字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }

  return area;
}

getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串'Triangle'

地点代码中,字符串“Triangle”正是一个魔术字符串。它往往涌出,与代码形成“强耦合”,不便宜以后的改动和维护。
常用的铲除魔术字符串的秘籍,就是把它写成2个变量。

var shapeType = {
  triangle: 'Triangle'
};

function getArea(shape, options) {
  var area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 });

上边代码中,大家把“Triangle”写成shapeType对象的triangle属性,那样就解除了强耦合。
设若条分缕析分析,可以发现shapeType.triangle等于哪个值并不重大,只要确定保障不会跟任何shapeType属性的值冲突即可。因而,那里就很符合改用Symbol值。

const shapeType = {
  triangle: Symbol()
};

一.四 属性名的遍历

Symbol作为属性名,该属性不汇合世在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()重临。不过,它也不是私人住房属性,有二个Object.getOwnPropertySymbols方法,能够博得内定对象的持有Symbol属性名。再次回到贰个数组,成员是当前指标的有着用作属性名的Symbol值。

var obj = {};

var foo = Symbol("foo");

Object.defineProperty(obj, foo, {
  value: "foobar",
});

for (var i in obj) {
  console.log(i); // 无输出
}

Object.getOwnPropertyNames(obj)
// []

Object.getOwnPropertySymbols(obj)
// [Symbol(foo)]

另3个新的API,Reflect.ownKeys方法能够回到全部品种的键名,包蕴常规键名和Symbol键名。

let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
};

Reflect.ownKeys(obj)
// [Symbol(my_key), 'enum', 'nonEnum']

var size = Symbol('size');

class Collection {
  constructor() {
    this[size] = 0;
  }

  add(item) {
    this[this[size]] = item;
    this[size]++;
  }

  static sizeOf(instance) {
    return instance[size];
  }
}

var x = new Collection();
Collection.sizeOf(x) // 0

x.add('foo');
Collection.sizeOf(x) // 1

Object.keys(x) // ['0']
Object.getOwnPropertyNames(x) // ['0']
Object.getOwnPropertySymbols(x) // [Symbol(size)]

上面代码中,对象x的size属性是八个Symbol值,所以Object.keys(x)、Object.getOwnPropertyNames(x)都爱莫能助获得它。那就招致了一种非私有的内部方法的成效。选取那性格子,为对象定义一些非私有的、但又希望只用于内部的秘诀

1.5 Symbol.for(),Symbol.keyFor()

Symbol.for()与Symbol()那三种写法,都会变动新的Symbol。它们的分歧是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每一次调用就赶回三个新的Symbol类型的值,而是会先检查给定的key是还是不是曾经存在,借使不设有才会新建二个值。比如,假诺您调用Symbol.for(“cat”)3肆遍,每一次都会重返同2个Symbol值,然则调用Symbol(“cat”)3八次,会回来220个不等的Symbol值。

var s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

var s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

Symbol.keyFor方法重返叁个已登记的Symbol类型值的key。

二、Proxy

Proxy用于修改有个别操作的默许行为,等同于在言语层面做出修改,所以属于1种“元编制程序”(meta
programming),即对编制程序语言进行编制程序。

2.1 概述

var proxy = new Proxy(target, handler)

Proxy对象的具有用法,都以地点那种情势,差异的只是handler参数的写法。个中,new
Proxy()表示生成3个Proxy实例,target参数表示所要拦截的对象对象,handler参数也是2个对象,用来定制拦截行为。

var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    console.log(`getting ${key}!`);
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, value, receiver) {
    console.log(`setting ${key}!`);
    return Reflect.set(target, key, value, receiver);
  }
});

obj.count = 1
//  setting count!
++obj.count
//  getting count!
//  setting count!
//  2

注意,要使得Proxy起作用,必须针对Proxy实例(上例是proxy对象)举办操作,而不是对准对象对象(上例是空对象)举办操作。

假诺handler未有安装任何阻拦,那就同样直接通往原对象。

var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"

var proxy = new Proxy({}, {
  get: function(target, property) {
    return 35;
  }
});

let obj = Object.create(proxy);
obj.time // 35

//proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截。

同二个拦截器函数,能够安装阻碍八个操作。

var handler = {
  get: function(target, name) {
    if (name === 'prototype') return Object.prototype;
    return 'Hello, '+ name;
  },
  apply: function(target, thisBinding, args) { return args[0]; },
  construct: function(target, args) { return args[1]; }
};

var fproxy = new Proxy(function(x,y) {
  return x+y;
},  handler);

fproxy(1,2); // 1
new fproxy(1,2); // 2
fproxy.prototype; // Object.prototype
fproxy.foo; // 'Hello, foo'

2.2 Proxy方法

(1)get(target, propKey, receiver)

阻拦对象属性的读取,比如proxy.foo和proxy[‘foo’],重临类型不限。最终二个参数receiver可选,当target对象设置了propKey属性的get函数时,receiver对象会绑定get函数的this对象。

(2)set(target, propKey, value, receiver)

阻挡对象属性的装置,比如proxy.foo = v或proxy[‘foo’] =
v,重临多少个布尔值。

(3)has(target, propKey)

截留propKey in proxy的操作,重回一个布尔值。

(4)deleteProperty(target, propKey)

拦截delete proxy[propKey]的操作,再次回到二个布尔值。

(5)enumerate(target)

掣肘for (var x in proxy),再次回到二个遍历器。

(6)ownKeys(target)

拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),重返1个数组。该办法再次回到对象拥有自个儿的性质,而Object.keys()仅再次回到对象可遍历的习性。

(7)getOwnPropertyDescriptor(target, propKey)

拦截Object.getOwnPropertyDescriptor(proxy,
propKey),重回属性的讲述对象。

(8)defineProperty(target, propKey, propDesc)

拦截Object.defineProperty(proxy, propKey,
propDesc)、Object.defineProperties(proxy, propDescs),重返三个布尔值。

(9)preventExtensions(target)

拦截Object.preventExtensions(proxy),重临1个布尔值。

(10)getPrototypeOf(target)

拦截Object.getPrototypeOf(proxy),重临2个目的。

(11)isExtensible(target)

拦截Object.isExtensible(proxy),重临一个布尔值。

(12)setPrototypeOf(target, proto)

拦截Object.setPrototypeOf(proxy, proto),再次来到一个布尔值。

假若指标对象是函数,那么还有三种额外操作能够阻碍。

(13)apply(target, object, args)

截留Proxy实例作为函数调用的操作,比如proxy(…args)、proxy.call(object,
…args)、proxy.apply(…)。

(14)construct(target, args, proxy)

掣肘Proxy实例作为构造函数调用的操作,比如new proxy(…args)。

实例方法查看阮一峰的ES6入门第2一章Proxy和Reflect

三、Reflect

Reflect对象与Proxy对象壹样,也是ES6为了操作对象而提供的新API。Reflect对象的宏图目标有诸如此类多少个。

3.1概述

(1)
Object目的的壹些鲜明属于语言内部的点子(比如Object.defineProperty),放到Reflect对象上。现阶段,某个方法同时在Object和Reflect对象上铺排,未来的新章程将只安排在Reflect对象上。

(二)修改某个Object方法的回到结果,让其变得更客观。比如,Object.defineProperty(obj,
name,
desc)在不能定义属性时,会抛出七个破绽百出,而Reflect.defineProperty(obj,
name, desc)则会回去false。

(3)让Object操作都变成函数行为。某个Object操作是命令式,比如name in
obj和delete obj[name],而Reflect.has(obj,
name)和Reflect.deleteProperty(obj, name)让它们成为了函数行为。

// 老写法
'assign' in Object // true

// 新写法
Reflect.has(Object, 'assign') // true

(肆)Reflect对象的艺术与Proxy对象的不二秘诀一壹对应,只尽管Proxy对象的格局,就能在Reflect对象上找到相应的方法。那就让Proxy对象能够便宜地调用对应的Reflect方法,达成私下认可行为,作为修改行为的底蕴。也正是说,不管Proxy怎么修改暗中同意行为,你总能够在Reflect上得到暗中同意行为。

var loggedObj = new Proxy(obj, {
  get(target, name) {
    console.log('get', target, name);
    return Reflect.get(target, name);
  },
  deleteProperty(target, name) {
    console.log('delete' + name);
    return Reflect.deleteProperty(target, name);
  },
  has(target, name) {
    console.log('has' + name);
    return Reflect.has(target, name);
  }
});

地点代码中,每八个Proxy对象的阻拦操作(get、delete、has),内部都调用对应的Reflect方法,保险原生行为能够寻常执行。添加的干活,正是将种种操作输出一行日志。

三.② Reflect对象的章程

Reflect对象的艺术清单如下,共15个。

  • Reflect.apply(target,thisArg,args)
  • Reflect.construct(target,args)
  • Reflect.get(target,name,receiver)
  • Reflect.set(target,name,value,receiver)
  • Reflect.defineProperty(target,name,desc)
  • Reflect.deleteProperty(target,name)
  • Reflect.has(target,name)
  • Reflect.ownKeys(target)
  • Reflect.enumerate(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

具体方法查看阮1峰的ES陆入门第3壹章Proxy和Reflect

参考阮一峰的书籍ECMAScript 6
入门
,谢谢阮大神!