元编程 (Meta Programming) 是指编写可以操作或改变其他程序的程序。元编程可以改变 JavaScript 的一些基本操作的行为
主要与这三个对象有关:
Symbol:通过内置 Symbol 值复写 JS 语言中的基本操作Proxy:通过钩子函数拦截、改变 JS 语言的基本操作Reflect:获取 JS 语言内部的基本操作基本操作包括:属性访问和赋值、属性删除、属性枚举、函数调用、对象构造等
使用 . 或 [] 访问对象的属性,使用 = 给属性赋值
let obj = { a: 1 }
console.log(obj.a) // 属性访问
obj.b = 2 // 属性赋值
console.log(obj['b']) // 属性访问
let obj = { a: 1 }
delete obj.a
const obj = {
name: 'test',
age: 18,
}
for (k in obj) {
console.log(obj[k])
}
function add(a, b) {
return a + b
}
const result = add(1, 2)
function Person(name, age) {
this.name = name
this.age = age
}
const alice = new Person('Alice', 25)
通过内置 Symbol 值复写 JS 语言中的内部实现
Reflect 是一个内置的全局对象,对一些函数式的操作提供了面向对象的 API。 Reflect 不是一个函数对象,因此它是不可构造的。它所有的方法都是静态的,类似于 Math 对象
目前共 13 个静态方法,可以分为函数相关、原型相关、对象相关三大类
apply通过指定的 this 和参数列表发起对目标 (target) 函数的调用
Reflect.apply(target, thisArg, argumentsList)
target:目标函数thisArg:target 函数调用时绑定的 this 对象argumentsList:类数组,target 函数调用时的参数列表this 值的给定的函数后返回的结果target 对象不可调用,抛出 TypeError 异常示例:
function greet(name, age) {
console.log(`Hello, my name is ${name} and I am ${age} years old.`);
}
const args = ['John', 30];
Reflect.apply(greet, null, args);
// 类似于:
// greet(...args)
// greet.apply(null, args)
// Function.prototype.apply.call(greet, null, args)
该方法的行为有点像 [[007.原型与原型链#^new|new 操作符]] 构造函数,相当于运行 new target(...args)
Reflect.construct(target, argumentsList[, newTarget])
target:被运行的目标构造函数argumentsList:类数组,target 构造函数调用时的参数newTarget(可选):作为新创建对象的原型对象的 constructor 属性,参考 new.target 操作符,默认值为 targettarget(如果 newTarget 存在,则为 newTarget)函数为构造函数,argumentList 为其初始化参数的对象实例target 或者 newTarget 不是构造函数,抛出 TypeError 异常示例:
function Person(name, age) {
this.name = name;
this.age = age;
}
const args = ['John', 30];
const instance = Reflect.construct(Person, args);
// 类似于:
// new Person(...args)
Reflect.construct() Vs. Object.create():function OneClass() {
this.name = "one";
}
function OtherClass() {
this.name = "other";
}
// 创建一个对象:
var obj1 = Reflect.construct(OneClass, args, OtherClass);
// 与上述方法等效:
var obj2 = Object.create(OtherClass.prototype);
OneClass.apply(obj2, args);
console.log(obj1.name); // 'one'
console.log(obj2.name); // 'one'
console.log(obj1 instanceof OneClass); // false
console.log(obj2 instanceof OneClass); // false
console.log(obj1 instanceof OtherClass); // true
console.log(obj2 instanceof OtherClass); // true
虽然两种方式结果相同,但在创建对象过程中仍一点不同:
Object.create() 和 Function.prototype.apply() 时,如果不使用 new 操作符调用构造函数,构造函数内部的 new.target 值会指向 undefinedReflect.construct() 来创建对象,new.target 值会自动指定到 target(或者 newTarget,前提是 newTarget 指定了)function OneClass() {
console.log("OneClass");
console.log(new.target);
}
function OtherClass() {
console.log("OtherClass");
console.log(new.target);
}
var obj1 = Reflect.construct(OneClass, args);
// 输出:
// OneClass
// function OneClass { ... }
var obj2 = Reflect.construct(OneClass, args, OtherClass);
// 输出:
// OneClass
// function OtherClass { ... }
var obj3 = Object.create(OtherClass.prototype);
OneClass.apply(obj3, args);
// 输出:
// OneClass
// undefined
与 Object.getPrototypeOf() 方法几乎是一样的,都是返回指定对象的原型(即内部的 [[Prototype]] 属性的值)
Reflect.getPrototypeOf(target)
target:获取原型的目标对象nulltarget 不是 Object,抛出一个 TypeError 异常示例:
const obj = { x: 1 };
const proto = Reflect.getPrototypeOf(obj); // 相当于 Object.getPrototypeOf(obj)
console.log(proto === Object.prototype); // true
除了返回类型以外,Reflect.setPrototypeOf() 与 Object.setPrototypeOf() 方法是一样的。它可设置对象的原型(即内部的 [[Prototype]] 属性)为另一个对象或 null,如果操作成功返回 true,否则返回 false
Reflect.setPrototypeOf(target, prototype)
target:设置原型的目标对象prototype:target 对象的新原型(一个对象或 null)Boolean 值表明是否原型已经成功设置target 不是对象,或 prototype 既不是对象也不是 null,抛出一个 TypeError 异常示例:
const obj = { x: 1 };
Reflect.setPrototypeOf(obj, Array.prototype); // 设置 obj 的原型为 Array.prototype
console.log(obj instanceof Array); // true
用于获取属性值,与从对象中读取属性类似 (target[propertyKey]),但 receiver 参数可以改变 getter 的 this 对象
Reflect.get(target, propertyKey[, receiver])
target:需要取值的目标对象propertyKey:需要获取的值的键值receiver(可选):receiver 为 getter 调用时的 this 值target 不是 Object,抛出一个 TypeError 异常用于设置属性值,等同于 target[propertyKey] = value,但 receiver 参数可以改变 setter 的 this 对象
Reflect.set(target, propertyKey, value[, receiver])
target:设置属性的目标对象propertyKey:需要设置的值的键值value:设置的值receiver(可选):receiver 为 setter 调用时的 this 值Boolean 值表明是否成功设置属性target 不是 Object,抛出一个 TypeError 异常基本等同于 propertyKey in target,用于检查一个属性是否在某个对象中
Reflect.has(target, propertyKey)
target:目标对象propertyKey:属性名,需要检查目标对象是否存在此属性Boolean 值指示是否存在此属性target 不是 Object,抛出一个 TypeError 异常用于删除属性,类似于 delete 操作符
Reflect.deleteProperty(target, propertyKey)
target:目标对象propertyKey:属性名Boolean 值表明该属性是否被成功删除target 不是 Object,抛出一个 TypeError 异常示例:
var obj = { x: 1, y: 2 };
Reflect.deleteProperty(obj, "x"); // true
obj; // { y: 2 }
var arr = [1, 2, 3, 4, 5];
Reflect.deleteProperty(arr, "3"); // true
arr; // [1, 2, 3, , 5]
// 如果属性不存在,返回 true
Reflect.deleteProperty({}, "foo"); // true
// 如果属性不可配置,返回 false
Reflect.deleteProperty(Object.freeze({ foo: 1 }), "foo"); // false
基本等同于 Object.defineProperty(),但返回值略有不同:
Object.defineProperty 返回一个对象,或者如果属性没有被成功定义,抛出一个 TypeErrorReflect.defineProperty 方法只返回一个 Boolean,来说明该属性是否被成功定义Reflect.defineProperty(target, propertyKey, attributes)
target:目标对象propertyKey:要定义或修改的属性的名称attributes:要定义或修改的属性的描述Boolean 值指示了属性是否被成功定义target 不是 Object,抛出一个 TypeError 异常用于获取对象自身的某个属性的属性描述符,与 Object.getOwnPropertyDescriptor() 方法相似。如果在对象中存在,则返回给定的属性的属性描述符,否则返回 undefined
Reflect.getOwnPropertyDescriptor(target, propertyKey)
target:需要寻找属性的目标对象propertyKey:获取属性描述符的属性名undefinedtarget 不是 Object,抛出一个 TypeError 异常返回一个由目标对象自身的属性键组成的数组,等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
Reflect.ownKeys(target)
target:目标对象Arraytarget 不是 Object,抛出一个 TypeError 异常使一个对象变为不可扩展(阻止新属性添加到对象),基本等同于 Object.preventExtensions(),但有一些不同:
Reflect.preventExtensions() 的第一个参数不是一个对象,将抛出一个 TypeError 异常Object.preventExtensions() 非对象的第一个参数会被强制转换为一个对象Reflect.preventExtensions(target)
target:目标对象Boolean 值表明目标对象是否成功被设置为不可扩展target 不是 Object,抛出一个 TypeError 异常判断一个对象是否可扩展(即是否能够添加新的属性)。与 Object.isExtensible() 方法相似,但有一些不同:
Reflect.isExtensible() 的第一个参数不是一个对象,将抛出一个 TypeError 异常Object.isExtensible() 非对象的第一个参数会被强制转换为一个对象Reflect.isExtensible(target)
target:目标对象Boolean 值表明该对象是否可扩展target 不是 Object,抛出一个 TypeError 异常不使用 Reflect:
const obj = {
_name: 'test',
get name() {
return this._name;
}
};
const proxyObj = new Proxy(obj, {
get(target, property, receiver) {
return target[property];
},
});
const child = {
_name: 'child'
};
Reflect.setPrototypeOf(child, proxyObj);
child.name // test,期望为 child
proxyObj.name // test
因为代理对象的 get 拦截中固定返回的 target[property],target 永远指向 obj,所以拿到的永远是 obj 的 _name 属性值
const obj = {
_name: 'test',
get name(){
return this._name;
}
};
const proxyObj = new Proxy(obj,{
get(target, property, receiver){
return Reflect.get(target, property, receiver)
},
});
const child = {
_name: 'child'
};
Reflect.setPrototypeOf(child, proxyObj);
child.name // child
proxyObj.name // test
当使用 Reflect 时可以正确转发运行时上下文,其实主要就是 receiver 这个参数,receiver 代表的是代理对象本身或者继承自代理对象的对象,它表示触发 get 时正确的上下文