属性描述符
Proprety Descriptor 属性描述符 用于描述一个属性的相关信息
1.Object.getOwnPropertyDescriptor(对象,属性名)
可以得到一个对象的 某个属性的属性描述符
Object.getOwnPropertyDescriptors(对象)
可以得到某个对象的所有属性描述符
如果需要为某个对象添加属性或修改属性时,配置其属性描述符,可以使用
Object.definePropertie(对象、属性、描述符 )
Object.definePropertie(obj, 'a', { configurable: false })
存取器属性
- 属性描述符中,如果配置了get set中任何一个,则该属性不再是一个普通属性,而变成存储器属性
- get、set配置均为函数,如果一个属性是存储器属性,则读取该属性时,会运行get方法,将get方法得到的返回值作为属性值
- 如果给该属性赋值,则会运行set方法
- 存取器属性最大的意义,在于可以控制属性的读取和赋值
const obj = {b: 1
}
Object.defineProperty(obj, 'a', {get () {console.log("运行了属性的get函数")}, set (val) {console.log("运行了属性的set函数", val)}
})
obj.a = obj.a + 1
//set(obj.a+1) ==>set(get()+1)
//1.需要先读取a值,但get()没有返回值 因此是undefined
//2.undefined+1=NaN
console.log(obj.a)
1.使用其他属性值赋值。也可新增属性 _a
const obj = {b: 1
}
Object.defineProperty(obj, 'a', {get () {console.log("运行了属性的get函数")return obj._a //使用其他属性值}, set (val) {console.log("运行了属性的set函数", val)obj._a = val}
})
obj.a = 10
console.log(obj.a)
2.也可对赋值做自定义规范
Object.defineProperty(obj, 'a', {get () {console.log("运行了属性的get函数")return obj._a}, set (val) {if (typeof val !== 'number') {throw new TypeError('必须是一个数字')}if (val > 200) {val = 200}console.log("运行了属性的set函数", val)obj._a = val}
})
obj.a = 10000
Reflect
1.reflect是什么?
reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能
由于类似于其他语、言的【 反射】,因此取名为Reflect
2.可以做什么?
可以实现 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在于对象中.etc
3.为什么还需要Reflect实现一次已有功能?
ES5提出重要理念 “减少魔法、让代码更加纯粹”,其很大程度受函数式编程影响
ES6进一步贯彻了该理念,它认为,对属性内存的控制、原型链的修改、函数的调用等,都属于底层实现,属于一种魔法,因此需要将他们提取出来形成一个正常的API ,高聚合到某个对象中,于是,造就了Reflect对象
4.提供了哪些API?
代理:Proxy
代理:提供了修改底层实现的方式
new Proxy(target,handler)
- 代理一个目标
- target:目标对象
- handler:是一个普通对象,其中可以重写底层实现
- 返回一个代理对象
const obj = {a: 1,b: 2
}
const proxy = new Proxy(obj, {set (target, propertyKey, value) {Reflect.set(target, propertyKey, value)},get (target, propertyKey) {if (Reflect.has(target, propertyKey)) {return Reflect.get(target, propertyKey)} else {return -1}},has (target, propertyKey) {return false}
})
proxy.a = 10
console.log(proxy.a)
console.log(proxy.d)
proxy应用——观察者模式
有一个对象,是观察者,他用于观察另外一个对象的属性值变化,当属性值变化后会收到一个通知,可能会做一些事
以下方式不太好
function observer (target) {const ob = {}const div = document.getElementById('container')const props = Object.keys(target)for (const prop of props) {Object.defineProperty(ob, prop, {// 读属性值时,把目标值给你get () {return target[prop]},// 赋值时,给目标对象赋值,再渲染一次set (val) {target[prop] = valrender()},enumerable: true})}// console.log(Object.getOwnPropertyDescriptors(ob))render()function render () {let html = ''for (const prop of Object.keys(ob)) {html += `<p><span>${prop}:</span><span>${ob[prop]}<span/></p>`}div.innerHTML = html}return ob
}
const target = {a: 1, b: 2
}
let obj = observer(target)
obj.a = 3
obj.b = 4
一开始打印一下 ob,其中是不可枚举,不可被遍历的,所以要在属性配置定义一下该属性
一开始,页面呈现 a 、b值
重新赋值后,页面也一起变化
两个对象内容相等,也造成内存的浪费
使用代理实现,不会再浪费一块内存
function observer (target) {const div = document.getElementById('container')const proxy = new Proxy(target, {set (target, prop, value) {Reflect.set(target, prop, value)},get () {return Reflect.get(target, prop, value)}})render()function render () {let html = ''for (const prop of Object.keys(target)) {html += `<p><span>${prop}:</span><span>${target[prop]}<span/></p>`}div.innerHTML = html}return proxy
}
const target = {a: 1, b: 2
}
let obj = observer(target)
proxy应用——构造函数
1.原本构造函数
class User {constructor(firstName,lastName,age) {this.firstName = firstNamethis.lastName = lastNamethis.age = age}
}
2.使用代理方式偷懒 可以这么写
class User { }
function ConstructorProxy (Class, ...propNames) {// 重写类的底层实现return new Proxy(Class, {construct (target, argumentsList) {const obj = Reflect.construct(target, argumentsList)// 给构造函数对象加上这些属性propNames.forEach((name, i) => {obj[name] = argumentsList[i]})console.log('构造函数被调用了')return obj},})
}
const UserProxy = ConstructorProxy(User,'firstName','lastName','age'
)
// 通过代理告诉 构造函数有三个属性
const obj = new UserProxy('张', '三', 17)
console.log('obj1:', obj)
3.再加一个类也一样
class Monster { }
const monstorProxy = ConstructorProxy(Monster,'attack','defence','hp','rate','name'
)
const obj2 = new monstorProxy(10, 100, 3, 4, 'monster')
console.log('obj2:', obj2)
proxy应用——可验证的函数参数
function sum (a, b) {return a + b
}
function validatorFunction (func, ...types) {const proxy = new Proxy(func, {apply (target, thisArgument, argumentList) {types.forEach((t, i) => {console.log(t, i)const arg = argumentList[i]console.log(arg)if (typeof arg !== t) {throw new TypeError(`第${i + 1}个参数${argumentList[i]}不满足参数类型`)}})Reflect.apply(target, thisArgument, argumentList)}})return proxy
}
const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, "2"))