学习小技巧,分类整理Object,Proxy,Reflect

81 阅读11分钟

我正在参与掘金创作者训练营第6期,点击了解活动详情

前言

有时候对于Object,Proxy,Reflect,可能小脑袋是空空如也的,想着每次遇到问题了再去查文档,但是脑袋里面都不知道某种方法是干什么的,遇到某个bug,或者写到紧急时刻需要某种方法来解决某些问题。临时去找的话太急了。小脑袋都是嗡嗡的。所以按照每种的方法写成表格的形式。视觉效果应该好些,快速查找。

Object

Object 构造函数

Object 构造函数 (1)作用
Object()创建一个新的 Object 对象。该对象将会包裹(wrapper)传入的参数

Object 静态方法

Object 静态方法 (19)作用
assign()通过复制一个或多个对象来创建一个新的对象。
create()使用指定的原型对象和属性创建一个新对象。
defineProperty()给对象添加一个属性并指定该属性的配置。
defineProperties()给对象添加多个属性并分别指定它们的配置
entries()返回给定对象自身可枚举属性的 [key, value] 数组
freeze()冻结对象:其他代码不能删除或更改任何属性
getOwnPropertyDescriptor())返回对象指定的属性配置。
getOwnPropertyNames()返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名
getOwnPropertySymbols()通返回一个数组,它包含了指定对象自身所有的符号属性。
getPrototypeOf()返回指定对象的原型对象
is()比较两个值是否相同。所有 NaN 值都相等(这与==和===不同)
isExtensible()判断对象是否可扩展。
isFrozen()判断对象是否已经冻结。
isSealed()判断对象是否已经密封
keys()返回一个包含所有给定对象自身可枚举属性名称的数组
preventExtensions()阻止对象的任何扩展。
seal()防止其他代码删除对象的属性。
setPrototypeOf()设置对象的原型(即内部 [[Prototype]] 属性)
values()返回给定对象自身可枚举值的数组

Object 实例属性

Object 实例属性 (2)作用
Object.prototype.constructor一个引用值,指向 Object 构造函数。
Object.prototype.__proto__指向一个对象,当一个 object 实例化时,使用该对象作为实例化对象的原型

Object 实例方法

Object 实例方法 (10)作用
prototype.__defineGetter__()将一个属性与一个函数相关联,当该属性被访问时,执行该函数,并且返回函数的返回值
prototype.__defineSetter__()将一个属性与一个函数相关联,当该属性被设置时,执行该函数,执行该函数去修改某个属性
prototype.__lookupGetter__()返回一个函数,该函数通过给定属性的 Object.prototype.__defineGetter__() 得出
prototype.__lookupSetter__()返回一个函数,该函数通过给定属性的 Object.prototype.__defineSetter__() 得出
prototype.hasOwnProperty()返回一个布尔值,用于表示一个对象自身是否包含指定的属性,该方法并不会查找原型链上继承来的属性
prototype.isPrototypeOf()返回一个布尔值,用于表示该方法所调用的对象是否在指定对象的原型链中
prototype.propertyIsEnumerable()返回一个布尔值,用于表示内部属性 ECMAScript [Enumerable] 是否被设置。
prototype.toLocaleString()调用 toString()。
prototype.toString()返回一个代表该对象的字符串
prototype.valueOf()返回指定对象的原始值。

Proxy

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等

// target,目标对象,(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
// handler, 通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 `p` 的行为。
const p = new Proxy(target, handler)

关于proxy的表格

proxy方法 (1)作用
Proxy.revocable()创建一个可撤销的Proxy对象,一旦某个代理对象被撤销,它将变得几乎完全不可用,在它身上执行任何的可代理操作都会抛出 TypeError异常
    const revocable = Proxy.revocable({}, {
      get(target, name) {
        return name;
      }
     });
     // 返回一个包含了代理对象本身和它的撤销方法
    console.log(revocable) // {proxy: Proxy, revoke: ƒ}
    const  proxy = revocable.proxy;
    proxy.foo;              // "[[foo]]"
    revocable.revoke();

    console.log(proxy.foo); // 抛出 TypeError
    proxy.foo = 1           // 还是 TypeError
    delete proxy.foo;       // 又是 TypeError
    typeof proxy            // "object",因为 typeof 不属于可代理操作

handler对象的方法 (12)作用
handler.getPrototypeOf()Object.getPrototypeOf 方法的捕捉器
handler.setPrototypeOf()Object.setPrototypeOf 方法的捕捉器
handler.isExtensible()Object.isExtensible 方法的捕捉器
handler.preventExtensions()Object.preventExtensions 方法的捕捉器
handler.getOwnPropertyDescriptor()Object.getOwnPropertyDescriptor 方法的捕捉器
handler.defineProperty()Object.defineProperty 方法的捕捉器
handler.has()in操作符的捕捉器
handler.get()属性读取操作的捕捉器
handler.set()属性设置操作的捕捉器
handler.deleteProperty()delete操作符的捕捉器
handler.ownKeys()Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器
handler.apply()new 操作符的捕捉器。。
 // 捕捉器:当Object上某个方法被触发时,被处于Proxy上的形参handler对象里面的相对应的进行监听的方法
    const monster = {
      name: "牧羊"
    };
    const monsterPrototype = {
      name: "沐浴阳光"
    };

    const handler = {
      getPrototypeOf(target) {
        // `getPrototypeOf` 方法的返回值必须是一个对象或者 `null`,否则会抛出错误
        return monsterPrototype;
      }
    };
    const proxy = new Proxy(monster, handler);
    //Object.getPrototypeOf 触发时被 handler.getPrototypeOf()进行捕获从而操作handler.getPrototypeOf里面的逻辑
    console.log(Object.getPrototypeOf(proxy) === monsterPrototype);
    // true
    console.log(Object.getPrototypeOf(proxy.name);
    //"沐浴阳光"

5 种触发 getPrototypeOf 代理方法的方式

   //  Object.getPrototypeOf
   //  Reflect.getPrototypeOf
   //  __proto__
   // Array.prototype.isPrototypeOf
   // instanceof
    const obj = {};
    const handler = { 
      getPrototypeOf(target) { 
        return Array.prototype; 
      } 
    };
    var p = new Proxy(obj, handler);
    console.log(
        Object.getPrototypeOf(p) === Array.prototype,  // true
        Reflect.getPrototypeOf(p) === Array.prototype, // true
        p.__proto__ === Array.prototype,               // true
        Array.prototype.isPrototypeOf(p),              // true
        p instanceof Array                             // true
    );

in操作符

如果指定的属性在指定的对象或其原型链中,则in 运算符返回true,否则返回false

    // 对象
    const obj = { option: 'vue', number: 2 };

    console.log('option' in obj);//  true
    
    //使用 `delete` 运算符删除了一个属性,则 `in` 运算符对所删除属性返回 `false`
    delete obj.option;
    if ('option' in car === false) {
      obj.option = 'react';
    }
    console.log(obj.option); // react
    
    // 如果只是将一个属性的值赋值为`undefined`,而没有删除它,则 `in` 运算仍然会返回`true`,因为修改的是属性值,但是属性,比如说number还在的
    obj.number = undefined
    console.log('number' in obj); // true
    // 数组
    var fruits = new Array("apple", "banana", "grape", "tangerine", "watermelon");
    0 in fruits        // 返回 true
    3 in fruits        // 返回 true
    6 in fruits        // 返回 false
    "apple" in fruits    // 返回 false (必须使用索引号,而不是数组元素的值)
    "length" in fruits // 返回 true (length 是一个数组属性)
    
    Symbol.iterator in fruits // 返回 true (数组可迭代,只在 ES2015+ 上有效)
    // 内置对象
    "PI" in Math          // 返回 true
    
   fruits[3] = undefined;
   3 in trees;  // true

new 操作符

语法: new constructor[([arguments])]

news属性作用
constructor用来指定对象实例是什么类型的类或函数 (构造函数)
arguments用来被 constructor 调用的参数列表

new 关键字会进行如下的操作:

  1. 创建一个空对象(即{});
  2. 添加属性__proto__到步骤 1 新创建的对象上,将该属性链接到构造函数的原型对象上;
  3. 将步骤 1 新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this
   // 编写函数来定义对象类型,构造函数Car
    function Car(name, height, birthday) {
      this.name = name;
      this.height = height;
      this.birthday = birthday;
    }
    // 通过 `new` 来创建对象实例
    const car1 = new Car('北京现代', 2 , 1993); // new Car()操作后的结果 作为`this`的上下文
    const car2 = new Car();
    
    console.log(car1.name); // "北京现代"
    
    // 可以使用`prototype` 属性将需要多次使用的属性添加到以前定义的对象类型上。定义一个共享属性
    console.log(car1.color); // undefined
    Car.prototype.color = "red";
    
    car1.color = 'blue';
    console.log(car1.color) // blue
    console.log(car1.__proto__.color) //red
    console.log(car2.__proto__.color) //red
    console.log(car1.color)  // blue
    console.log(car2.color) //red

Reflect

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。

与大多数全局对象不同,Reflect不是构造函数,所以不能通过new运算符来进行调用,也不能将Reflect对象作为一个函数来调用。Reflect的所有属性和方法(就像Math对象)都是静态的。

Reflect静态方法 (14)作用
Reflect.apply()对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似)
Reflect.construct()对构造函数进行 new 操作,相当于执行 new target(...args)
Reflect.defineProperty()和 Object.defineProperty() 类似。如果设置成功就会返回 true
Reflect.deleteProperty()作为函数的delete操作符,相当于执行 delete target[name]
Reflect.get()获取对象身上某个属性的值,类似于 target[name]
Reflect.getOwnPropertyDescriptor()获取对象身上某个属性的值,类似于 target[name]
Reflect.getOwnPropertyDescriptor()类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined
Reflect.getPrototypeOf()类似于 Object.getPrototypeOf()
Reflect.has()判断一个对象是否存在某个属性,和 in 运算符的功能完全相同。
Reflect.isExtensible()类似于 Object.isExtensible()
Reflect.ownKeys()返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响).
Reflect.preventExtensions()类似于 Object.preventExtensions()。返回一个Boolean
Reflect.set()将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true
Reflect.setPrototypeOf()设置对象原型的函数。返回一个 Boolean, 如果更新成功,则返回true

Reflect部分方法用法

  1. Reflect.apply()

语法: Reflect.apply(target, thisArgument, argumentsList)

该方法与 ES5 中Function.prototype.apply()方法类似:调用一个方法并且显式地指定 this 变量和参数列表 (arguments) ,参数列表可以是数组,或类似数组的对象

Function.prototype.apply.call(Math.floor, undefined, [1.75]);

使用 Reflect.apply 方法会使代码更加简洁易懂

//Reflect.apply('目标函数','this指向','传入的实参列表')
Reflect.apply(Math.floor, undefined, [2.9]);   // 2
Reflect.apply(String.fromCharCode, undefined, [108, 101, 109, 111,110 ]); // "lemon"
Reflect.apply(RegExp.prototype.exec, /fa/, ["confabulation"]).index; // 3
Reflect.apply("".charAt, "ponies", [4]);  // "e"
  1. Reflect.construct()

语法: Reflect.construct(target, argumentsList[, newTarget])

Reflect.construct允许使用可变的参数来调用构造函数,这和使用new 操作符搭配对象展开符调用一样

    function OneClass() {
        this.name = '今晚不要下雨';
    }
    function OtherClass() {
        this.name = '天晴了';
    }

    // 创建一个对象:
    var obj1 = Reflect.construct(OneClass, [], OtherClass);
    // 与上述方法等效:
    var obj2 = Object.create(OtherClass.prototype);
    OneClass.apply(obj2, args);

    console.log(obj1.name); // '今晚不要下雨'
    console.log(obj2.name); // '今晚不要下雨'

    console.log(obj1 instanceof OneClass); // false
    console.log(obj2 instanceof OneClass); // false

    console.log(obj1 instanceof OtherClass); // true
    console.log(obj2 instanceof OtherClass); // true

  1. Reflect.get()

语法: Reflect.get(target, propertyKey[, receiver])

    // Reflect.get(取值的目标对象;获取的值的键值;取值的目标对象中如果指定了`getter`,那么则为`getter`调用时的`this`值)
    // Object
    var obj = { x: 1, y: 2 };
    Reflect.get(obj, "x"); // 1

    // Array
    Reflect.get(["索引0", "索引1"], 1); // "索引1"

    // Proxy.handler和Reflectget.get结合
    var x = {p: 1 , l: 2};
    const handler = { 
      get(t, k, r) { 
      // t传进来的对象x
      // k, Reflect.get的第二个参数"foo"
      // r, Proxy对象
         return k + "下雨吗"; 
       }
    };
    var obj = new Proxy(x, handler);
    Reflect.get(obj, "今天"); // "今天下雨吗"
  1. Reflect.set()

语法: Reflect.set(target, propertyKey[, receiver])

静态方法 Reflect.set()  工作方式就像在一个对象上设置一个属性,返回布尔值表明设置属性是否成功。

    // Object
    var obj = {};
    Reflect.set(obj, "prop", "value"); // true
    obj.prop; // "value"

    // Array
    var arr = ["duck1", "duck2", "duck3"];
    arr[2]; //  "duck3"
    Reflect.set(arr, 2, "goose"); // true
    arr[2]; // "goose"

    // 截断数组.
    Reflect.set(arr, "length", 1); // true
    arr; // ["duck1"];
    Reflect.set(arr, "length", 3); // true
    arr //["duck1", , ];

    //只有一个参数时, 值是 "undefined".
    var obj = {};
    Reflect.set(obj); // true
    Reflect.getOwnPropertyDescriptor(obj, "undefined");
    // { value: undefined, writable: true, enumerable: true, configurable: true }
  1. Reflect.ownKeys()

语法: Reflect.ownKeys(target)

静态方法 Reflect.set()  返回一个由目标对象自身的属性键组成的数组。target 必须是Object,不然会抛出错误TypeError

    Reflect.ownKeys({z: 3, y: 2, x: 1}); // [ "z", "y", "x" ]
    Reflect.ownKeys([]); // ["length"]
  1. Reflect.deleteProperty()

语法: Reflect.deleteProperty(target,propertyKey)

静态方法 Reflect.deleteProperty()  返回一个布尔值表明该属性是否被成功删除。target 必须是Object,不然会抛出错误TypeError

    var obj = { x: 1, y: 2 };
    //Reflect.deleteProperty(目标对象, 被删除的键)
    Reflect.deleteProperty(obj, "x"); // true
    obj; // { y: 2 }

    var arr = [1, 2, 3, 4, 5];
    // 删除数组索引为3的项,数组的占位还留着的
    Reflect.deleteProperty(arr, "3"); // true
    arr; // [1, 2, 3, , 5]
    // 虽然被删除了,但是位置还保留着的,索引长度不变
    arr.length // 5

    // 如果属性不存在,返回 true
    Reflect.deleteProperty({}, "foo"); // true

    // 如果属性不可配置,被冻结后,删除不了,返回 false
    Reflect.deleteProperty(Object.freeze({foo: 1}), "foo"); // false

  1. Reflect.has()

语法: Reflect.has(target,propertyKey)

静态方法 Reflect.has()  和in操作符类似,检查目标对象是否存该属性,存在返回true,不存在返回fasle

    Reflect.has({x: 0}, "x"); // true
    Reflect.has({x: 0}, "y"); // false

    // 如果该属性存在于原型链中,返回 true
    Reflect.has({x: 0}, "toString");

    // Proxy 对象的 .has() 方法
    obj = new Proxy({}, {
      has(t, k) { 
        return k.startsWith("door"); 
      }
    });
    // Reflect.has方法被Proxy代理的has监听触发了
    Reflect.has(obj, "doorbell"); // true
    Reflect.has(obj, "dormitory"); // false

  1. Reflect.getPrototypeOf()

语法: Reflect.getPrototypeOf(target)

静态方法 Reflect.getPrototypeOf()  获取原型的目标对象,如果给定对象没有继承的属性,则返回 null。target必须是Object,不然会抛出错误TypeError

   Reflect.getPrototypeOf({}); // Object.prototype
   Reflect.getPrototypeOf(Object.prototype); // null
   Reflect.getPrototypeOf(Object.create(null)); // null

与 Object.getPrototypeOf() 比较

    // 如果参数为 Object,返回结果相同
   Object.getPrototypeOf({})   // Object.prototype
   Reflect.getPrototypeOf({})  // Object.prototype

   // 在 ES5 规范下,对于非 Object,抛异常
   Object.getPrototypeOf('foo')   // Throws TypeError
   Reflect.getPrototypeOf('foo')  // Throws TypeError

   // 在 ES2015 规范下,Reflect 抛异常,Object 强制转换非 Object
   Object.getPrototypeOf('foo')   // String.prototype
   Reflect.getPrototypeOf('foo')  // Throws TypeError

   // 如果想要模拟 Object 在 ES2015 规范下的表现,需要强制类型转换
   Reflect.getPrototypeOf(Object('foo'))  // String.prototype

总结

不积跬步,无以至千里。ObjectProxyReflect 在源码中出现的次数很多。先把这些知识点看懂记牢,相信之后看源码的话会轻松很多。掌握的更快!!!让我们一起快乐的学习