属性描述符
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] = val
render()
},
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 = firstName
this.lastName = lastName
this.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"))