代理与Reflect反射

属性描述符

Proprety Descriptor 属性描述符 用于描述一个属性的相关信息

1.Object.getOwnPropertyDescriptor(对象,属性名)

可以得到一个对象的 某个属性的属性描述符 

代理与Reflect反射_第1张图片

  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)

代理与Reflect反射_第2张图片

 

 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)

代理与Reflect反射_第3张图片

 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反射_第4张图片

Reflect 

1.reflect是什么?

reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能

由于类似于其他语、言的【 反射】,因此取名为Reflect

2.可以做什么?

可以实现 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在于对象中.etc

3.为什么还需要Reflect实现一次已有功能?

ES5提出重要理念  “减少魔法、让代码更加纯粹”,其很大程度受函数式编程影响

ES6进一步贯彻了该理念,它认为,对属性内存的控制、原型链的修改、函数的调用等,都属于底层实现,属于一种魔法,因此需要将他们提取出来形成一个正常的API ,高聚合到某个对象中,于是,造就了Reflect对象

4.提供了哪些API?

代理与Reflect反射_第5张图片

代理:Proxy

代理:提供了修改底层实现的方式

代理与Reflect反射_第6张图片

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 += `

${prop}:${ob[prop]}

` } div.innerHTML = html } return ob } const target = { a: 1, b: 2 } let obj = observer(target) obj.a = 3 obj.b = 4

 一开始打印一下 ob,其中是不可枚举,不可被遍历的,所以要在属性配置定义一下该属性

代理与Reflect反射_第7张图片

代理与Reflect反射_第8张图片

一开始,页面呈现 a 、b值 

重新赋值后,页面也一起变化

代理与Reflect反射_第9张图片

两个对象内容相等,也造成内存的浪费 

代理与Reflect反射_第10张图片

使用代理实现,不会再浪费一块内存

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 += `

${prop}:${target[prop]}

` } 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)

代理与Reflect反射_第11张图片

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"))

代理与Reflect反射_第12张图片

你可能感兴趣的:(javascript)