【javascript】Reflect学习笔记

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作 的方法,这些方法与 proxy handler 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的,所以不能通过 new 运算符对其进行调用 ,或者Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的(就像 Math 对象)。

静态方法

Reflect.apply(target, thisArgument, argumentsList)

对一个函数进行调用 操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。

静态方法 Reflect.apply() 通过指定的参数列表发起对目标 (target) 函数的调用

javascript 复制代码
console.log(Reflect.apply(Math.floor, undefined, [1.75])) 
// 输出: 1

Reflect.construct(target, argumentsList[, newTarget])

对构造函数进行 new 操作,相当于执行 new target(...args)

javascript 复制代码
function reflectConstructExample() {
  const Person = function (name, age) {
    this.name = name
    this.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())来创建一个对象的。

javascript 复制代码
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) // 输出: true
  console.log(obj instanceof TwoClass) // 输出: false
  // 原型指向 TwoClass.prototype
  const obj1 = Reflect.construct(OneClass, [], TwoClass)
  console.log(obj1) // 输出: TwoClass {name: 'oneClass'}
  console.log(obj1 instanceof OneClass) // 输出: false
  console.log(obj1 instanceof TwoClass) // 输出: true
  //Reflect.construct之前的原型指向 TwoClass.prototype实现方法
  const obj2 = Object.create(TwoClass.prototype) // object.create 创建一个新对象,原型指向 TwoClass.prototype
  OneClass.call(obj2) // "借用" OneClass 的构造函数逻辑来初始化 obj2,对 OneClass 本身及其原型链无影响。
  console.log(obj2) // 输出: TwoClass {name: 'oneClass'}
  console.log(obj2 instanceof OneClass) // 输出: false
  console.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

javascript 复制代码
function reflectDefinePropertyExample() {
  let obj = {}
  Reflect.defineProperty(obj, 'name', {
    value: 'kk'
  })
  console.log(obj.name) // 输出: kk
  console.log(Object.getOwnPropertyDescriptor(obj, 'name'))
  //输出:{value: 'kk', writable: false, enumerable: false, configurable: false}
}

检查属性是否被成功定义:
Object.defineProperty 方法,如果成功则返回一个对象,否则抛出一个 TypeError 。所以,当定义一个属性时,可以使用 try...catch 去捕获其中任何的错误。
Reflect.defineProperty 返回 Boolean 值作为成功的标识,所以使用 if...else 来判断结果。

javascript 复制代码
if (Reflect.defineProperty(target, property, attributes)) {
  // 成功
} else {
  // 失败
}

Reflect.deleteProperty(target, propertyKey)

作为函数的delete操作符,相当于执行 delete target[name]

javascript 复制代码
function reflectDeletePropertyExample() {
	let obj = {
	  name: 'kk',
	  age: 18
	}
	
	console.log(Reflect.deleteProperty(obj, 'name')) // 输出: true
	console.log(obj) // 输出: { age: 18 }
	// 尝试删除被冻结的属性会失败
	Object.freeze(obj)
	console.log(Reflect.deleteProperty(obj, 'age')) // 输出: false
	// 尝试删除不存在的属性会返回 true
	console.log(Reflect.deleteProperty(obj, 'number')) // 输出: true
	var arr = [1, 2, 3, 4, 5]
	Reflect.deleteProperty(arr, '3') // true
	console.log(arr, arr.length) // 输出: [1, 2, 3, empty, 5] 5
}

上面当删除数组元素时,JavaScript 不会自动重新索引数组,而是将该位置标记为 "empty"(稀疏数组),数组长度保持为5,索引3的位置变成空位(empty)。

改进:

javascript 复制代码
// 方案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]

javascript 复制代码
function reflectGetExample() {
  let obj = {
    name: 'kk',
    age: 18
  }
  console.log(Reflect.get(obj, 'name')) // 输出: kk
  console.log(Reflect.get(obj, 'number')) // 输出: undefined
  let arr = [1, 2]
  console.log(Reflect.get(arr, 0)) // 输出: 1
  console.log(Reflect.get(arr, 2)) // 输出: undefined
}

receiver 参数用于指定 getter 函数中的 this 上下文。

当目标属性是访问器属性(getter)时,receiver 会作为 getter 函数的 this 值。如果省略该参数,则默认使用目标对象本身作为 this

javascript 复制代码
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

javascript 复制代码
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')) // 输出: undefined
  console.log(Reflect.getOwnPropertyDescriptor([], 'length'))
  // 输出: {value: 0, writable: true, enumerable: false, configurable: false}
}
  • 如果该方法的第一个参数不是一个对象 (一个原始值),那么将造成 TypeError 错误。

  • Object.getOwnPropertyDescriptor,非对象的第一个参数将被强制转换为一个对象处理。

    javascript 复制代码
    // 尝试获取非对象的属性描述符
    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()

javascript 复制代码
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({}))) // 输出: null
  console.log(Reflect.getPrototypeOf(proto1)) // 输出: null
}

在 ES2015 规范下,Reflect 抛异常,Object 强制转换非 Object 类型

javascript 复制代码
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 运算符 的功能完全相同。

javascript 复制代码
function reflectHasExample() {
  let obj = {
    name: 'kk',
    age: 18
  }
  console.log(Reflect.has(obj, 'name')) // 输出: true
  console.log(Reflect.has(obj, 'number')) // 输出: false
  console.log(Reflect.has([], 'length')) // 输出: true
  // Reflect.has 可以用于检查数组的索引
  console.log(Reflect.has([1, 2, 3], '0')) // 输出: true
  console.log(Reflect.has([], '0')) // 输出: false
  console.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()

javascript 复制代码
function reflectIsExtensibleExample() {
  let obj = {}
  console.log(Reflect.isExtensible(obj)) // 输出: true
  Object.preventExtensions(obj) // 阻止扩展
  console.log(Reflect.isExtensible(obj)) // 输出: false
  let sealedObj = Object.seal({ name: 'kk' })
  console.log(Reflect.isExtensible(sealedObj)) // 输出: false
  let frozenObj = Object.freeze({ age: 18 })
  console.log(Reflect.isExtensible(frozenObj)) // 输出: false
  // Reflect.isExtensible 也可以用于检查数组
  let arr = [1, 2, 3]
  console.log(Reflect.isExtensible(arr)) // 输出: true
  Object.preventExtensions(arr) // 阻止扩展
  console.log(Reflect.isExtensible(arr)) // 输出: false
  // 尝试在不可扩展的字符串上使用 Reflect.isExtensible
  console.log(Reflect.isExtensible('hello'))
  // 输出报错: TypeError: Reflect.isExtensible called on non-object
}

Reflect.ownKeys(target)

返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响)。它的返回值等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

javascript 复制代码
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

javascript 复制代码
function reflectPreventExtensionsExample() {
	let obj = {
	  name: 'kk',
	  age: 18
	}
	console.log(Reflect.preventExtensions(obj)) // 输出: true
	console.log(Reflect.isExtensible(obj)) // 输出: false
}
  • 如果该方法的 target 参数不是一个对象(是原始值),那么将造成一个 TypeError 异常。

  • 对于Object.preventExtensions() 方法,非对象的 target 参数将被强制转换为对象。

    javascript 复制代码
    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

javascript 复制代码
function reflectSetExample() {
  let obj = {
    name: 'kk',
    age: 18
  }
  console.log(Reflect.set(obj, 'name', 'll')) // 输出: true
  console.log(obj.name) // 输出: ll
  console.log(Reflect.set(obj, 'age', 20)) // 输出: true
  console.log(obj.age) // 输出: 20
  console.log(Reflect.set('hello', 0, 'a'))
  // 输出报错: TypeError: Reflect.set called on non-object
}

Reflect.setPrototypeOf(target, prototype)

设置对象原型的函数。返回一个 Boolean ,如果更新成功,则返回 true

javascript 复制代码
function reflectSetPrototypeOfExample() {
	let obj = {}
	console.log(Reflect.setPrototypeOf(obj, null)) // 输出: true
}
  • 对象设置freeze

    javascript 复制代码
    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

    javascript 复制代码
     let obj = {}
     let obj3 = Object.seal({})
     console.log(Reflect.setPrototypeOf(obj3, null)) // 输出: false
     let obj3_1 = Object.seal(obj)
     console.log(Reflect.setPrototypeOf(obj3_1, null)) // 输出: true
     console.log(Reflect.setPrototypeOf(obj3_1, {})) // 输出: false
  • 对象设置preventExtensions

    javascript 复制代码
    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

    javascript 复制代码
    const target = {}
    const proto = Object.create(target)
    console.log(Reflect.setPrototypeOf(target, proto)) // 输出: false

参考

NMD Reflect

相关推荐
淮北4942 小时前
STL学习(十一、常用的算数算法和集合算法)
c++·vscode·学习·算法
_Kayo_5 小时前
VUE2 学习笔记14 nextTick、过渡与动画
javascript·笔记·学习
咔咔一顿操作6 小时前
Vue 3 入门教程7 - 状态管理工具 Pinia
前端·javascript·vue.js·vue3
哪 吒7 小时前
OpenAI放大招:ChatGPT学习模式上线,免费AI智能家教
人工智能·学习·ai·chatgpt·gemini·deepseek
漂流瓶jz7 小时前
JavaScript语法树简介:AST/CST/词法/语法分析/ESTree/生成工具
前端·javascript·编译原理
AI视觉网奇8 小时前
语音识别dolphin 学习笔记
笔记·学习
killer Curry8 小时前
B站 XMCVE Pwn入门课程学习笔记(6)
笔记·学习
徐子竣8 小时前
[学习记录]Unity-Shader-常量缓冲区(CBUFFER)
学习·unity·游戏引擎
自学也学好编程8 小时前
【工具】jsDelivr CDN完全指南:免费高速的开源项目CDN服务
学习·github