【javascript】Reflect学习笔记
Reflect
是一个内置的对象,它提供拦截 JavaScript 操作的方法,这些方法与 proxy handler 的方法相同。Reflect
不是一个函数对象,因此它是不可构造的,所以不能通过 new
运算符对其进行调用,或者将 Reflect
对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的(就像 Math 对象)。
静态方法
Reflect.apply(target, thisArgument, argumentsList)
对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply()
功能类似。
静态方法 Reflect.apply()
通过指定的参数列表发起对目标 (target) 函数的调用。
console.log(Reflect.apply(Math.floor, undefined, [1.75]))
// 输出: 1
Reflect.construct(target, argumentsList[, newTarget])
对构造函数进行 new
操作,相当于执行 new target(...args)
。
function reflectConstructExample() {const Person = function (name, age) {this.name = namethis.age = age}const person = Reflect.construct(Person, ['John', 30])console.log(person) // 输出: Person { name: 'John', age: 30 }
}
参数
target
: 被运行的目标构造函数argumentsList
: 类数组,目标构造函数调用时的参数newTarget
(可选): 作为新创建对象的原型对象的constructor
属性,参考new.target
操作符,默认值为target
new.target
: 是一个元属性(meta property),在ECMAScript 2015 (ES6) 中引入,用于检测函数是否通过 new
关键字调用。简单来说,当一个函数用 new
关键字调用时,new.target
会指向这个函数本身;如果函数是直接调用,new.target
的值则为 undefined。
Reflect.construct() vs Object.create()
在新语法 Reflect出现之前,是通过明确指定构造函数和原型对象(使用Object.create()
)来创建一个对象的。
function reflectConstructExample2() {const OneClass = function () {this.name = 'oneClass'}const TwoClass = function () {this.name = 'twoClass'}const obj = Reflect.construct(OneClass, [])console.log(obj) // 输出: OneClass {name: 'oneClass'}console.log(obj instanceof OneClass) // 输出: trueconsole.log(obj instanceof TwoClass) // 输出: false// 原型指向 TwoClass.prototypeconst obj1 = Reflect.construct(OneClass, [], TwoClass)console.log(obj1) // 输出: TwoClass {name: 'oneClass'}console.log(obj1 instanceof OneClass) // 输出: falseconsole.log(obj1 instanceof TwoClass) // 输出: true//Reflect.construct之前的原型指向 TwoClass.prototype实现方法const obj2 = Object.create(TwoClass.prototype) // object.create 创建一个新对象,原型指向 TwoClass.prototypeOneClass.call(obj2) // “借用” OneClass 的构造函数逻辑来初始化 obj2,对 OneClass 本身及其原型链无影响。console.log(obj2) // 输出: TwoClass {name: 'oneClass'}console.log(obj2 instanceof OneClass) // 输出: falseconsole.log(obj2 instanceof TwoClass) // 输出: true
}
虽然两种方式结果相同,但在创建对象过程中仍一点不同。
-
当使用
Object.create()
和Function.prototype.apply()
时,如果不使用new
操作符调用构造函数,构造函数内部的new.target
值会指向undefined
。 -
当调用
Reflect.construct()
来创建对象,new.target
值会自动指定到target
(或者newTarget
,前提是newTarget
指定了)。
Reflect.defineProperty(target, propertyKey, attributes)
和 Object.defineProperty()
类似,如果设置成功就会返回 true
。
function reflectDefinePropertyExample() {let obj = {}Reflect.defineProperty(obj, 'name', {value: 'kk'})console.log(obj.name) // 输出: kkconsole.log(Object.getOwnPropertyDescriptor(obj, 'name'))//输出:{value: 'kk', writable: false, enumerable: false, configurable: false}
}
检查属性是否被成功定义:
Object.defineProperty
方法,如果成功则返回一个对象,否则抛出一个 TypeError
。所以,当定义一个属性时,可以使用 try...catch
去捕获其中任何的错误。
Reflect.defineProperty
返回 Boolean
值作为成功的标识,所以使用 if...else
来判断结果。
if (Reflect.defineProperty(target, property, attributes)) {// 成功
} else {// 失败
}
Reflect.deleteProperty(target, propertyKey)
作为函数的delete
操作符,相当于执行 delete target[name]
。
function reflectDeletePropertyExample() {let obj = {name: 'kk',age: 18}console.log(Reflect.deleteProperty(obj, 'name')) // 输出: trueconsole.log(obj) // 输出: { age: 18 }// 尝试删除被冻结的属性会失败Object.freeze(obj)console.log(Reflect.deleteProperty(obj, 'age')) // 输出: false// 尝试删除不存在的属性会返回 trueconsole.log(Reflect.deleteProperty(obj, 'number')) // 输出: truevar arr = [1, 2, 3, 4, 5]Reflect.deleteProperty(arr, '3') // trueconsole.log(arr, arr.length) // 输出: [1, 2, 3, empty, 5] 5
}
上面当删除数组元素时,JavaScript 不会自动重新索引数组,而是将该位置标记为 “empty”(稀疏数组),数组长度保持为5,索引3的位置变成空位(empty)。
改进:
// 方案1:使用 splice 保持数组连续性
arr.splice(3, 1) // 输出: [1, 2, 3, 5]// 方案2:使用 filter 创建新数组(不修改原数组)
const newArr = arr.filter((_, index) => index !== 3) // 输出: [1, 2, 3, 5]// 方案3:显式设置为 undefined(保留位置)
Reflect.set(arr, 3, undefined) // 输出: [1, 2, 3, undefined, 5]
Reflect.get(target, propertyKey[, receiver])
获取对象身上某个属性的值,类似于 target[name]
。
function reflectGetExample() {let obj = {name: 'kk',age: 18}console.log(Reflect.get(obj, 'name')) // 输出: kkconsole.log(Reflect.get(obj, 'number')) // 输出: undefinedlet arr = [1, 2]console.log(Reflect.get(arr, 0)) // 输出: 1console.log(Reflect.get(arr, 2)) // 输出: undefined
}
receiver
参数用于指定 getter 函数中的 this
上下文。
当目标属性是访问器属性(getter)时,receiver
会作为 getter 函数的 this
值。如果省略该参数,则默认使用目标对象本身作为 this
function reflectGetExample2() {let obj = {name: 'kk',get greeting() {return `Hello, ${this.name}!`}}console.log(Reflect.get(obj, 'greeting')) // 输出: Hello, kk!const receiver = { name: 'll' }console.log(Reflect.get(obj, 'greeting', receiver)) // 输出: Hello, ll!
}
Reflect.getOwnPropertyDescriptor(target, propertyKey)
类似于 Object.getOwnPropertyDescriptor()
。如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined
。
function reflectGetOwnPropertyDescriptorExample() {let obj = {name: 'kk',age: 18}console.log(Reflect.getOwnPropertyDescriptor(obj, 'name'))// 输出: {value: 'kk', writable: true, enumerable: true, configurable: true}console.log(Reflect.getOwnPropertyDescriptor(obj, 'number')) // 输出: undefinedconsole.log(Reflect.getOwnPropertyDescriptor([], 'length'))// 输出: {value: 0, writable: true, enumerable: false, configurable: false}
}
- 如果该方法的第一个参数不是一个对象(一个原始值),那么将造成 TypeError 错误。
Object.getOwnPropertyDescriptor
,非对象的第一个参数将被强制转换为一个对象处理。// 尝试获取非对象的属性描述符 console.log(Object.getOwnPropertyDescriptor('a', '0')) //输出: {value: 'a', writable: false, enumerable: true, configurable: false} console.log(Reflect.getOwnPropertyDescriptor('a', '0')) // 输出报错: TypeError: Reflect.getOwnPropertyDescriptor called on non-object
Reflect.getPrototypeOf(target)
类似于 Object.getPrototypeOf()
。
function reflectGetPrototypeOfExample() {const object1 = {property1: 42}const proto1 = Reflect.getPrototypeOf(object1)console.log(proto1)// 输出: {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}console.log(Reflect.getPrototypeOf({}))// 输出: {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}console.log(Reflect.getPrototypeOf(Reflect.getPrototypeOf({}))) // 输出: nullconsole.log(Reflect.getPrototypeOf(proto1)) // 输出: null
}
在 ES2015 规范下,Reflect 抛异常,Object 强制转换非 Object 类型
console.log(Object.getPrototypeOf('foo'))
// 输出: String {'', constructor: ƒ, anchor: ƒ, at: ƒ, big: ƒ, …}
console.log(Reflect.getPrototypeOf('foo'))
// 输出报错: TypeError: Reflect.getPrototypeOf called on non-object
Reflect.has(target, propertyKey)
判断一个对象是否存在某个属性,和 in
运算符 的功能完全相同。
function reflectHasExample() {let obj = {name: 'kk',age: 18}console.log(Reflect.has(obj, 'name')) // 输出: trueconsole.log(Reflect.has(obj, 'number')) // 输出: falseconsole.log(Reflect.has([], 'length')) // 输出: true// Reflect.has 可以用于检查数组的索引console.log(Reflect.has([1, 2, 3], '0')) // 输出: trueconsole.log(Reflect.has([], '0')) // 输出: falseconsole.log(Reflect.has([0], '1')) // 输出: false// Reflect.has 也可以用于检查 Symbol 属性let sym = Symbol('test')obj[sym] = 'symbol value'console.log(Reflect.has(obj, sym)) // 输出: true
}
Reflect.isExtensible(target)
类似于 Object.isExtensible()
function reflectIsExtensibleExample() {let obj = {}console.log(Reflect.isExtensible(obj)) // 输出: trueObject.preventExtensions(obj) // 阻止扩展console.log(Reflect.isExtensible(obj)) // 输出: falselet sealedObj = Object.seal({ name: 'kk' })console.log(Reflect.isExtensible(sealedObj)) // 输出: falselet frozenObj = Object.freeze({ age: 18 })console.log(Reflect.isExtensible(frozenObj)) // 输出: false// Reflect.isExtensible 也可以用于检查数组let arr = [1, 2, 3]console.log(Reflect.isExtensible(arr)) // 输出: trueObject.preventExtensions(arr) // 阻止扩展console.log(Reflect.isExtensible(arr)) // 输出: false// 尝试在不可扩展的字符串上使用 Reflect.isExtensibleconsole.log(Reflect.isExtensible('hello'))// 输出报错: TypeError: Reflect.isExtensible called on non-object
}
Reflect.ownKeys(target)
返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys()
, 但不会受enumerable
影响)。它的返回值等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
。
function reflectOwnKeysExample() {let obj = {[Symbol.for('comet')]: 'comet',name: 'kk',age: 18,773: 773,0: 0,'-1': '-1',[Symbol.for('meteor')]: 'meteor'}console.log(Reflect.ownKeys(obj))// 输出: ['0', '773', 'name', 'age', '-1', Symbol(comet), Symbol(meteor)]let arr = [1, 2, 3]console.log(Reflect.ownKeys(arr))// 输出: ['0', '1', '2', 'length']
}
Reflect.preventExtensions(target)
类似于 Object.preventExtensions()
。返回一个Boolean。
function reflectPreventExtensionsExample() {let obj = {name: 'kk',age: 18}console.log(Reflect.preventExtensions(obj)) // 输出: trueconsole.log(Reflect.isExtensible(obj)) // 输出: false
}
- 如果该方法的 target 参数不是一个对象(是原始值),那么将造成一个 TypeError 异常。
- 对于Object.preventExtensions() 方法,非对象的 target 参数将被强制转换为对象。
console.log(Object.preventExtensions(1)) // 输出: 1 console.log(Reflect.preventExtensions(1)) // 输出报错: TypeError: Reflect.preventExtensions called on non-object
Reflect.set(target, propertyKey, value[, receiver])
将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回 true。
function reflectSetExample() {let obj = {name: 'kk',age: 18}console.log(Reflect.set(obj, 'name', 'll')) // 输出: trueconsole.log(obj.name) // 输出: llconsole.log(Reflect.set(obj, 'age', 20)) // 输出: trueconsole.log(obj.age) // 输出: 20console.log(Reflect.set('hello', 0, 'a'))// 输出报错: TypeError: Reflect.set called on non-object
}
Reflect.setPrototypeOf(target, prototype)
设置对象原型的函数。返回一个 Boolean,如果更新成功,则返回 true。
function reflectSetPrototypeOfExample() {let obj = {}console.log(Reflect.setPrototypeOf(obj, null)) // 输出: true
}
- 对象设置freeze
let obj = {} console.log(Reflect.setPrototypeOf(obj, null)) // 输出: true let obj2 = Object.freeze({}) console.log(Reflect.setPrototypeOf(obj2, null)) // 输出: false let obj2_1 = Object.freeze(obj) console.log(Reflect.setPrototypeOf(obj2_1, null)) // 输出: true,因为 Object.freeze() 冻结的对象的原型已经是 null,再次设置null返回true console.log(Reflect.setPrototypeOf(obj2_1, {})) // 输出: false
- 对象设置seal
let obj = {}let obj3 = Object.seal({})console.log(Reflect.setPrototypeOf(obj3, null)) // 输出: falselet obj3_1 = Object.seal(obj)console.log(Reflect.setPrototypeOf(obj3_1, null)) // 输出: trueconsole.log(Reflect.setPrototypeOf(obj3_1, {})) // 输出: false
- 对象设置preventExtensions
let obj = {} let obj4 = Object.preventExtensions({}) console.log(Reflect.setPrototypeOf(obj4, null)) // 输出: false let obj4_1 = Object.preventExtensions(obj) console.log(Reflect.setPrototypeOf(obj4_1, null)) // 输出: true console.log(Reflect.setPrototypeOf(obj4_1, {})) // 输出: false
- 如果导致原型链循环,则返回 false
const target = {} const proto = Object.create(target) console.log(Reflect.setPrototypeOf(target, proto)) // 输出: false
参考
NMD Reflect