ES6(十二)—— Reflect

Reflect

  • 反射,什么是反射机制?
  • Reflect简介
  • 为什么要用Reflect?
  • Reflect API
    • .apply()
    • .construct()
    • .defineProperty()
    • .deleteProperty()
    • .get()
    • .getOwnPropertyDescriptor()
    • .getPrototypeOf()
    • .has()
    • .isExtensible() —— 是否可扩展
    • .ownKeys() —— 判断对象自身属性
    • .preventExtensions() —— 是否可扩展
    • .set() —— 写数据
    • .setPrototypeOf()
  • ES6-ES10学习版图

反射,什么是反射机制?

java的反射机制】是在编译阶段不知道是哪个类被加载,而是在运行的时候才加载、执行。
js中的apply就是反射机制。

Reflect简介

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法,这些方法与处理器对象的方法相同。Reflect不是一个函数对象,因此它是不可构造的。

与大多数全局对象不同,Reflect没有构造函数。你不能将其与一个new运算符一起使用,或者将Reflect对象作为一个函数来调用。Reflect的所有属性和方法都是静态的( 就像Math对象

为什么要用Reflect?

Reflect内部封装了一系列对对象的底层操作,Reflect成员方法就是Proxy处理对象的默认实现

const proxy = new Proxy(obj, {
    get(target, property) {
        // 如果没有定义get方法,那么默认返回的就是Reflect的get方法
        return Reflect.get(target, property)
    }
})

为什么要用Reflect
因为Reflect提供了一套用于操作对象的API,我们之前操作对象可以用Object上面的一些方法,也可以用in、delete这种操作符,使用Reflect就统一了操作方式。

Reflect API

handler ⽅法 默认调⽤ 功能
get Reflect.get() 获取对象身上某个属性的值
set Reflect.set() 在对象上设置属性
has Reflect.has() 判断一个对象是否存在某个属性
deleteProperty Reflect.deleteProperty() 删除对象上的属性
getProperty Reflect.getPrototypeOf() 获取指定对象原型的函数
setProperty Reflect.setPrototypeOf() 设置或改变对象原型的函数
isExtensible Reflect.isExtensible() 判断一个对象是否可扩展 (即是否能够添加新的属性)
preventExtensions Reflect.preventExtensions() 阻止新属性添加到对象
getOwnPropertyDescriptor Reflect.getOwnPropertyDescriptor() 获取给定属性的属性描述符
defineProperty Reflect.defineProperty() 定义或修改一个对象的属性
ownKeys Reflect.ownKeys() 返回由目标对象自身的属性键组成的数组
apply Reflect.apply() 对一个函数进行调用操作,同时可以传入一个数组作为调用参数
construct Reflect.construct() 对构造函数进行 new 操作,实现创建类的实例
.preventExtensions Reflect.preventExtensions() 阻止新属性添加到对象

.apply()

Reflect.apply(target, thisArgument, argumentsList)

target:目标函数,必传

thisArgument:target函数调用时绑定的this对象,不必须

argumentsList:target函数调用时传入的实参列表,该参数应该是一个类数组的对象,不必须

//ES5
console.log(Math.floor.apply(null,[1.72]))
// 1
// 必须先指定方法,再去调用apply。
//Reflect是一个对象,但是不能使用new
// 先传递apply,再指定是哪个方法
// 静态扫描的时候,Math.floor是没有被执行的,运行的时候,是动态的将Math.floor作为参数传进来的。
Reflect.apply(Math.floor,null,[4.72])
// 4

实际应用

// ES5
let price = 101.5
if (price > 100) {
  price = Math.floor.apply(null, [price])
} else {
  price = Math.ceil.apply(null, [price])
}
console.log(price)
// 101

//ES6
let price = 101.5
console.log(Reflect.apply(price > 100 ? Math.floor : Math.ceil, null, [price]))
// 101

.construct()

使用反射的方式去实现创建类的实例,类似于new target(…args).

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

target:被运行的目标函数,必选

argumentsList 调用构造函数的数组或者伪数组,不必选

newTarget 该参数为构造函数, 参考 new.target 操作符,如果没有newTarget参数, 默认和target一样,不必选

let d = new Date()
console.log(d.getTime())
let d1 = Reflect.construct(Date, [])
console.log(d1.getTime())

.defineProperty()

静态方法 Reflect.defineProperty() 基本等同于 Object.defineProperty() 方法

Reflect.defineProperty(target, propertyKey, attributes)

target:目标对象,必填

propertyKey:要定义或修改的属性的名称,不必填

attributes:要定义或修改的属性的描述,不必填

const student = {}
const r = Object.defineProperty(student, 'name', { value:'Mike' })
console.log(student, r)
// {name: "Mike"} {name: "Mike"}
const student = {}
const r = Reflect.defineProperty(student, 'name', { value:'Mike' })
console.log(student, r)
// {name: "Mike"} true

这两个方法效果上来看是一摸一样的,都可以改变一个对象的值,他们的区别在于返回值不同。Object是返回这个值,Reflect是返回true

PS: 在W3C中,以后所有的Object上面的方法,都会慢慢迁移到Reflect对象,可能以后会在Object上面移除这些方法。

.deleteProperty()

Reflect.deleteProperty 允许你删除一个对象上的属性。返回一个 Boolean 值表示该属性是否被成功删除。它几乎与非严格的 delete operator 相同。

Reflect.deleteProperty(target, propertyKey)

target:删除属性的目标对象

propertyKey:将被删除的属性的名称

const obj = { x: 1, y: 2 }
delete obj.x
console.log(obj)
// {y: 2}
const obj = { x: 1, y: 2 }
const d = Reflect.deleteProperty(obj, 'x')
console.log(obj, d)
// {y: 2} true

.get()

Reflect.get() 方法的工作方式,就像从 object (target[propertyKey]) 中获取属性,但它是作为一个函数执行的。

Reflect.get(target, propertyKey[, receiver])

const obj = { x: 1, y: 2 }
console.log(obj.x)
// 1
console.log(obj['x'])
// 1
const obj = { x: 1, y: 2 }
console.log(Reflect.get(obj, 'x'))
// 1
console.log(Reflect.get([3, 4], 1))
// 4

.getOwnPropertyDescriptor()

静态方法 Reflect.getOwnPropertyDescriptor()Object.getOwnPropertyDescriptor() 方法相似。如果在对象中存在,则返回给定的属性的属性描述符,否则返回 undefined

Reflect.getOwnPropertyDescriptor(target, propertyKey)

const obj = { x: 1, y: 2 }
console.log(Object.getOwnPropertyDescriptor(obj, 'x'))
// {value: 1, writable: true, enumerable: true, configurable: true}
console.log(Reflect.getOwnPropertyDescriptor(obj, 'x'))
// {value: 1, writable: true, enumerable: true, configurable: true}
console.log(Reflect.getOwnPropertyDescriptor({ x: 'hello' }, 'y'))
// undefined
console.log(Reflect.getOwnPropertyDescriptor([], 'length'))
// {value: 0, writable: true, enumerable: false, configurable: false}

对比

如果该方法的第一个参数不是一个对象(一个原始值),那么将造成 TypeError 错误。而对于Object.getOwnPropertyDescriptor,非对象的第一个参数将被强制转换为一个对象处理。

Reflect.getOwnPropertyDescriptor("foo", 0);
// TypeError: "foo" is not non-null object

Object.getOwnPropertyDescriptor("foo", 0);
// { value: "f", writable: false, enumerable: true, configurable: false }

.getPrototypeOf()

静态方法 Reflect.getPrototypeOf()Object.getPrototypeOf() 方法是一样的。都是返回指定对象的原型(即,内部的 [[Prototype]] 属性的值)。

Reflect.getPrototypeOf(target)

const d = New Date()
console.log(Reflect.getPrototypeOf(d))
// {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}
console.log(Object.getPrototypeOf(d))
// {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}

.has()

判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。

Reflect.has(target, propertyKey)

const obj = { x: 1, y: 2 }
console.log(Reflect.has(obj, 'x'))
// true
console.log(Reflect.has(obj, 'z'))
// false

.isExtensible() —— 是否可扩展

Reflect.isExtensible 判断一个对象是否可扩展 (即是否能够添加新的属性),它与 Object.isExtensible() 方法一样。

Reflect.isExtensible(target)

const obj = { x: 1, y: 2 }
console.log(Reflect.isExtensible(obj))
// true
Object.freeze(obj)
obj.z = 3
console.log(Reflect.isExtensible(obj))
// false
console.log(obj)
// {x: 1, y: 2}

.ownKeys() —— 判断对象自身属性

Reflect.ownKeys 方法返回一个由目标对象自身的属性键组成的数组。它的返回值等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

Reflect.ownKeys(target)

const obj = { x: 1, y: 2 }
console.log(Reflect.ownKeys(obj))
// ["x", "y"]
console.log(Reflect.ownKeys([]))
// ["length"]
console.log(Reflect.ownKeys([1,2]))
// ["0", "1", "length"]

.preventExtensions() —— 是否可扩展

等同于Object.freeze()

Reflect.preventExtensions 方法阻止新属性添加到对象 例如:防止将来对对象的扩展被添加到对象中)。该方法与 Object.preventExtensions() 方法一致

Reflect.preventExtensions(target)

const obj = { x: 1, y: 2 }
console.log(Reflect.isExtensible(obj))
// true
Reflect.preventExtensions(obj)
obj.z = 3
console.log(Reflect.isExtensible(obj))
// false
console.log(obj)
// {x: 1, y: 2}

.set() —— 写数据

Reflect.set 方法允许你在对象上设置属性。它的作用是给属性赋值并且就像 property accessor 语法一样,但是它是以函数的方式。

Reflect.set(target, propertyKey, value[, receiver])

const obj = { x: 1, y: 2 }
Reflect.set(obj, 'z', 4)
console.log(obj)
// {x: 1, y: 2, z: 4}
const arr = ['apple', 'pear']
Reflect.set(arr, 1, 'banana')
console.log(arr)
// ["apple", "banana"]

.setPrototypeOf()

Reflect.setPrototypeOf 方法改变指定对象的原型 (即,内部的 [[Prototype]] 属性值)

Reflect.setPrototypeOf(target, prototype)

const arr = ['apple', 'pear']
console.log(Reflect.getPrototypeOf(arr))
// [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ,…]
Reflect.setPrototypeOf(arr, String.prototype)
console.log(Reflect.getPrototypeOf(arr))
// String {"", constructor: ƒ, anchor: ƒ, big: ƒ, blink: ƒ, …}

ES6-ES10学习版图

image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353