第二十八節(jié): ES6 Iterator與Proxy

1. Iterator

1.1 迭代器的理解

迭代器是一種接口颤枪、是一種機(jī)制汗捡。

為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問(wèn)機(jī)制。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口汇鞭,就可以完成遍歷操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)凉唐。

//以前的循環(huán)本質(zhì)跟蹤索引
let arr =[1,3,4,8,99];
console.log(arr.length);
for(let i=0; i<length; i++){
  console.log(arr[i]);
}
迭代器的理解
//迭代器是個(gè)方法,執(zhí)行后創(chuàng)建迭代對(duì)象霍骄。迭代對(duì)象上有next()方法台囱,每次調(diào)用next方法,返回一個(gè)結(jié)果對(duì)象读整。迭代對(duì)象中value屬性為數(shù)據(jù)簿训,done屬性判斷迭代是否繼續(xù)(false繼續(xù),true結(jié)束)
let arr = [12,3,45,88,23,66]    //查看數(shù)組身上有個(gè)迭代器函數(shù)Symbol(Symbol.iterator): ? values()
let obj =arr[Symbol.iterator]()  //迭代函數(shù)arr[Symbol.iterator]執(zhí)行后返回迭代對(duì)象
obj.next();//調(diào)用這個(gè)對(duì)象上的next()方法,會(huì)返回結(jié)果對(duì)象{value: 12, done: false}米间。value的值第一個(gè)值强品,done:false表示數(shù)組沒(méi)迭代完需要繼續(xù)調(diào)用next方法arr[Symbol.iterator]().next()
console.log(obj.next());  //返回的結(jié)果對(duì)象中done為true時(shí),迭代結(jié)束
terator 的作用有三個(gè):
  1. 為各種數(shù)據(jù)結(jié)構(gòu)屈糊,提供一個(gè)統(tǒng)一的的榛、簡(jiǎn)便的訪問(wèn)接口;
  2. 使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列逻锐;
  3. 主要供for...of消費(fèi)夫晌。
1.2 Iterator的本質(zhì)

Iterator本質(zhì)上,就是一個(gè)指針對(duì)象昧诱。

過(guò)程是這樣的:

(1)創(chuàng)建一個(gè)指針對(duì)象晓淀,指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置。

(2)第一次調(diào)用指針對(duì)象的next方法盏档,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個(gè)成員凶掰。

(3)第二次調(diào)用指針對(duì)象的next方法,指針就指向數(shù)據(jù)結(jié)構(gòu)的第二個(gè)成員蜈亩。

(4)不斷調(diào)用指針對(duì)象的next方法懦窘,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置。

1.3. 普通函數(shù)實(shí)現(xiàn)Iterator
    //單單抽離出來(lái)的迭代方法
    let obj = {
      0: 11,
      1: 22,
      2: 33,
      3: 44,
      4: 55,
      5: 66,
      length:5
    }
    function diedai() {
      let i=0;
      return {
        next: function () {
          //這里使用i勺拣,i就不會(huì)被銷毀奶赠,就形成了閉包
          let value=obj[i];    //值
          let done = i >= obj.length;  //判斷條件
          i++
          return {
            value,//簡(jiǎn)寫(xiě)
            done,
          }
        }
      }
    }
   let res= diedai(obj);
   console.log(res.next());
   console.log(res.next());
   console.log(res.next());
   console.log(res.next());
   console.log(res.next());
   console.log(res.next());
   console.log(res.next());
// 自定義迭代器的方法
//方法一,借用數(shù)組的迭代器
// 希望自定義對(duì)象也能使用迭代器药有。所以對(duì)象屬性要按類數(shù)組定義毅戈,有l(wèi)ength屬性
// let obj ={
//   0:11,
//   1:22,
//   2:33,
//   3:44,
//   4:55,
//   5:66,
//   length:6,
//   [Symbol.iterator] : Array.prototype[Symbol.iterator]//把數(shù)組原型上的迭代函數(shù)賦值給自定義對(duì)象上的屬性[Symbol.iterator]
// }
// console.log(Array.prototype);//數(shù)組的迭代就在數(shù)組構(gòu)造函數(shù)的原型上苹丸。打印數(shù)組原型上的屬性,就可以看到迭代器Symbol(Symbol.iterator): ? values()
// // 有了以上還是不可以迭代苇经,還要添加上length
// for(let val of obj){
//   console.log(val);
// }
//方法二自己定義一個(gè)迭代器(更好的理解迭代器的原理)
let obj ={
  0:11,
  1:22,
  2:33,
  3:44,
  4:55,
  5:66,
  length:6,
  [Symbol.iterator] :function(){
      let i=0;
      let This=this;   //這里的this是obj赘理,所以定義一個(gè)This=this,內(nèi)部的next方法才能使用到這個(gè)this
      return {
        next: function(){   
          //這里使用i扇单,i就不會(huì)被銷毀商模,就形成了閉包
          let value=This[i];    //值
          let done = i >= This.length;  //判斷條件
          i++
          return {
            value,//簡(jiǎn)寫(xiě)
            done,
          }
        }
      }
    }
}
for(let val of obj){       //for of 循環(huán)是obj對(duì)象會(huì)自動(dòng)調(diào)用[Symbol.iterator]方法,所以它是個(gè)方法蜘澜,返回一個(gè)對(duì)象施流,對(duì)象里有next方法,next方法執(zhí)行返回{value:值鄙信,done:true/false}
  console.log(val);
}
    let obj = {
      0: 11,
      1: 22,
      2: 33,
      3: 44,
      4: 55,
      5: 66,
      length: 5
    }
    function myIter() {
      let i = 0;
      return {
        next() {
          let done = (i >= obj.length);
          let value = !done ? obj[i++] : undefined;
          return {
            value,
            done,
          }
        }
      }
    }
    let res = myIter(obj);
    console.log(res.next());
    console.log(res.next());
    console.log(res.next());
    console.log(res.next());
    console.log(res.next());
    console.log(res.next());
    console.log(res.next());

原生具備 Iterator 接口的數(shù)據(jù)結(jié)構(gòu)如下瞪醋。
  • Array
  • Map
  • Set
  • String
  • 函數(shù)的 arguments 對(duì)象
  • NodeList 對(duì)象
<body>
  <ul>
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
  </ul>
  <script>
    // NodeList 對(duì)象例子
    let aLi = document.getElementsByTagName('li');
    console.log(aLi);  //打印后可以看到這個(gè)數(shù)組上也有迭代器方法Symbol(Symbol.iterator): ? values()。所以也可以使用for of方法
    for (let val of aLi) {
      console.log(val);
    }
  </script>
</body>
//下面的例子是數(shù)組的Symbol.iterator屬性装诡。
    let arr = ['a', 'b', 'c'];
    console.log(arr);
    let iter = arr[Symbol.iterator]();
    console.log(iter);
    iter.next() // { value: 'a', done: false }
    iter.next() // { value: 'b', done: false }
    iter.next() // { value: 'c', done: false }
    iter.next() // { value: undefined, done: true }

//下面是另一個(gè)類數(shù)組的對(duì)象調(diào)用數(shù)組的Symbol.iterator方法的例子银受。 
let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};

for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}
//注意,普通對(duì)象部署數(shù)組的Symbol.iterator方法鸦采,并無(wú)效果宾巍。
let iterable = {
  a: 'a',
  b: 'b',
  c: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // undefined, undefined, undefined
}
//字符串的包裝類是一個(gè)類似數(shù)組的對(duì)象,也原生具有 Iterator 接口渔伯。
var someString = "hi";
console.dir(String);
console.log(typeof someString[Symbol.iterator]);// "function"
var iterator = someString[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

2. Proxy 代理

Proxy 用于修改某些操作的默認(rèn)行為顶霞,等同于在語(yǔ)言層面做出修改,所以屬于一種“元編程”锣吼,即對(duì)編程語(yǔ)言進(jìn)行編程确丢。

講通俗一點(diǎn)就是擴(kuò)展(增強(qiáng))了對(duì)象,方法(函數(shù))的一些功能

ES6 原生提供 Proxy 構(gòu)造函數(shù),用來(lái)生成 Proxy 實(shí)例吐限。

Proxy其實(shí)是設(shè)計(jì)模式的一種,代理模式

2.1. 語(yǔ)法使用

new Proxy(target,handle)

  1. 參數(shù)
    第一個(gè)參數(shù): target 是你要代理的對(duì)象
    第二個(gè)參數(shù),handle是對(duì)代理對(duì)象做什么操作
    {
    set(){},
    get(){},
    deleteProperty(){},
    has(){},
    apply(),
    ......
    }
    返回值,返回一個(gè)新的對(duì)象
let proxy = new Proxy(target, handle);
  //代理的基本使用
    let obj = {
      name: 'xiaoming',
    }
    let myname = obj.name;  //js內(nèi)置獲取對(duì)象屬性的行為褂始,通過(guò)元對(duì)象操作
    console.log(myname);
    let proxy = new Proxy(obj, {  //寫(xiě)上代理哪個(gè)對(duì)象
      //里面寫(xiě)攔截函數(shù)
      //如果代理對(duì)象沒(méi)有任何攔截行為诸典,那么代理對(duì)象上的操作都是用原生的行為
    })
    console.log(proxy);    //Proxy {name: 'xiaoming'}打印后有Proxy表示代理對(duì)象

Proxy 對(duì)象的所有用法,都是上面這種形式崎苗,不同的只是handler參數(shù)的寫(xiě)法狐粱。其中,new Proxy()表示生成一個(gè)Proxy實(shí)例胆数,target參數(shù)表示所要攔截的目標(biāo)對(duì)象肌蜻,handler參數(shù)也是一個(gè)對(duì)象,用來(lái)定制攔截行為必尼。

2.2 如果沒(méi)有做任何攔截設(shè)置

如果handler沒(méi)有設(shè)置任何攔截蒋搜,那就等同于直接通向原對(duì)象篡撵。

var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"

上面代碼中,handler是一個(gè)空對(duì)象豆挽,沒(méi)有任何攔截效果育谬,訪問(wèn)proxy就等同于訪問(wèn)target。

let obj = {
    name : 'wuwei'
}
console.log(obj.name);

// 我希望你在獲取name 屬性的時(shí)候做一些事情,那么我們就可以用代理模式
let newObj = new Proxy(obj,{
    get(target,property){   // target就是代理對(duì)象obj,property就是用戶訪問(wèn)的屬性
        // console.log(target,property);   // {name: "wuwei"} "aaa"
        console.log(`你訪問(wèn)了${property}屬性`)
        return target[property];
    }
})

2.3 添加攔截處理程序

2.3.1 get 獲取攔截

get 攔截程序接受三個(gè)參數(shù)

  1. target 代理的目標(biāo)對(duì)象
  2. prop 操作的屬性
  3. receiver 代理對(duì)象
    //代理的攔截行為(函數(shù)方法)對(duì)js底層對(duì)象獲取屬性功能進(jìn)行攔截(操作代理對(duì)象獲取屬性時(shí)觸發(fā))
    //對(duì)象的操作 獲取 obj.name  修改obj.name='bb'   刪除 delete obj.name  判斷對(duì)象是否有XX屬性 XX in obj
    let obj = {
      name: 'xiaoming',
      age: 18
    }
    let proxy = new Proxy(obj, {
      //  獲取攔截
      get(target, prop, receiver) {
        //自己想擴(kuò)展的功能
        console.log(arguments);
        // return 111;      //方法一 return的值就是操作對(duì)象屬性的值
        // return target[prop]   //方法二 通過(guò)參數(shù)的代理目標(biāo)對(duì)象和屬性獲取原來(lái)的值
        // return Reflect.get(...arguments)//方法三 通過(guò)Reflect 反射到j(luò)s默認(rèn)的對(duì)象獲取屬性的行為上
        if (prop in target) {
          return Reflect.get(...arguments)
        } else {
          return `你獲取的對(duì)象${prop}不存在于當(dāng)前對(duì)象上`
          // throw new Error(`你獲取的對(duì)象${prop}不存在于當(dāng)前對(duì)象上`)//也可以選擇拋出一個(gè)錯(cuò)誤
        }
      }
    })
    console.log(proxy);
    console.log(proxy.name);  //沒(méi)有攔截之前帮哈,獲取得到的是默認(rèn)的值膛檀。添加了攔截就會(huì)自動(dòng)走對(duì)象的get方法。get獲取三個(gè)參數(shù):訪問(wèn)的代理目標(biāo)對(duì)象target娘侍、操作的屬性prop咖刃、receiver代理對(duì)象
    console.log(proxy.age);
    console.log(proxy.hobbies); //原來(lái)js不存在的js屬性會(huì)返回undefined

//訪問(wèn)代理對(duì)象上不具有的屬性就報(bào)錯(cuò)
let obj = {
            name: 'aabb'
        }
        let proxy = new Proxy(obj, {
            get(target, key, receiver) {
                if (!(key in receiver)) {
                    throw new ReferenceError(`屬性${key}不存在`)
                }
                return target[key]
            }
        })
        console.log(proxy.name);  // aabb
        console.log(proxy.age);   // 報(bào)錯(cuò)

get攔截的用處--批量創(chuàng)建HTML標(biāo)簽,屬性憾筏、內(nèi)容自動(dòng)添加

    // let oD = document.createElement('div')   //以前js創(chuàng)建標(biāo)簽創(chuàng)建div標(biāo)簽
    // oD.className = 'aa'//給標(biāo)簽添加class屬性
    // oD.innerHTML = '這里是內(nèi)容部分'   //標(biāo)簽加內(nèi)容
    let body = document.getElementsByTagName('body')[0]     //獲取標(biāo)簽
    // body.appendChild(oD)    //標(biāo)簽放到頁(yè)面上
    //get攔截的用處--批量創(chuàng)建HTML標(biāo)簽嚎杨,屬性、內(nèi)容自動(dòng)添加
    let pro = new Proxy({}, {  //代理空對(duì)象重要的是走get方法
      get(target, prop, receiver) {  //target是空對(duì)象 prop是屬性div  receiver是代理對(duì)象
        return function (attr = {}, child = []) {  //接收兩個(gè)參數(shù)  屬性羅列  子項(xiàng)  都有默認(rèn)值防意外
          let oBox = document.createElement(prop)//創(chuàng)建一個(gè)標(biāo)簽 
          for (let key in attr) {
            oBox[key] = attr[key]   //添加屬性
          }
          for (let item of child) {
            if (typeof item === 'string') {//判斷是字符串添加節(jié)點(diǎn)踩叭,不是字符串直接添加  
              item = document.createTextNode(item)
            }
            oBox.appendChild(item)  //添加節(jié)點(diǎn)
          }
          return oBox
        }
      }
    })
    let aa = pro.li()
    console.log('aa是沒(méi)有傳參數(shù)建立的空標(biāo)簽', aa);
    let bb = pro.div(      //返回函數(shù)磕潮,功能
      {
        className: 'aa',
        id: 'bb',
        style: 'backgroun-color:#ccc'
      }, [
      '字符串內(nèi)容',
      document.createTextNode('<p>文本內(nèi)容</p>')
    ]
    )
    console.log('bb是傳參數(shù)建立的標(biāo)簽', bb);

    let ul = pro.ul(
      {
        id: 'idul',
        className: 'aa',
        style: 'background-color:#ccc;font-size:30px',

      }, [
      document.createTextNode('<li>產(chǎn)品說(shuō)明</li>'),
      document.createTextNode('<li>運(yùn)輸方式</li>')
    ]
    )
    console.log('創(chuàng)建ul標(biāo)簽', ul);
    body.appendChild(ul)    //頁(yè)面中標(biāo)簽
2.3.2 set 設(shè)置攔截

set 攔截程序接受4個(gè)參數(shù)

  1. target 代理的目標(biāo)對(duì)象
  2. prop 操作的屬性
  3. value 被寫(xiě)入的屬性值
  4. receiver 代理對(duì)象
    //設(shè)置攔截(給代理對(duì)象屬性賦值)
    let obj = {
      name: 'xiaoming',
      age: 18
    }
    let proxy = new Proxy(obj, {
      set(target, prop, value, receiver) {  //目標(biāo)對(duì)象  操作的屬性  賦值的值 代理對(duì)象
        // target[prop] = value;   //方法直接操作代理對(duì)象修改
        // Reflect.set(...arguments)   //方法二 反射
        // console.log(arguments);
        // return 222;//基本沒(méi)用
        if (prop === 'age' && !(typeof value === 'number')) {    //age值不是數(shù)字類型就會(huì)報(bào)錯(cuò),其他屬性不受影響
          throw new Error('age屬性值必須是一個(gè)數(shù)字類型')
        } else {
          Reflect.set(...arguments)
        }
      }

    })
    console.log(proxy);
    proxy.name = 'mingming';
    proxy.age = '123';   //年齡一般是數(shù)字容贝,這里賦值字符串會(huì)報(bào)錯(cuò)
    console.log(proxy);
    console.log(obj);
var obj = new Proxy({},{
    set(target,prop,value){
        if(prop == 'age'){
            if(!Number.isInteger(value)){
                throw new TypeError('年齡必須為整數(shù)')
            }
            if(value > 200){
                throw new RangeError('年齡超標(biāo)了,必須小于200歲')
            }
        }
        target[prop] = value;
    }
})

obj.a = '12.5';
obj.name = 'wuwei';
console.log(obj);    //Proxy {a: "12.5", name: "wuwei"}

// 以下是報(bào)錯(cuò)
obj.age = 12.6;   // Uncaught TypeError: 年齡必須為整數(shù)
obj.age = 201;    // Uncaught RangeError: 年齡超標(biāo)了,必須小于200歲
2.3.3 has 包含攔截

使用in 操作符檢查對(duì)象中是否包含某個(gè)屬性. 觸發(fā)has攔截

has 攔截程序接受2個(gè)參數(shù)

  1. target 代理的目標(biāo)對(duì)象
  2. prop 需要判斷的屬性
    //判斷是否為對(duì)象屬性的攔截has
    let obj = {
      name: 'xiaoming',
      age: 18
    }
    console.log('name' in obj);    //判斷obj對(duì)象上是否有屬性name時(shí)自脯,name要加引號(hào)。否則就為false斤富。這是js的默認(rèn)行為
    console.log('age' in obj)
    console.log('hobby' in obj)   //沒(méi)有這個(gè)屬性膏潮,所以false


    let pro = new Proxy(obj, {
      has(target, prop) {  //兩個(gè)參數(shù),目標(biāo)對(duì)象满力,判斷的哪個(gè)屬性
        console.log(arguments)
        console.log(`判斷屬性${prop}是否存在于當(dāng)前對(duì)象中`)
        // return prop in target//方法一得到值操作原生的
        return Reflect.has(...arguments)//方法二通過(guò)反射

      }
    })
    console.log('name' in pro);    //判斷屬性是否屬于攔截對(duì)象
let obj = {
      a: 1,
      b: 2
    }
    let proxy = new Proxy(obj, {
      has(target, prop) {
        console.log(`判斷屬性${prop}是否存在于當(dāng)前對(duì)象中`)

        return prop in target
      }
    })
    console.log('a' in proxy);

2.3.4 deleteProperty 刪除攔截

通過(guò)delete 操作符刪除屬性的時(shí)候,就會(huì)觸發(fā)deleteProperty 攔截

deleteProperty 攔截處理程序接受2個(gè)參數(shù)

  1. target 代理的目標(biāo)對(duì)象
  2. prop 刪除操作的屬性
    // 攔截刪除行為
    let obj = {
      name: 'xiaoming',
      age: 18
    }
    // console.log(delete obj.name); //js原生的默認(rèn)行為
    let pro = new Proxy(obj, {
      deleteProperty(target, prop) {//兩個(gè)參數(shù)目標(biāo)對(duì)象焕参,操作哪個(gè)屬性  
        console.log(arguments)
        console.log(`你要?jiǎng)h除${prop}屬性`);
        // return delete target[prop] //方法一 原生的
        return Reflect.deleteProperty(...arguments)//通過(guò)反射,反射到j(luò)s底層的 deleteProperty
      }
    })
    console.log(delete pro.name);

2.4 函數(shù)攔截

所有的代理攔截中, 只有apply 和 constructor 的代理目標(biāo)是一個(gè)函數(shù). apply 和construct 攔截方法覆寫(xiě)函數(shù)內(nèi)部的[[ Call ]]和 [[ Constructor ]],這些內(nèi)部方法.

函數(shù)的普通的調(diào)用實(shí)際是調(diào)用函數(shù)內(nèi)部的[[ Call ]]方法油额。通過(guò)new來(lái)操作一個(gè)函數(shù)底層是調(diào)用的 [[ Constructor ]]

2.4.1 apply 攔截

apply攔截接受三個(gè)參數(shù)

  1. target 代理的目標(biāo)函數(shù)
  2. thisArg 函數(shù)被調(diào)用時(shí)內(nèi)部this的值
  3. argumentsList 傳遞給函數(shù)的數(shù)組
//代理函數(shù)修改tihs
function haha() {
  console.log(this);
  console.log(arguments);
}
let bb = new Proxy(haha,{   //第一個(gè)參數(shù)為代理的函數(shù)
  apply(target,thisArg,Arg){     //apply 攔截
    // console.log(arguments);//參數(shù) 目標(biāo)函數(shù)叠纷、this指向、實(shí)參組成的數(shù)組
    //里面的this始終是window潦嘶,要從外面改變this,this才會(huì)改變
    // target.apply(thisArg,Arg)   //方法一 通過(guò)代理的目標(biāo)函數(shù)來(lái)執(zhí)行的 
    Reflect.apply(...arguments)//方法二Reflect
  }
})
let obj ={name:'obj'}
bb.apply(obj,[10,20,30])
//代理函數(shù)修改tihs的分析與理解
function haha() {
  console.log(this);
  console.log(arguments);
}
let bb = new Proxy(haha,{  
  apply(target,thisArg,Arg){    
    Reflect.apply(target,obj,Arg)     //代理對(duì)象中手動(dòng)修改this涩嚣,一般不會(huì)這么操作
  }
})
let obj ={name:'obj'}
bb(10,20,30)
2.4.2 construct 攔截

construct 攔截 new 操作符調(diào)用函數(shù)

  1. target 代理目標(biāo)函數(shù)
  2. argumentsList 傳遞給函數(shù)的參數(shù)數(shù)組
    function fn(a, b) {
      this.a = a,
        this.b = b
      return 44
    }
    let proxy = new Proxy(fn, {
      apply(target, thisArg, argumentsList) {
        return target.apply(this.Arg, argumentsList)

      },    //底層apply叫[[ Call ]]
      construct(target, argumentsList) {
        console.log(arguments)   //目標(biāo)函數(shù) 參數(shù) 代理對(duì)象
        // return new target(...argumentsList)   
        return Reflect.construct(...arguments)//更好的寫(xiě)法
      }             //底層construct叫 [[ Constructor ]]
    })

    let res = new proxy(10, 20)   //有new走construct  沒(méi)有走apply
    console.log(res)  //使用new返回一個(gè)對(duì)象
    console.log(fn(5, 6));
2.5 取消代理 Proxy.revocable()

Proxy.revocable方法返回一個(gè)可撤銷代理實(shí)例, 參數(shù)與Proxy構(gòu)造函數(shù)一致。

    let target = { name: 'aa', age: 19 };
    let handler = {};
    let { proxy, revoke } = Proxy.revocable(target, handler); //通常采用解構(gòu)方式
    //Proxy.revocable方法參數(shù)和new Proxy一樣掂僵,返回值不一樣航厚,返回一個(gè)對(duì)象,對(duì)象中的proxy屬性為代理對(duì)象,其實(shí)就是new Proxy()創(chuàng)建的對(duì)锰蓬。 revoke是一個(gè)方法用來(lái)取消代理
    // new Proxy()    //這種代理返回值是代理對(duì)象proxy
    proxy.foo = 123;
    console.log(proxy.foo)
    console.log(proxy);
    revoke();
    console.log(proxy.foo)    //報(bào)錯(cuò)幔睬,取消代理后無(wú)法獲取

Proxy.revocable方法返回一個(gè)對(duì)象,該對(duì)象的proxy屬性是Proxy實(shí)例芹扭,revoke屬性是一個(gè)函數(shù)麻顶,撤銷代理調(diào)用的函數(shù)赦抖。上面代碼中.

當(dāng)執(zhí)行revoke函數(shù)之后,任何與代理對(duì)象的交互都會(huì)觸發(fā)錯(cuò)誤澈蚌。

2.6. Proxy支持的攔截操作
  • get(target, propKey, receiver):攔截對(duì)象屬性的讀取摹芙,比如proxy.foo和proxy['foo']。
  • set(target, propKey, value, receiver):攔截對(duì)象屬性的設(shè)置宛瞄,比如proxy.foo = v或proxy['foo'] = v浮禾,返回一個(gè)布爾值。
  • has(target, propKey):攔截propKey in proxy的操作份汗,返回一個(gè)布爾值盈电。
  • deleteProperty(target, propKey):攔截delete proxy[propKey]的操作,返回一個(gè)布爾值杯活。
  • ownKeys(target):攔截Object.getOwnPropertyNames(proxy)匆帚、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)旁钧,返回一個(gè)數(shù)組吸重。該方法返回目標(biāo)對(duì)象所有自身的屬性的屬性名,而Object.keys()的返回結(jié)果僅包括目標(biāo)對(duì)象自身的可遍歷屬性歪今。
    let obj = { name: 'aa', age: 19 };
    console.log(Object.getOwnPropertyNames(obj)) //原生的js底層的一個(gè)方法獲取key嚎幸,組成數(shù)組
    let pro = new Proxy(obj, {  //通過(guò)代理的
      ownKeys() {
        console.log(arguments)
        return []
      }
    })
    console.log(Object.getOwnPropertyNames(pro))     //參數(shù)為代理對(duì)象,就會(huì)觸發(fā)代理對(duì)象里的ownKeys的攔截
  • getOwnPropertyDescriptor(target, propKey):攔截Object.getOwnPropertyDescriptor(proxy, propKey)寄猩,返回屬性的描述對(duì)象嫉晶。
  • defineProperty(target, propKey, propDesc):攔截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs)田篇,返回一個(gè)布爾值替废。
  • preventExtensions(target):攔截Object.preventExtensions(proxy),返回一個(gè)布爾值泊柬。
  • getPrototypeOf(target):攔截Object.getPrototypeOf(proxy)椎镣,返回一個(gè)對(duì)象。
  • isExtensible(target):攔截Object.isExtensible(proxy)兽赁,返回一個(gè)布爾值衣陶。
  • setPrototypeOf(target, proto):攔截Object.setPrototypeOf(proxy, proto),返回一個(gè)布爾值闸氮。如果目標(biāo)對(duì)象是函數(shù),那么還有兩種額外操作可以攔截教沾。
  • apply(target, object, args):攔截 Proxy 實(shí)例作為函數(shù)調(diào)用的操作蒲跨,比如proxy(...args)、proxy.call(object, ...args)授翻、proxy.apply(...)或悲。
  • construct(target, args):攔截 Proxy 實(shí)例作為構(gòu)造函數(shù)調(diào)用的操作孙咪,比如new proxy(...args)。
2.7.reflect 反射
function fn(a,b){
    return a+b
}
var newFn = new Proxy(fn,{
  apply(target,context,args){
    // console.log(target,context,args);
    // console.log(arguments);
    // console.log(...arguments);
    return Reflect.apply(...arguments); 
  }
  
})

console.log(newFn(3,2));   // 5

如果要增強(qiáng)方法需要跟Reflect配合

我也可以在反射時(shí)干些事情

    //反射調(diào)用函數(shù)
    function aa() {
      return 10
    }
    let pro = new Proxy(aa, {//代理aa函數(shù)
      apply() {
        return '函數(shù)原生執(zhí)行返回值為' + Reflect.apply(...arguments) + '巡语。原生執(zhí)行返回值的三次方為' + Reflect.apply(...arguments) ** 3//函數(shù)原生的執(zhí)行
      }
    })
    console.log(pro());
    function fn() {
      return 5;
    }
    var newFn = new Proxy(fn, {
      apply(target, context, args) {
        // console.log(target, context, args);
        // console.log(arguments);
        // console.log(...arguments);
        return Reflect.apply(...arguments) ** 3;   //反射結(jié)果的3次方
      }
    })
    console.log(newFn(3, 2));   // 125

2.8 Reflect.apply()

和fn.apply()很相似

Reflect.apply(target,context,args) 有三個(gè)參數(shù)

target: 需要調(diào)用的函數(shù)

context: this指向

args : 參數(shù)數(shù)組

let num =Math.ceil(5.01);   //向上取整
console.log(num);
//也可以用反射
let num2 = Reflect.apply(Math.ceil,null,[5.01]) //反射Math.ceil的apply方法翎蹈,調(diào)用Math.ceil原生的方法 ,不需要修改this指向,參數(shù)是數(shù)組   
console.log(num2);

就是調(diào)用函數(shù)的不同的方式而已男公,更高級(jí)荤堪,js底層些的函數(shù)調(diào)用方法

function show(...args){
    console.log(this);
    console.log(args);
}
// 正常調(diào)用 
show(1,2,3,4);                        // this是window, args是[1,2,3,4]
// call調(diào)用函數(shù)
show.call('aaa',1,2,3,4);             // this是aaa,    args是[1,2,3,4]
// apply調(diào)用函數(shù)
show.apply('aaa',[1,2,3,4]);          // this是aaa,    args是[1,2,3,4]
// reflect.apply調(diào)用函數(shù)
Reflect.apply(show,'aaa',[1,2,3,4]);  // this是aaa,    args是[1,2,3,4]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市枢赔,隨后出現(xiàn)的幾起案子澄阳,更是在濱河造成了極大的恐慌,老刑警劉巖踏拜,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碎赢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡速梗,警方通過(guò)查閱死者的電腦和手機(jī)肮塞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)姻锁,“玉大人枕赵,你說(shuō)我怎么就攤上這事∥菟ぃ” “怎么了烁设?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)钓试。 經(jīng)常有香客問(wèn)我装黑,道長(zhǎng),這世上最難降的妖魔是什么弓熏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任恋谭,我火速辦了婚禮,結(jié)果婚禮上挽鞠,老公的妹妹穿的比我還像新娘疚颊。我一直安慰自己,他們只是感情好信认,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布材义。 她就那樣靜靜地躺著,像睡著了一般嫁赏。 火紅的嫁衣襯著肌膚如雪其掂。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天潦蝇,我揣著相機(jī)與錄音款熬,去河邊找鬼深寥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛贤牛,可吹牛的內(nèi)容都是我干的惋鹅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼殉簸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼闰集!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起喂链,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤返十,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后椭微,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體洞坑,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年蝇率,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迟杂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡本慕,死狀恐怖排拷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锅尘,我是刑警寧澤监氢,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站藤违,受9級(jí)特大地震影響浪腐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜顿乒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一议街、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧璧榄,春花似錦特漩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至搓蚪,卻和暖如春凌净,著一層夾襖步出監(jiān)牢的瞬間悠瞬,已是汗流浹背羔巢。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工檩小, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杜耙。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓搜骡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親佑女。 傳聞我的和親對(duì)象是個(gè)殘疾皇子记靡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. Iterator 1.1 迭代器的理解 迭代器是一種接口、是一種機(jī)制团驱。 為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問(wèn)機(jī)...
    時(shí)光如劍閱讀 579評(píng)論 0 2
  • Es6 1.數(shù)組 1.擴(kuò)展運(yùn)算符 是三個(gè)點(diǎn)(...)摸吠。它好比 rest 參數(shù)的逆運(yùn)算,將一個(gè)數(shù)組轉(zhuǎn)為用逗號(hào)分隔的參...
    林深不見(jiàn)鹿閱讀 226評(píng)論 0 0
  • 原本想稍微整理一下 ES 新特性嚎花,沒(méi)想到花了相當(dāng)多的時(shí)間寸痢,本文也巨長(zhǎng),依然推薦使用 簡(jiǎn)悅[https://gith...
    401閱讀 1,871評(píng)論 0 5
  • 1.let和const命令 暫時(shí)性死區(qū)的本質(zhì)就是紊选,只要一進(jìn)入當(dāng)前作用域啼止,所要使用的變量就已經(jīng)存在了,但是不可獲取兵罢,...
    塵滿面鬢微霜閱讀 377評(píng)論 0 0
  • 本文為阮一峰大神的《ECMAScript 6 入門》的個(gè)人版提純献烦! babel babel負(fù)責(zé)將JS高級(jí)語(yǔ)法轉(zhuǎn)義,...
    Devildi已被占用閱讀 1,983評(píng)論 0 4