Skip to content
On this page

代理与反射

属性描述符

Property Descriptor

Property Descriptor 属性描述符 是一个普通对象,用于描述一个属性的相关信息 通过Object.getOwnPropertyDescriptor(对象, 属性名)可以得到一个对象的某个属性的属性描述符

javascript
const desc = Object.getOwnPropertyDescriptor(obj, "a")
console.log(desc);
const desc = Object.getOwnPropertyDescriptor(obj, "a")
console.log(desc);
  • value:属性值
  • configurable:该属性的描述符是否可以修改
  • enumerable:该属性是否可以被枚举
  • writable:该属性是否可以被重新赋值

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

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

javascript
const obj = {
  a: 1,
  b: 2
}
Object.defineProperty(obj, "a", {
  value: 3,
  configurable: false,//描述符不可以修改
  enumerable: false,//不可枚举
  writable: false//不可写
})
console.log(obj)
for (const prop in obj) {
  console.log(prop);//只有b,没有a了
}
obj.a = 10;//不可写
console.log(obj);
const props = Object.keys(obj)//得到对象的所有属性
console.log(props)
const values = Object.values(obj);//得到对象的所有值
console.log(values);
const obj = {
  a: 1,
  b: 2
}
Object.defineProperty(obj, "a", {
  value: 3,
  configurable: false,//描述符不可以修改
  enumerable: false,//不可枚举
  writable: false//不可写
})
console.log(obj)
for (const prop in obj) {
  console.log(prop);//只有b,没有a了
}
obj.a = 10;//不可写
console.log(obj);
const props = Object.keys(obj)//得到对象的所有属性
console.log(props)
const values = Object.values(obj);//得到对象的所有值
console.log(values);

Object.defineProperties(对象, 多个属性的描述符)

javascript
Object.defineProperties(obj, {
  a: {
    value: 3,
    configurable: false,
    enumerable: false,
    writable: false
  }
})
Object.defineProperties(obj, {
  a: {
    value: 3,
    configurable: false,
    enumerable: false,
    writable: false
  }
})

存取器属性

属性描述符中,如果配置了 get 和 set 中的任何一个,则该属性,不再是一个普通属性,而变成了存取器属性。 get 和 set配置均为函数,如果一个属性是存取器属性,则读取该属性时,会运行get方法,将get方法得到的返回值作为属性值;如果给该属性赋值,则会运行set方法。 属性已经不再内存里面了,而是get和set

javascript
const obj = {
  b: 2
}
Object.defineProperty(obj, "a", {
  get() {
    console.log("运行了属性a的get函数")
  },
  set(val) {
    console.log("运行了属性a的set函数", val)
  }
})
// obj.a的时候运行get函数
// obj.a赋值的时候运行set函数
// 所以console.log(obj.a)//undefined,因为get函数没有返回结果
obj.a = 1;//相当于set(20)
console.log(obj.a)// 相当于console.log(get())
// 运行set
// 运行get
// undefined
const obj = {
  b: 2
}
Object.defineProperty(obj, "a", {
  get() {
    console.log("运行了属性a的get函数")
  },
  set(val) {
    console.log("运行了属性a的set函数", val)
  }
})
// obj.a的时候运行get函数
// obj.a赋值的时候运行set函数
// 所以console.log(obj.a)//undefined,因为get函数没有返回结果
obj.a = 1;//相当于set(20)
console.log(obj.a)// 相当于console.log(get())
// 运行set
// 运行get
// undefined
javascript
// 演示1
obj.a = 20 + 10; // set(20 + 10) 相当于运行set()
console.log(obj.a); // console.log(get()) 读取属性a,相当于运行get()
// 演示2
obj.a = obj.a + 1; // set(obj.a + 1) 相当于读取 set(get() + 1)  undefined+1=NaN
console.log(obj.a);//读取属性一定运行get  undefined
// 演示1
obj.a = 20 + 10; // set(20 + 10) 相当于运行set()
console.log(obj.a); // console.log(get()) 读取属性a,相当于运行get()
// 演示2
obj.a = obj.a + 1; // set(obj.a + 1) 相当于读取 set(get() + 1)  undefined+1=NaN
console.log(obj.a);//读取属性一定运行get  undefined
javascript
const obj = {
  b: 2
}
Object.defineProperty(obj, "a", {
  get() {
    console.log("运行了属性a的get函数")
    return obj._a;
    // 错误的思路:return obj.a
    // 这里就成了无限递归,无限读取,导致浏览器卡死,同理set
  },
  set(val) {
    console.log("运行了属性a的set函数", val)
    obj._a = val;
  }
})
obj.a = 10;
console.log(obj.a);
const obj = {
  b: 2
}
Object.defineProperty(obj, "a", {
  get() {
    console.log("运行了属性a的get函数")
    return obj._a;
    // 错误的思路:return obj.a
    // 这里就成了无限递归,无限读取,导致浏览器卡死,同理set
  },
  set(val) {
    console.log("运行了属性a的set函数", val)
    obj._a = val;
  }
})
obj.a = 10;
console.log(obj.a);

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

javascript
obj = {
  name: "adsf"
}
Object.defineProperty(obj, "age", {
  get() {
    return obj._age;
  },
  set(val) {
    if (typeof val !== "number") {
      throw new TypeError("年龄必须是一个数字")
    }
    if (val < 0) {
      val = 0;
    } else if (val > 200) {
      val = 200;
    }
    obj._age = val;
  }
})
obj.age = "Asdfasasdf";
console.log(obj.age);
obj = {
  name: "adsf"
}
Object.defineProperty(obj, "age", {
  get() {
    return obj._age;
  },
  set(val) {
    if (typeof val !== "number") {
      throw new TypeError("年龄必须是一个数字")
    }
    if (val < 0) {
      val = 0;
    } else if (val > 200) {
      val = 200;
    }
    obj._age = val;
  }
})
obj.age = "Asdfasasdf";
console.log(obj.age);

应用:将对象的属性和元素关联

html
<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>

  <body>
    <p>
      <span>姓名:</span>
      <span id="name"></span>
    </p>
    <p>
      <span>年龄:</span>
      <span id="age"></span>
    </p>
    <script>
      const spanName = document.getElementById("name")
      const spanAge = document.getElementById("age")
      const user = {}
      Object.defineProperties(user, {
        name: {
          get() {
            return spanName.innerText;
            //获取页面窗口中对应的元素内容
          },
          set(val) {
            spanName.innerText = val;
          }
        },
        age: {
          get() {
            return +spanAge.innerText;
          },
          set(val) {
            if (typeof val !== "number") {
              throw new TypeError("年龄必须是一个数字")
            }
            if (val < 0) {
              val = 0;
            } else if (val > 200) {
              val = 200;
            }
            spanAge.innerText = val;
          }
        }
      })
    </script>
  </body>
</html>

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>

  <body>
    <p>
      <span>姓名:</span>
      <span id="name"></span>
    </p>
    <p>
      <span>年龄:</span>
      <span id="age"></span>
    </p>
    <script>
      const spanName = document.getElementById("name")
      const spanAge = document.getElementById("age")
      const user = {}
      Object.defineProperties(user, {
        name: {
          get() {
            return spanName.innerText;
            //获取页面窗口中对应的元素内容
          },
          set(val) {
            spanName.innerText = val;
          }
        },
        age: {
          get() {
            return +spanAge.innerText;
          },
          set(val) {
            if (typeof val !== "number") {
              throw new TypeError("年龄必须是一个数字")
            }
            if (val < 0) {
              val = 0;
            } else if (val > 200) {
              val = 200;
            }
            spanAge.innerText = val;
          }
        }
      })
    </script>
  </body>
</html>

属性描述符应用

js
var obj = {
  b: 2,
};

// 得到属性描述符
// var desc = Object.getOwnPropertyDescriptor(obj, 'a');
// console.log(desc);

// 设置属性描述符
Object.defineProperty(obj, 'a', {
  value: 10,
  writable: false, // 不可重写
  enumerable: false, // 不可遍历
  configurable: false, // 不可修改描述符本身
});
// Object.defineProperty(obj, 'a', {
//   writable: true,
// });
obj.a = 'abc';
console.log(obj.a);
// for (var key in obj) {
//   console.log(key);
// }

// var keys = Object.keys(obj);
// console.log(keys);

// console.log(obj);

var obj = {
  b: 2,
};

// 得到属性描述符
// var desc = Object.getOwnPropertyDescriptor(obj, 'a');
// console.log(desc);

// 设置属性描述符
Object.defineProperty(obj, 'a', {
  value: 10,
  writable: false, // 不可重写
  enumerable: false, // 不可遍历
  configurable: false, // 不可修改描述符本身
});
// Object.defineProperty(obj, 'a', {
//   writable: true,
// });
obj.a = 'abc';
console.log(obj.a);
// for (var key in obj) {
//   console.log(key);
// }

// var keys = Object.keys(obj);
// console.log(keys);

// console.log(obj);

js
var obj = {};

Object.defineProperty(obj, 'a', {
  get: function () {
    return 123;
  }, // 读取器 getter
  set: function (val) {
    throw new Error(
      `兄弟,你正在给a这个属性重新赋值,你所赋的值是${val},但是,这个属性是不能复制,你再考虑考虑`
    );
  }, // 设置器 setter
});

console.log(obj.a);
obj.a = 'abx';
// console.log(obj.a); // console.log(get())

var obj = {};

Object.defineProperty(obj, 'a', {
  get: function () {
    return 123;
  }, // 读取器 getter
  set: function (val) {
    throw new Error(
      `兄弟,你正在给a这个属性重新赋值,你所赋的值是${val},但是,这个属性是不能复制,你再考虑考虑`
    );
  }, // 设置器 setter
});

console.log(obj.a);
obj.a = 'abx';
// console.log(obj.a); // console.log(get())

购物车描述

js
var aGoods = {
  pic: '.',
  title: '..',
  desc: `...`,
  sellNumber: 1,
  favorRate: 2,
  price: 3,
};

class UIGoods {
  get totalPrice() {
    return this.choose * this.data.price;
  }

  get isChoose() {
    return this.choose > 0;
  }

  constructor(g) {
    g = { ...g };// 克隆一份
    Object.freeze(g);// 冻结克隆对象
    Object.defineProperty(this, 'data', {
      get: function () {
        return g;
      },
      set: function () {
        throw new Error('data 属性是只读的,不能重新赋值');
      },
      configurable: false,
    });
    var internalChooseValue = 0;
    Object.defineProperty(this, 'choose', {
      configurable: false,
      get: function () {
        return internalChooseValue;
      },
      set: function (val) {
        if (typeof val !== 'number') {
          throw new Error('choose属性必须是数字');
        }
        var temp = parseInt(val);
        if (temp !== val) {
          throw new Error('choose属性必须是整数');
        }
        if (val < 0) {
          throw new Error('choose属性必须大于等于 0');
        }
        internalChooseValue = val;
      },
    });
    this.a = 1;
    Object.seal(this);// 密封自己 不能加属性了,其他属性还可以改
  }
}

Object.freeze(UIGoods.prototype);

var g = new UIGoods(aGoods);

UIGoods.prototype.haha = 'abc';
// g.data.price = 100;

console.log(g.haha);



// 思考:能不能想到各种场景,提前预防
var aGoods = {
  pic: '.',
  title: '..',
  desc: `...`,
  sellNumber: 1,
  favorRate: 2,
  price: 3,
};

class UIGoods {
  get totalPrice() {
    return this.choose * this.data.price;
  }

  get isChoose() {
    return this.choose > 0;
  }

  constructor(g) {
    g = { ...g };// 克隆一份
    Object.freeze(g);// 冻结克隆对象
    Object.defineProperty(this, 'data', {
      get: function () {
        return g;
      },
      set: function () {
        throw new Error('data 属性是只读的,不能重新赋值');
      },
      configurable: false,
    });
    var internalChooseValue = 0;
    Object.defineProperty(this, 'choose', {
      configurable: false,
      get: function () {
        return internalChooseValue;
      },
      set: function (val) {
        if (typeof val !== 'number') {
          throw new Error('choose属性必须是数字');
        }
        var temp = parseInt(val);
        if (temp !== val) {
          throw new Error('choose属性必须是整数');
        }
        if (val < 0) {
          throw new Error('choose属性必须大于等于 0');
        }
        internalChooseValue = val;
      },
    });
    this.a = 1;
    Object.seal(this);// 密封自己 不能加属性了,其他属性还可以改
  }
}

Object.freeze(UIGoods.prototype);

var g = new UIGoods(aGoods);

UIGoods.prototype.haha = 'abc';
// g.data.price = 100;

console.log(g.haha);



// 思考:能不能想到各种场景,提前预防

Reflect

Reflect是什么?

Reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能 由于它类似于其他语言的反射,因此取名为Reflect

它可以做什么?

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

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

有一个重要的理念,在ES5就被提出:减少魔法、让代码更加纯粹,这种理念很大程度上是受到函数式编程的影响

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

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

它里面到底提供了哪些API呢?

  • Reflect.set(target, propertyKey, value): 设置对象target的属性propertyKey的值为value,等同于给对象的属性赋值
javascript
const obj = {
  a: 1,
  b: 2
}
// obj.a = 10;
Reflect.set(obj, "a", 10);//给obj的a赋值为10
console.log(Reflect.get(obj, "a"))
const obj = {
  a: 1,
  b: 2
}
// obj.a = 10;
Reflect.set(obj, "a", 10);//给obj的a赋值为10
console.log(Reflect.get(obj, "a"))
  • Reflect.get(target, propertyKey): 读取对象target的属性propertyKey,等同于读取对象的属性值
  • Reflect.apply(target, thisArgument, argumentsList):调用一个指定的函数,并绑定this和参数列表。等同于函数调用
  • Reflect.deleteProperty(target, propertyKey):删除一个对象的属性
  • Reflect.defineProperty(target, propertyKey, attributes):类似于Object.defineProperty,不同的是如果配置出现问题,返回false而不是报错
  • Reflect.construct(target, argumentsList):用构造函数的方式创建一个对象
  • Reflect.has(target, propertyKey): 判断一个对象是否拥有一个属性
  • 其他API
javascript
function method(a, b){
    console.log("method", a, b);
}
// method(3, 4);
Reflect.apply(method, null, [3, 4])

const obj = {
    a: 1,
    b: 2
}
// delete obj.a;
Reflect.deleteProperty(obj, "a");
console.log(obj);

function Test(a, b) {
    this.a = a;
    this.b = b;
}

// const t = new Test(1, 3);
const t = Reflect.construct(Test, [1, 3]);
console.log(t)
function method(a, b){
    console.log("method", a, b);
}
// method(3, 4);
Reflect.apply(method, null, [3, 4])

const obj = {
    a: 1,
    b: 2
}
// delete obj.a;
Reflect.deleteProperty(obj, "a");
console.log(obj);

function Test(a, b) {
    this.a = a;
    this.b = b;
}

// const t = new Test(1, 3);
const t = Reflect.construct(Test, [1, 3]);
console.log(t)
js
const obj = {
  a: 1,
  b: 2
}

// console.log("a" in obj);
console.log(Reflect.has(obj, "a"));
const obj = {
  a: 1,
  b: 2
}

// console.log("a" in obj);
console.log(Reflect.has(obj, "a"));

Proxy

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

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

const proxy = new Proxy(obj, {
  set(target, propertyKey, value) {
    // console.log(target, propertyKey, value);
    // 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//代理说没有就没有
  }
});
// console.log(proxy);
// proxy.a = 10;
// console.log(proxy.a);

console.log(proxy.d);//运行get
console.log("a" in proxy);
const obj = {
  a: 1,
  b: 2
}

const proxy = new Proxy(obj, {
  set(target, propertyKey, value) {
    // console.log(target, propertyKey, value);
    // 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//代理说没有就没有
  }
});
// console.log(proxy);
// proxy.a = 10;
// console.log(proxy.a);

console.log(proxy.d);//运行get
console.log("a" in proxy);

应用

观察者模式

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

vue2

html
<div id="container">
</div>
<script>
  //创建一个观察者
  function observer(target) {//通过观察目标元素的变化,把这些属性渲染到页面上
    const div = document.getElementById("container");
    const ob = {};
    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//这两个属性默认不能被枚举
      })
    }
    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
  }
  const obj = observer(target)
</script>
<div id="container">
</div>
<script>
  //创建一个观察者
  function observer(target) {//通过观察目标元素的变化,把这些属性渲染到页面上
    const div = document.getElementById("container");
    const ob = {};
    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//这两个属性默认不能被枚举
      })
    }
    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
  }
  const obj = observer(target)
</script>

缺陷:搞出来了两个对象,占用了内存,代理不占用内存

vue3

html
<div id="container">

</div>

<script>
  //创建一个观察者
  function observer(target) {
    const div = document.getElementById("container");
    const proxy = new Proxy(target, {
      set(target, prop, value) {// 重写底层实现
        Reflect.set(target, prop, value);
        render();
      },
      get(target, prop){
        return Reflect.get(target, prop);
      }
    })
    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
  }
  const obj = observer(target)
</script>
<div id="container">

</div>

<script>
  //创建一个观察者
  function observer(target) {
    const div = document.getElementById("container");
    const proxy = new Proxy(target, {
      set(target, prop, value) {// 重写底层实现
        Reflect.set(target, prop, value);
        render();
      },
      get(target, prop){
        return Reflect.get(target, prop);
      }
    })
    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
  }
  const obj = observer(target)
</script>

偷懒的构造函数

恶心的类创建

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

自动赋值

javascript
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];
      })
      return obj;
    }
  })
}
const UserProxy = ConstructorProxy(User, "firstName", "lastName", "age")
const obj = new UserProxy("付", "志强", 18);
console.log(obj)
class Monster {
}
const MonsterProxy = ConstructorProxy(Monster, "attack", "defence", "hp", "rate", "name")
const m = new MonsterProxy(10, 20, 100, 30, "怪物")
console.log(m);
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];
      })
      return obj;
    }
  })
}
const UserProxy = ConstructorProxy(User, "firstName", "lastName", "age")
const obj = new UserProxy("付", "志强", 18);
console.log(obj)
class Monster {
}
const MonsterProxy = ConstructorProxy(Monster, "attack", "defence", "hp", "rate", "name")
const m = new MonsterProxy(10, 20, 100, 30, "怪物")
console.log(m);

可验证的函数参数

javascript
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}`);
        }
      })
      return Reflect.apply(target, thisArgument, argumentsList);
    }
  })
  return proxy;
}

const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, 2))
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}`);
        }
      })
      return Reflect.apply(target, thisArgument, argumentsList);
    }
  })
  return proxy;
}

const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, 2))

Proxy:如果没有我

javascript
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)//运行这个函数
  }
  return proxy;
}

const sumProxy = validatorFunction(sum, "number", "number")
console.log(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)//运行这个函数
  }
  return proxy;
}

const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, 2))