ES6 -- Reflect 与 Proxy (反射 和 代理)

属性描述符

Property Descriptor 属性描述符,用于描述一个属性的相关信息。

通过Object.getOwnPropertyDescriptor()可以得到一个对象的某个属性的属性描述符。

const obj = {
    a: 1,
    b: 2
}

const desc = Object.getOwnPropertyDescriptor(obj, "a");
console.log(desc); // {value: 1, writable: true, enumerable: true, configurable: true}
  • value: 属性值。
const obj = {
    a: 1,
    b: 2
}

Object.defineProperty(obj, "a", {
    value: 4,
});

obj.a = 10;

const desc = Object.getOwnPropertyDescriptor(obj, "a");
console.log(desc); // {value: 10, writable: true, enumerable: true, configurable: true}
  • configurable: 该属性的描述符是否可以修改。
const obj = {
    a: 1,
    b: 2
}

Object.defineProperty(obj, "a", {
    value: 4,
    configurable: false,
});

obj.a = 10;

// Cannot redefine property: a at Function.defineProperty 
Object.defineProperty(obj, "a", {
    value: 4,
    configurable: true, 
});

const desc = Object.getOwnPropertyDescriptor(obj, "a");
console.log(desc);
  • enumerable: 该属性是否可以被枚举。
const obj = {
    a: 1,
    b: 2
}

Object.defineProperty(obj, "a", {
    value: 4,
    configurable: false,
    enumerable: false
});


// enumerable: true
// for(const prop in obj) {
//     console.log(prop); // a  b
// }


// enumerable: false
for(const prop in obj) {
    console.log(prop); // b
}

const desc = Object.getOwnPropertyDescriptor(obj, "a");
console.log(desc); // {value: 4, writable: true, enumerable: true, configurable: true}
  • writable: 该属性是否可以被重新赋值。
const obj = {
    a: 1,
    b: 2
}

Object.defineProperty(obj, "a", {
    value: 4,
    configurable: false,
    enumerable: false,
    writable: false
});


obj.a = 10;

const desc = Object.getOwnPropertyDescriptor(obj, "a");
console.log(desc); // {value: 4, writable: true, enumerable: true, configurable: true}

Object.getOwnPropertyDescriptors(对象)可以得到某个对象的所有属性描述符。

const obj = {
    a: 1,
    b: 2
}

const desc = Object.getOwnPropertyDescriptors(obj);
console.log(desc); // {a: {…}, b: {…}}

如果需要为某个对象添加属性时 或 修改属性时,配置其属性描述符,可以使用下面的代码:

Object.defineProperty(对象,属性名,描述符);

Object.defineProperties(对象,多个属性的描述符);
const obj = {
    a: 1,
    b: 2,
    c: 3
}

Object.defineProperty(obj, "a", {
    value: "a",
    configurable: false,
    enumerable: false,
    writable: false
})

Object.defineProperties(obj, {
    b: {
        value: 33,
        configurable: false,
        enumerable: false,
        writable: false
    },
    c: {
        value: 55,
        configurable: false,
        enumerable: false,
        writable: false
    },
})

const desc = Object.getOwnPropertyDescriptors(obj);
console.log(desc); // {a: {…}, b: {…}, c: {…}}

存取器属性

属性描述符中,如果配置了 get 和 set 中的任何一个,则该属性,不再是一个普通属性,而变成了存取器属性。

get 和 set 配置均为函数,如果一个属性是存取器属性,则读取该属性时,会运行 get 方法,将 get 方法得到的返回值作为属性值;如果给该属性赋值,则会运行 set 方法。

const obj = {
    a: 1
}

Object.defineProperty(obj, "a", {
    get() {
        console.log("运行了属性a的get函数");
    },
    set() {
        console.log("运行了属性a的set函数");
    }
})

// obj.a = 20; // set(20)
// console.log(obj.a); // console.log(get())

obj.a = obj.a + 1; // set(obj.a + 1)   set(get() + 1)
console.log(obj.a);
const obj = {
    a: 1
}

Object.defineProperty(obj, "a", {
    get() {
        console.log("运行了属性a的get函数");
        return obj._a;
    },
    set(val) {
        console.log("运行了属性a的set函数");
        obj._a = val;
    }
})

obj.a = 20; // set(20)
console.log(obj.a); // console.log(get())

存取器属性最大的意义,在于可以控制属性的读取和赋值。

const obj = {
    name: "qwee"
}

Object.defineProperty(obj, "age", {
    get() {
        console.log("运行了属性a的get函数");
        return obj._age;
    },
    set(val) {
        console.log("运行了属性a的set函数");
        if(typeof val !== "number") {
            throw new TypeError("年龄必须是数字");
        }
        if(val < 0) {
            val = 0;
        }else if(val > 200) {
            val = 200;
        }
        obj._age = val;
    }
})

// obj.age = "aa"; // 报错
obj.age = -100;
console.log(obj.age); // console.log(get())

姓名:

年龄:

Reflect 反射

1. Reflect 是什么?

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

由于它类似于其他语言的反射,因此取名为 Reflect。

  1. 它可以做什么?

使用 Reflect 可以实现诸如属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在于对象中等等功能。

  1. 这些功能不是已经存在了吗?为什么还需要用 Reflect 实现一次?

有一个重要的理念,在 ES5 就被提出:减少魔法,让代码更加纯粹。

这种理念很大程度上是受到 函数式编程的影响。

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

因此,你可以看到 Reflect 对象中有很多的 API 都可以使用过去的某种语法或其他 API 实现。

  1. 它里面到底提供了哪些 API 呢?
  • Reflect.set(target, propertyKey, value): 设置对象 target 的属性 propertyKey 的值为 value,等同于给对象的属性赋值。
const obj = {
    a: 1,
    b: 2
}

// obj.a = 20; // 魔法

Reflect.set(obj, "a", 33);

console.log(obj.a); // 33
  • Reflect.get(target, propertyKey): 读取对象 target 的属性 propertyKey,等同于读取对象的属性值。
const obj = {
    a: 1,
    b: 2
}

// console.log(obj.a); // 魔法

console.log(Reflect.get(obj, "a")); // 1
  • Reflect.apply(target, thisArgument, argumentsList): 调用一个指定的函数,并绑定 this 和参数列表。等同于函数调用。

thisArgument: 绑定的 this。

function test(a, b) {
    console.log(a, b); // 3  4
}

// test(10, 20); // 调用

Reflect.apply(test, null, [3, 4]);
  • Reflect.deleteProperty(target, propertyKey): 删除一个对象属性。
const obj = {
    a: 1,
    b: 2
}

Reflect.deleteProperty(obj, "b");

console.log(obj); // {a: 1}
  • Reflect.defineProperty(target, propertyKey, attributes): 类似于 Object.defineProperty,不同的是如果配置出现问题,返回 false 而不是报错。
const obj = {
    a: 1,
    b: 2
}

Reflect.defineProperty(obj, "a", {
    enumerable: false
})

console.log(Object.getOwnPropertyDescriptors(obj));
  • Reflect.construct(target, argumentsList): 用构造函数的方式创建一个对象。
function test(a, b) {
    this.a = a;
    this.b = b;
}
const t = Reflect.construct(test, [1, 2]);
console.log(t); // test {a: 1, b: 2}
  • Reflect.has(target, propertyKey): 判断一个对象是否拥有一个属性。
const obj = {
    a: 1,
    b: 2
}

// console.log("a" in obj); // true

console.log(Reflect.has(obj, "a")); // true
  • 其他 API: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect

Proxy 代理

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

// 代理一个目标
// target:目标对象
// handler:是一个普通对象,其中可以重写底层实现
// 返回一个代理对象
new Proxy(target, handler);
const obj = {
    a: 1,
    b: 2
}

const proxy = new Proxy(obj, {
    set(target, propertyKey, value) {
        // console.log(target, propertyKey, value); // {a: 1, b: 2} "a" 3

        // 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) {
        // 重写has方法
    }
})

proxy.a = 3;

console.log(obj,proxy); // {a: 3, b: 2}    Proxy {a: 3, b: 2}

console.log(proxy.d); // -1

应用-观察者模式

有一个对象,是观察者,它用于观察另外一个对象的属性值变化,当属性值变化后会收到一个通知,可能会做一些事。

以前的实现方式:缺点有两个对象

代理实现:

应用-偷懒的构造函数

构造函数:

class User {
    constructor(firstName, lastName, age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;

        console.log(this.firstName, this.lastName, this.age); // 12  13  14
    }
}

const a = new User(12, 13, 14);

偷懒的构造函数:

class User {

}

function ConstructorProxy(Class, ...propNames) {
    return new Proxy(Class, {
        construct(target, argumentsList) {
            console.log("构造函数被调用了");
            const obj = Reflect.construct(target, argumentsList);
            propNames.forEach((name, i) => {
                obj[name] = argumentsList[i];
            })
            return obj;
        }
    })
}

const UserProxy = ConstructorProxy(User, "firstName", "lastName", "age");
const a = new UserProxy(12, 13, 14);
console.log(a); // User {firstName: 12, lastName: 13, age: 14}

class Monster {

}


const MonsterProxy = ConstructorProxy(Monster, "sttack", "defence", "hp", "rate", "name");
const b = new MonsterProxy(12, 13, 14, 100, "aaa");

console.log(b); // Monster {sttack: 12, defence: 13, hp: 14, rate: 100, name: "aaa"}

应用-可验证的函数参数

function sum(a, b) {
    return a + b;
}

function validatorFunction(func, ...types) {
    const proxy = new Proxy(func, {
        apply(target, thisArgument, argumentsList) {
            types.forEach((t, i) => {
                const arg = argumentsList[i];
                if(typeof arg !== t) {
                    throw new TypeError(`第${i + 1}参数${argumentsList[i]}不满足类型${t}`);

                }
            })

            Reflect.apply(target, thisArgument, argumentsList);
        }
    })
    return proxy;
}

const sumProxy = validatorFunction(sum, "number", "number");

sumProxy(1, 2);

以前的做法:

function sum(a, b) {
    return a + b;
}

function validatorFunction(func, ...types) {
    return function(...argumentsList) {
        types.forEach((t, i) => {
            const arg = argumentsList[i];
            if(typeof arg !== t) {
                throw new TypeError(`第${i + 1}参数${argumentsList[i]}不满足类型${t}`);

            }
        })
        return func(...argumentsList);
    }
}

const sumProxy = validatorFunction(sum, "number", "number");

sumProxy(1, "2");

你可能感兴趣的:(ES6 -- Reflect 与 Proxy (反射 和 代理))