js版本之ES6特性简述【Proxy、Reflect、Iterator、Generator】(五)
目录
Proxy
Reflect
静态方法
部分实例
Iterator
实际开发迭代器的使用实例
迭代器(Iterator)应用
Generator
Proxy
Proxy 是 ES6 中新增的对象
Proxy
是JavaScript中的内置对象,它提供了一种机制,可以拦截并自定义各种操作,如属性访问、函数调用、构造函数调用等。Proxy
的构造函数接受两个参数:目标对象(被代理的对象)和一个处理器对象(用于定义拦截器)。
// 写法:target是目标对象,handler是处理器对象
const proxy = new Proxy(target, handler);
详解请看:ES6之---Proxy简介
Reflect
Proxy 是 ES6 中新增的对象【注:不可使用new操作符生成实例】
特点:
- 将 Object 对象的一些明显属于语言内部的方法(如 Object.defineProperty)放到 Reflect 对象上,现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来新的方法只在 Reflect 对象上部署。也就是说,从 Reflect 对象上可以获得语言内部的方法。
- 修改某些Object的内部方法返回结果,使其变的合理。(以Object.defineProperty为例, 现在如果没有办法定义时,则会报错,放在Reflect上面,则会返回false)
- 让 Object 操作都编程函数行为,某些 Object 操作是命令式,比如 name in obj 和 delete obj [name],而 Reflect.has(obj, name) 和 Reflect.deleteProperty(obj, name) 让它们变成了函数行为。
- Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法,这就是 Proxy 对象可以方便的调用对应的 Reflect 方法来完成默认行为,作为修改行为的基础。也就是说,无论 Proxy 怎么修改默认行为,我们总可以在 Reflect 上获取到默认行为。
静态方法
- Reflect.apply
- Reflect.construct
- Reflect.defineProperty
- Reflect.deleteProperty
- Reflect.get
- Reflect.getOwnPropertyDescriptor
- Reflect.getPrototypeOf
- Reflect.has
- Reflect.isExtensible
- Reflect.ownKeys
- Reflect.preventExtensions
- Reflect.set
- Reflect.setPrototypeOf
部分实例
Reflect.construct(target, args)
- Reflect.construct 方法等同于 new target(…args),这提供了一种不使用 new ,来调用构造函数的方法。
- 如果 Reflect.construct () 方法的第一个参数不是对象,会报错。
// 说明1的例子如下:
function Greeting(name) {
this.name = name;
}
// new 的写法
const instance = new Greeting('张三');
// Reflect.construct 的写法
const instance1 = Reflect.construct(Greeting, ['张三']); // {name: '张三'}
// 说明2的例子如下:
console.log(Reflect.construct(1, 'baz')) // TypeError: Reflect.construct called on non-object
Reflect.deleteProperty(target, name)
- Reflect.deleteProperty方法等同于delete obj[name],用于删除对象的属性。
- 注意:**如果删除成功,或者被删除的属性不存在,返回 true ;删除失败,被删除的属性依然存在,返回 false **
- 如果 Reflect.deleteProperty() 方法的第一个参数不是对象,会报错。
// 说明1,2的例子如下:
var myObject = {
foo: 1,
}
console.log(Reflect.deleteProperty(myObject, 'foo')) // true
console.log(Reflect.deleteProperty(myObject, 'zzz')) // true
// 此时的myObject 就是{}
Reflect.get(target, name, receiver)
- Reflect.get方法查找并返回 target 对象的 name 属性值,如果没有该属性,则返回 undefined 。
- 如果 name 属性部署了读取函数(getter),则读取函数的 this 绑定 receiver 。
- 如果第一个参数不是对象, Reflect.get 方法会报错。
// 说明1的例子如下:
var myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
}
console.log(Reflect.get(myObject, 'foo')) // 1
console.log(Reflect.get(myObject, 'bar')) // 2
console.log(Reflect.get(myObject, 'baz')) // 3 没有传receiver,则this取原对象
console.log(Reflect.get(myObject, 'zzz')) // undefined
// 说明2的例子如下:
const otherObject = {
foo: 3,
bar: 4
}
console.log(Reflect.get(myObject, 'baz', otherObject)) // 7
// 说明3的例子如下:
console.log(Reflect.get(1, 'baz')) // TypeError: Reflect.get called on non-object
Reflect.has(target, name)
- Reflect.has方法对应name in obj里面的in运算符。
- 如果 Reflect.has() 方法的第一个参数不是对象,会报错。
// 说明1的例子如下:
var myObject = {
foo: 1,
}
console.log(Reflect.has(myObject, 'foo')) // true
console.log(Reflect.has(myObject, 'zzz')) // false
// 说明2的例子如下:
console.log(Reflect.has(1, 'baz')) // TypeError: Reflect.has called on non-object
观察者模式实例
// 这里就是简单观察者的核心逻辑,主要实现两个功能,一个就是observe,另一个就是observable
// 先定义了一个Set 集合,所有观察者函数都放进这个集合。
// observable 函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数
// 拦截函数 set 之中,会自动执行所有观察者
const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer());
return result;
}
// 使用如下:
const person = observable({
name: '张三',
age: 20
});
function print() {
console.log( ${person.name}, ${person.age} )
}
observe(print);
person.name = '李四';
Iterator
Iterator即迭代器,它是一种接口,为各种不同的数据结构提供了统一的访问机制,换句话说,只要有任何数据结构部署了迭代接口,就可以使用统一的方式的来遍历它。
ES6为数组和普通对象,以及新增的Map和Set提供了统一的遍历机制:迭代器(Iterator),并新增了for … of语法来使用迭代器。
实现可迭代接口的数据结构,一般都自身实现或继承了以Symbol.iterator
属性的,就属于可迭代对象。Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。
一个包含
next()
方法的对象,才可以称为一个迭代对象。next()
对象的会有返回一个对象,对象中包含两个值,如下所示:
value
:迭代器返回的任何JavaScript
值。done
为true
时可省略。done
:一个布尔值,为false
时表示迭代未停止,为true
时立即停止迭代器,且可以省略value
的值。
实际开发迭代器的使用实例
实际开发中,为每一个对象单独定义一个迭代器属性实在不是一件聪明事。我们可以仿照js引擎部署迭代器的做法,将迭代器署在对象原型上,这样由某个构造函数或类(指es6的class)所生成的每个对象都可以方便地进行遍历了。
构造函数写法
function Student(name, age){
this.name = name;
this.age = age;
}
Student.prototype[Symbol.iterator] = function(){
let index = 0;
let keys = [...Object.keys(this)];
let _this = this;
keys.sort((key1, key2) => { //按字母顺序对属性排序
return key1 < key2 ? -1 : 1;
})
return {
next(){
return index < keys.length ?
{value: _this[keys[index++]], done: false} :
{value: undefined, done: true}
}
}
}
let s = new Student('小明', 24);
for(let val of s){
console.log(val);
} //输出:24 '小明'
类写法
class Student{
constructor(name, age){
this.name = name;
this.age = age;
}
[Symbol.iterator](){
let index = 0;
let keys = [...Object.keys(this)];
keys.sort((key1, key2) => { //按字母顺序对属性排序
return key1 < key2 ? -1 : 1;
})
let _this = this;
return {
next(){
return index < keys.length ?
{value: _this[keys[index++]], done: false} :
{value: undefined, done: true}
}
}
}
}
迭代器(Iterator)应用
【1】 解构赋值
Array和Set的解构赋值就是借助迭代器来实现的
js引擎依次在左右两侧结构上调用next方法,进行逐个赋值,这样左侧数组的每个变量会对应被赋为右侧的值。
const [a, b] = [1, 2]; //a: 1, b: 2
const [c, d] = new Set([3, 4]); //c: 3, d: 4
【2】扩展运算符
ES6的扩展运算符可以将数组展开为一列,这也是借助Iterator接口实现的
let args = ['name', 'age'];
f(...args); //等价于f('name', 'age')
【3】return和throw
迭代器对象除了必要的next方法外,还可以部署return和throw方法,用于在for … of语句中终止遍历和抛出异常。
let s = {
name: '小明',
age: 24,
[Symbol.iterator]: function (){
let index = 0;
let keys = ['name', 'age'];
let _this = this;
return {
next(){
return index < keys.length ?
{value: _this[keys[index++]], done: false} :
{value: undefined, done: true}
},
return(){
... //结束循环前可以在这里执行某些操作,如关闭文件系统等
return {done: true}
},
throw(){
... //抛出异常时可以在这里执行某些操作
return {done: true}
}
}
}
}
for(let val of s){
console.log(val);
break; //该语句会触发迭代器对象的return方法
}
for(let val of s){
console.log(val);
throw new Error(); //该语句会触发迭代器对象的throw方法
}
【4】Iterator与Generator函数
Generator函数调用之后返回的就是一个迭代器对象,这个对象原生就具备next接口
let s = {
name: '小明',
age: 24,
[Symbol.iterator]: function* (){
yield this.name;
yield this.age;
}
}
Generator
Generator【生成器】是ES6中提供的一种异步编程解决方案,定义Generator函数在function
关键字和函数名中间使用*
星号,函数内部使用yield
关键字定义不同的状态。
注:需要注意的是,生成器函数定义时需要在函数关键字 function 后面加上星号(*),以标识该函数为生成器函数。另外,yield 关键字只能在生成器函数内部使用。
function* testGenerator(){
// yield定义一个状态
yield1 'css6之generator'
yield 'es新特性'
return 'generator' // 终结Generator,后面即使有yield关键字也无效
}
const g=testGenerator() // 返回 Generator 对象,通过next()方法移动状态
g.next() //{value:'-碗周',done:false }
g.next() //{value:'es新特性',done:false }
g.next() //{ value:generator',done:true }
Generator详解
此文借鉴了一下博主的优秀文章
https://blog.csdn.net/Rookie_lei/article/details/140790532
https://blog.csdn.net/qq_41694291/article/details/103432571
上一章:js版本之ES6特性简述【let和const、数组、函数、集合、Symbol】(四)