ES6+ Reflect(一)
任何一门语言在走向成熟的过程都是趋向精细化和规范化,在 API 设计之初满足当时的需求场景。随着前端的飞速发展,软件复杂度的提升,很多 API 在使用过程中存在很多使用别扭的情况,不符合软件开发的规范。上一节我们学习了 Proxy,Proxy 的设计目的是为了取代 Object.defineProperty,优化,使得数据劫持的过程更加规范。
本节我们将学习 ES6 新的全局对象 —— Reflect(反射),首先我们要了解一下,为什么会新这么全局对象?Reflect 上的一些基本上都可以在 Object 上找到,找不到的,也是可以通过对对象命令式的操作去实现的;那么为什么还要新呢?本节我们将学习 Reflect 的相关知识。
Reflect 是内置的对象,它提供了 JavaScript 操作的。这些与 Proxy 中的 handlers 相同。与大多数全局对象不同 Reflect
并非构造,所以不能通过 new 运算符对其进行,或者将 Reflect
对象作为来。Reflect
的所有和都是静态的(类似 JSON
或者 Math
等对象)。
Reflect 可以检查对象上是否存在特定,可以使用 Reflect.has()
检测。
let key = Symbol.for('a');
const obj = {
name: 'imooc',
lession: 'ES6 Wiki',
[key]:
}
console.log(Reflect.has(obj, 'name')); // true
console.log(Reflect.has(obj, 'age')); // false
可以使用 Reflect.get()
对象上的值。
console.log(Reflect.get(obj, 'name')); // imooc
可以使用 Reflect.set()
为对象新的。
const res = Reflect.set(obj, 'age', );
console.log(res); // true
console.log(obj); // {name: "imooc", lession: "ES6 Wiki", age: 7}
使用 Reflect.ownKeys()
对象上的自有。
console.log(Object.keys(obj)); // ["name", "lession"]
console.log(Reflect.ownKeys(obj)); // ["name", "lession", Symbol(a)]
上面的可以看出,使用 Object.keys()
不到是 Symbol 的值。
Reflect 对象上的并不是专门为对象设计的,而是在语言层面的,它可以拿到语言内部的,和 Proxy 的结合可以实现元编程。并且每个操作都是有返回值的,上节我们使用 Proxy 简单地实现了 Vue3 的响应式。但是在 Vue3 源码中和设置对象上的使用的是 Reflect,Reflect 会返回状态表示和设置的成功与否。
// const res = target[key]; // 上节
const res = Reflect.get(target, key); // target上key的值
// target[key] = value; // 上节
const result = Reflect.set(target, key, value); // 设置目标对象key的值
上面的两段是 Vue3 中的源码,因为在源码中需要知道或赋值的结果,因为可能失败。在 ES5 中如果想要监听劫持操作的结果需要使用 try...catch
的方式。
try {
Object.defineProperty(obj, prop, descriptor);
// success
} catch (e) {
// failure
}
Reflect 在操作对象时是有返回结果的,而 Object.defineProperty 是没有返回结果的,如果失败则会抛出异常,所以需要使用 try...catch
来捕获异常。
Object 中操作数据时,有一些是命令式的操作,如:delete obj.a
、name in obj
,Reflect 则将一些命令式的操作如 delete
,in
等使用来替代,这样做的目的是为了让更加好维护,更容易向下兼容;也避免出现更多的保留字。
// ES5
'assign' in Object // true
// ES6
Reflect.has(Object, 'assign') // true
delete obj.name; // ES5
Reflect.deleteProperty(obj, 'name'); // ES6
Reflect 的出现是为了取代 Object 中一些属于语言层面的 API,这些 API 在 Object 上也是可以找到的,并且它们的基本是相同的。上面我们也提到了 Reflect 和 Proxy 中 handlers 的是一一对应的,在很多场景中它门都是配套使用的。这里我们就来学习一下 Reflect 提供的静态:
Reflect.get()
是从对象中读取的值,类似 ES5 中访问器语法: obj[key]
,但是它是通过来获得返回结果的。
语法:
Reflect.get(target, propertyKey[, receiver])
如果目标值 target 类型不是 Object
,则抛出 TypeError
。
// Object
var obj = { a: , b: };
Reflect.get(obj, "a"); // 1
// Array
Reflect.get(["a", "b", "c"], ); // "one"
第三个参数 receiver 是 this 所在的上下文,不传时指的是当前对象,如果传如人对象则 this 指向该对象。下面我们来看个实例:
let obj = {
name: 'imooc',
lesson: 'ES5 Wiki',
get info() {
console.log(`这是 ${this.lesson}`);
return
}
};
Reflect.get(obj, 'info'); // 这是 ES5 Wiki
Reflect.get(obj, 'info', {lesson: 'ES6 Wiki'}); // 这是 ES5 Wiki
Reflect.set()
是在对象上设置,类似 ES5 中设置语法:obj[key] = value
,它也是通过的方式来对对象设置的。
语法:
Reflect.set(target, propertyKey, value[, receiver])
这个会返回 Boolean 值,表示在目标对象上设置是否成功。
// Object
var obj = {};
Reflect.set(obj, "name", "imooc"); // true
console.log(obj.name); // "imooc"
// Array
var arr = ["a", "b", "c"];
Reflect.set(arr, , "C"); // true
console.log(arr); // ["a", "b", "C"]
使用可以截断数组:
var arr = ["a", "b", "c"];
Reflect.set(arr, "length", ); // true
console.log(arr); // ["a", "b"]
当有 receiver 参数时,如果 receiver 对象中有 propertyKey ,则会使用 receiver 对象中的值。
Reflect.set(obj, 'lession', 'ES5 Wiki', {lession: 'ES6 Wiki', age: });
console.log(obj); // {name: "imooc", lesson: "ES5 Wiki"}
Reflect.delete
允许对象的。它类似 ES5 中的 delete
操作符,但它也是,通过来实现。
语法:
Reflect.deleteProperty(target, propertyKey)
这个的返回值是 Boolean 值,如果成功的话,返回 true;失败的话返回 false。我们来看下面的实例:
var obj = {
name: 'imooc',
lession: 'ES6 Wiki'
};
var r1 = Reflect.deleteProperty(obj, 'name');
console.log(r1); // true
console.log(obj); // {lession: "ES6 Wiki"}
var r2 = Reflect.deleteProperty(Object.freeze(obj), 'lession');
console.log(r2); // false
上面的例子中使用 Object.freeze()
来冻结 obj 对象使之不能被。
Reflect.has()
可以检查对象上是否含有特定的,这个相当于 ES5 的 in
操作符。
语法:
Reflect.has(target, propertyKey)
这个的返回结果是 Boolean 值,如果存在就返回 true,不存在就返回 false。当然如果目标对象 (target) 不是对象,那么就会抛出异常。
Reflect.has({x: }, "x"); // true
Reflect.has({x: }, "y"); // false
// 如果该存在于原型链中,也返回true
Reflect.has({x: }, "toString"); // true
这也可检查构造的。
function A(name) {
this.name = name || 'imooc';
}
// 在原型上
A.prototype.getName = function() {
return this.name;
};
var a = new A();
console.log('name' in a); // true
console.log('getName' in a); // true
let r1 = Reflect.has(a, 'name');
let r2 = Reflect.has(a, 'getName');
console.log(r1, r2); // true true
Reflect.ownKeys()
返回由目标对象自身的键组成的数组。
语法:
Reflect.ownKeys(target)
如果这个目标对象不是对象那么这个就会抛出异常。这个数组的值等于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
我们来看下面的实例:
let a = Symbol.for('a');
let b = Symbol.for('b');
let obj = {
[a]: ,
[b]: ,
key1: ,
key2:
};
let arr1 = Object.getOwnPropertyNames(obj);
console.log(arr1); // [ 'key1', 'key2' ]
let arr2 = Object.getOwnPropertySymbols(obj);
console.log(arr2); // [ Symbol(a), Symbol(b) ]
let arr3 = Reflect.ownKeys(obj);
console.log(arr3); // [ 'key1', 'key2', Symbol(a), Symbol(b) ]
本节主要学习了 ES6 新增的全局对象 Reflect
,它的目的是为了分离 Object 中属于语言部分的,每个使用 Reflect
下的操作的对象都要返回值。 Reflect
对象和 Proxy
下的是一一对应的,二者配合可以实现很多。Vue3 中的数据响应就是使用的它们。