Proxy

如果你平时有关注 Vue 的进展的话,可能已经知道了在 Vue3.0 中将会通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。 Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作

Proxy它是一个构造函数,返回一个代理对象Proxy,主要用于从外部控制对对象内部的访问。

使用 Proxy 的好处是:对象只需关注于核心逻辑,一些非核心的逻辑(如:读取或设置对象的某些属性前记录日志;设置对象的某些属性值前,需要验证;某些属性的访问控制等)可以让 Proxy 来做。从而达到关注点分离,降级对象复杂度的目的。

代理允许你拦截在目标对象上的底层操作(Object下的许多原型方法),而这原本是 JS 引擎的内部能力。拦截行为使用了 一个能够响应特定操作的函数(被称为陷阱)
外部如何去影响对象内部的访问呢,先创建一个简单的代理
    //目标对象        句柄对象
    var target = {}, handler = {};
    var proxy = new Proxy(target, handler);

那么Proxy、target、handler这三者之间有什么关系呢?Proxy的行为很简单:将Proxy的所有内部方法转发至target 。即调用Proxy的方法就会调用target上对应的方法。那么handler又是用来干嘛的?handler的方法可以覆写任意代理的内部方法(一个或多个陷阱函数的对象)。 外界每次通过Proxy访问target 对象的属性时,就会经过 handler 对象,因此,我们可以通过重写handler对象中的一些方法来做一些拦截的操作。

    //最简单粗暴的proxy
    let target={}
    let proxy=new Proxy(target,{})

    proxy.name = "proxy"; 
    console.log(proxy.name); // "proxy" 
    console.log(target.name); // "proxy"
    target.name = "target";
    console.log(proxy.name); // "target" 
    console.log(target.name); // "target"
    console.log(proxy,target)//Proxy {name: "target"} {name: "target"}

之前说了哈Proxy返回一个Proxy对象

该例中的 proxy 对象将所有操作直接传递给 target 对象,当然没有陷阱函数的代理没什么用

使用 set 陷阱函数验证属性值

         var a={
                _year:2019,
                get year(){
                    return this._year
                },
                set year(newValue){
                    this._year=newValue
                }
            }

可以区分一下写法,ES5 set支持一个参数,代理中的set支持四个参数

    trapTarget :将接收属性的对象(即代理的目标对象);
    key :需要写入的属性的键(字符串类型或符号类型);
    value :将被写入属性的值;
    receiver :操作发生的对象(通常是代理对象)//很多时候可以省略

简单的demo

 let target={
          name:'lwj'
      }

      let proxy=new Proxy(target,{
          set(trapTarget, key, value, receiver){
              if(!trapTarget.hasOwnProperty(key)){ //如果已存在的属性,绕过
                  if(isNaN(value)){
                    throw new TypeError("Property must be a number.");
                  }
              }
              //添加属性 
              return Reflect.set(trapTarget, key, value, receiver)
             //这里也可以改写 不使用上方的代码  函数还是尽量return一下 当然建议使用上方的方法
            //trapTarget[key]=value  
           //   return true
          }
      })
      proxy.count=1
      console.log(proxy.count) //1
      console.log(target.count) //1
      proxy.name='lwj'
      console.log(proxy.name)//lwj
      console.log(target.name) //lwj
      proxy.anotherName = "proxy"; //报错

set 代理陷阱允许你在写入属性值的时候进行拦截,而 get 代理陷阱则允许你在读取属性 值的时候进行拦截。

get陷阱函数参数借鉴了 set 陷阱函数的参数,只有一个明显的不同,也就是没有使用 value

      let target={
          name:'lwj'
      }
      console.log(target.age)//undefined

由于js语言的特殊,属性是不会报错的,然而有时候我们不希望存在这个问题

      let proxy=new Proxy({},{
          get(tarpTarget,key,receiver){
              //不存在此属性
              if(!(key in receiver)){
                throw new TypeError("Property " + key + " doesn't exist.");
              }
              return Reflect.get(tarpTarget,key,receiver);
          }
      })
      proxy.name='lwj'
      console.log(proxy.name)
      console.log(proxy.names) //报错 Property names doesn't exist.

这里使用receiver是因为receiver自带has陷阱函数,使用 trapTarget 会跳过 has 陷阱函数

写has方法之前写一个数据监听,如果你能看懂的话很棒,看不懂还是再看看前面!
     let onWatch=(obj,sets,gets)=>{
         let handler={
             get(tarpTarget,key,receiver){
                 gets(tarpTarget,key)
                 return Reflect.get(tarpTarget,key,receiver)
             },
             set(tarpTarget,key,value,receiver){
                sets(value,key) 
                return Reflect.set(tarpTarget,key,value,receiver)
             }
         }
         return new Proxy(obj, handler)
     }

就不打注释了,自己理解的好

     let obj={a:1}
     let p=onWatch(obj,
       (v,property)=>{
        console.log(`监听到属性${property}改变为${v}`)
       },
       (target,property)=>{
        console.log(`'${property}' = ${target[property]}`)
       }
     ) 
     p.a = 2 // 监听到属性a改变
     p.a // 'a' = 2

之所以 Vue3.0 要使用 Proxy 替换原本的 API 原因在于 Proxy 无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是 Proxy 可以完美监听到任何方式的数据改变,唯一缺陷可能就是浏览器的兼容性不好了

   get(target,key,receiver){s
        gets(target, key)
        // 这句判断代码是新增的
        if (typeof tarpTarget[key] === 'object' && tarpTarget[key] !== null) {
            return new Proxy(tarpTarget[key],handler);
        } else {
            return Reflect.get(tarpTarget,key);
        }
    }

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与处理器对象(陷阱函数)的方法相同。Reflect不是一个函数对象,因此它是不可构造的。比如Math对象

has陷阱函数

trapTarget :需要读取属性的对象(即代理的目标对象);
key :需要检查的属性的键(字符串类型或符号类型)。

let target={
    value:10,
    name:'lwj'
}

let proxy=new Proxy(target,{
    has(trapTarget,key){
        if(key==='value'){
            return false
        }else{
            return Reflect.has(trapTarget,key)
        }
    }
})
console.log("value" in proxy); // false
console.log("name" in proxy); // true
console.log("toString" in proxy); // true

使用 deleteProperty 陷阱函数避免属性被删除

delete 运算符能够从指定对象上删除一个属性,在删除成功时返回 true ,否则返回 false

var obj={
    name:'lwj'
}
Object.defineProperty(obj,'age',{
    value:18,
    configurable:false
})

console.log("name" in obj) //true
console.log('age' in obj)  //true
let result= delete obj.name
console.log(result) //true
console.log("name" in obj) //false
let results= delete obj.age
console.log(results) // false
console.log('age' in obj)  //true

deleteProperty 陷阱函数会在使用 delete 运算符去删除对象属性时下被调用,并且会被传 入两个参数:
trapTarget :需要删除属性的对象(即代理的目标对象);
key :需要删除的属性的键(字符串类型或符号类型)。

let target={
    name:'lwj',
    age:18
}

let proxy=new Proxy(target,{
    deleteProperty(trapTarget,key){
        if(key==="age"){
            return false
        }else{
            return Reflect.deleteProperty(trapTarget,key)
        }
    }
})

console.log("age" in proxy); // true
let result1 = delete proxy.age
console.log(result1); // false
console.log("age" in proxy); // true
console.log("name" in proxy); // true
let result2 = delete proxy.name
console.log(result2); // true
console.log("name" in proxy); // false
写下个陷阱函数之前先弥补两个对象下的方法getPrototypeOf(),setPrototypeOf(),获取原型设置原型
let person={
    getting(){
        return 'Hello'
    }
}

let dog={
    getting(){
        return 'Hi'
    }
}

let friend = Object.create(person)
console.log(friend.getting());//Hello
console.log(Object.getPrototypeOf(friend) === person); // true
//原型设置为dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getting());//Hi
console.log(Object.getPrototypeOf(friend) === dog); // true

寄生组合继承就是利用了Object.create方法,将父类的原型赋值给了子类,既解决了无用的父类属性问题,还能正确的找到子类的构造函数。

function Person(age){
    this.age=age
}
function Child(age){
    Person.call(this,age)
}
Child.prototype=Object.create(Person.prototype,{
    constructor:{
        value:Child,
        enumerable: false,
        writable: true,
        configurable: true
    }
})
const child=new Child(18)

原型代理的陷阱函数setPrototypeOf() getPrototypeOf()

原型代理的陷阱函数类似上方,也有两个,同样也可以利用Reflect反射,参数:
trapTarget :需要设置原型的对象(即代理的目标对象);
proto :需用被用作原型的对象。
getPrototypeOf 陷阱函数只接受 trapTarget 参数

这些陷阱函数受到一些限制。首先, getPrototypeOf 陷阱函数的返回值必须是一个对象或者 null ,其他任何类型的返回值都会引发“运行时”错误。对于返回值的检测确保了 Object.getPrototypeOf() 会返回预期的结果。类似的, setPrototypeOf 必须在操作没有成 功的情况下返回 false ,这样会让 Object.setPrototypeOf() 抛出错误;而若 setPrototypeOf 的返回值不是 false ,则 Object.setPrototypeOf() 就会认为操作已成 功。

先看代码再看上面文字容易理解
let target = {};
let proxy=new Proxy(target,{
    getPrototypeOf(trapTarget){
        return null
    },
    setPrototypeOf(trapTarget,proto){
        return false
    }
})

let targetProto = Object.getPrototypeOf(target);
let proxyProto = Object.getPrototypeOf(proxy);
console.log(targetProto === Object.prototype); // true
console.log(proxyProto === Object.prototype); // false
console.log(proxyProto); // null

Object.setPrototypeOf(target, {}); //成功

Object.setPrototypeOf(proxy, {}); //报错

使用 target 对象作为参数调用 Object.getPrototypeOf() 会返回一个对象值;而使用 proxy 对象调用该方法则会返回 null ,因为 getPrototypeOf 陷阱函数被调用了。类似的,使用 target 去调用 Object.setPrototypeOf() 会成功;而由于 setPrototypeOf 陷阱函数的存在,使用 proxy 则会引发错误。

let target = {};
let proxy=new Proxy(target,{
    getPrototypeOf(trapTarget){
        return Reflect.getPrototypeOf(trapTarget);
    },
    setPrototypeOf(trapTarget,proto){
        return Reflect.setPrototypeOf(trapTarget, proto);
    }
})

如此就可以了,俩者方法看起来非常相似但是也有差异,有兴趣可以百度,这里简单过一下

首先, Object.getPrototypeOf() 与 Object.setPrototypeOf() 属于高级操作,而 Reflect.getPrototypeOf() 与 Reflect.setPrototypeOf() 属于底层 操作,允许开发者访问 [[GetPrototypeOf]] 与 [[SetPrototypeOf]]

Reflect.getPrototypeOf() 方法在接收到的参数不是一个对象时会抛出错误,而 Object.getPrototypeOf() 则会在操作之前先将参数值转换为一个对象。

 let result1 = Object.getPrototypeOf(1);
 console.log(result1 === Number.prototype); // true
 Reflect.getPrototypeOf(1); //报错

对象可扩展性的陷阱函数

对象是否是可扩展的(是否可以在它上面添加新的属性)

Object.isExtensible(obj); // === true

大多数基本对象都是可扩展的

    // ...可以变的不可扩展.
    Object.preventExtensions(obj);
    Object.isExtensible(obj); // === false

密封对象与冻结对象也不可扩展
Object.seal({}); Object.freeze({});

    let target={}

    let proxy=new Proxy(target,{
        isExtensible(trapTarget){
            return Reflect.isExtensible(trapTarget);
        },
        preventExtensions(trapTarget) {
            //return false 即不会改变代理对象的可扩展性
            return Reflect.preventExtensions(trapTarget);
        }
    })
    console.log(Object.isExtensible(target)); // true
    console.log(Object.isExtensible(proxy)); // true
    Object.preventExtensions(proxy);
    console.log(Object.isExtensible(target)); // false
    console.log(Object.isExtensible(proxy)); // false

属性描述符的陷阱函数

defineProperty(trapTarget, key, descriptor)
getOwnPropertyDescriptor(trapTarget, key)

descriptor :为该属性准备的描述符对象

ES5 最重要的特征之一就是引入了 Object.defineProperty() 方法用于定义属性的特性。在 JS 之前的版本中,没有方法可以定义一个访问器属性,也不能让属性变成只读或是不可枚 举。而这些特性都能够利用 Object.defineProperty() 方法来实现,并且你还可以利用 Object.getOwnPropertyDescriptor() 方法来检索这些特性。

  var obj={
      name:'lwj'
  }
  Object.defineProperty(obj,'age',{
      value:18,
      enumerable:true,
      writable:true,
      configurable:true
  })

 console.log(Object.getOwnPropertyDescriptor(obj,'name')) ===> descriptor
 descriptor : {value: "lwj", writable: true, enumerable: true, configurable: true}

 let proxy=new Proxy({},{
     defineProperty(trapTarget,key,descriptor){
         //此处可以做拦截  比如:
        //  if (typeof key === "symbol") { return false; }
        return Reflect.defineProperty(trapTarget, key, descriptor);
     },
     getOwnPropertyDescriptor(trapTarget,key){
        return Reflect.getOwnPropertyDescriptor(trapTarget, key);
     }
 })

 Object.defineProperty(proxy,'age',{
     value:18
 })
 console.log(proxy.age) //18
 let descriptor = Object.getOwnPropertyDescriptor(proxy, "age");
 console.log(descriptor.value);//18

ownKeys 陷阱函数

这个函数比较大哥,相当于调用了Object.keys() 方法、 Object.getOwnPropertyNames() 方法、 Object.getOwnPropertySymbols() 方法与 Object.assign() 方法

   let proxy=new Proxy({},{
       ownKeys(trapTarget){
           //返回一个数组 
        return Reflect.ownKeys(trapTarget).filter( key =>{ //这里的key是返回的每一项
            return typeof key!=='string'||key[0]!=='_' //过滤 带下划线的属性
        });
       }
   })
   let nameSymbol = Symbol("name");
   //增加属性
   proxy.name = "proxy";
   proxy._name = "private";
   proxy[nameSymbol] = "symbol";

   //获取返回数组
   let names = Object.getOwnPropertyNames(proxy),
   keys = Object.keys(proxy),
   symbols = Object.getOwnPropertySymbols(proxy);
   console.log(names)  //name
   console.log(keys)   //name
   console.log(symbols)//[Symbol(name)]

使用 apply 与 construct 陷阱函数的函数代理

在所有的代理陷阱中,只有 apply 与 construct 要求代理目标对象必须是一个函数

函数被调用是执行 apply 陷阱函数
trapTarget :被执行的函数(即代理的目标对象);
thisArg :调用过程中函数内部的 this 值;
argumentsList :被传递给函数的参数数组。

当使用 new 去执行函数时, construct 陷阱函数会被调用并接收到下列两个参数:
trapTarget :被执行的函数(即代理的目标对象);
argumentsList :被传递给函数的参数数组。

     let target=function(){return 1}
     let proxy=new Proxy(target,{
         apply(trapTarget,thisArg,argumentList){
            return Reflect.apply(trapTarget,thisArg,argumentList);
         },
         construct(trapTarget,argumentList){
             return Reflect.construct(trapTarget,argumentList)
         }
     })
     // 使用了函数的代理,其目标对象会被视为函数
     console.log(typeof proxy); // "function"
     console.log(proxy()); // 1
     var instance = new proxy();
     console.log(instance instanceof proxy); // true
     console.log(instance instanceof target); // true

apply 与 construct 陷阱函数在函数的执行方式上开启了很多的可能性。例如,假设你想要 保证所有参数都是某个特定类型的,可使用 apply 陷阱函数来进行验证:

     function sum(...values){
         return values.reduce((prev,current)=>prev+current)
     }
     let sumProxy=new Proxy(sum,{
         apply(trapTarget,thisArg,argumentList){
             argumentList.forEach(el=> {
                 //参数必须为num
                 if(typeof el!=='number'){
                    throw new TypeError("All arguments must be numbers.");
                 }
             });
             return Reflect.apply(trapTarget,thisArg,argumentList);
         },
         construct(trapTarget,argumentList){   //不允许创建实例对象 
            throw new TypeError("This function can't be called with new.");
         }
     })
     console.log(sumProxy(1, 2, 3, 4)); // 10
     console.log(sumProxy(1, "2", 3, 4));//报错
     let result = new sumProxy();//报错

同样可以再construc陷阱函数中对argumentList进行限制

ES6为我们提供了一个元属性(元属性指的是“非对象”(例如 new ) 上的一个属性) new.target,主要可以限制构造函数必须使用new

     function Person(name){
         if(typeof new.target!=='undefined'){
             this.name=name //使用了new
         }else{
            throw new Error("You must use new with Person.")
         }
     }
     var person = new Person("lwj");
     var notAPerson = Person('age') //报错

一般情况下构造函数是必须带new的
在new一个实例的过程中,其实是执行了如下的步骤

1、声明一个中间对象
2、将该中间对象的原型指向构造函数的原型
3、将构造函数中的this指向该中间对象
4、返回该中间对象,即返回实例对象
现在可以利用apply与construct陷阱函数使其无需new也可以

    function Num(...values){ //构造函数首字母大写 
        if(typeof new.target==='undefined'){
            throw new TypeError("This function must be called with new.");
        }
        this.values=values
    }

    let NumbersProxy=new Proxy(Num,{
        apply(trapTarget,thisArg,argumentList){
            return Reflect.construct(trapTarget,argumentList)//在函数调用的时候执行 construct函数
        }
    })
    let instance = NumbersProxy(1, 2, 3, 4); //未报错
    console.log(instance.values); // [1,2,3,4]

同样的道理可以return new trapTarget(...argumentList);

可被撤销的代理

Proxy.revocable() 方法来创建一个可被撤销的代理,该方法接受的参数与 Proxy 构造器的相同:一个目标对象、一个代理处理器,而返回值是包含下列属性的一个对 象:
proxy :可被撤销的代理对象;
revoke :用于撤销代理的函数。

revoke() 函数被调用后,就不能再对该 proxy 对象进行更多操作

   let target={
       name:'lwj'
   }
   //解构赋值操作
   let{proxy,revoke}=Proxy.revocable(target,{})
   console.log(proxy.name); // "target"
   revoke();
   console.log(proxy.name);//报错

解决一个数组的小问题

     let colors = ["red", "green", "blue"];
     console.log(colors.length); // 3
     colors[3] = "black";
     console.log(colors.length); // 4
     console.log(colors[3]); // "black"
     colors.length = 2;
     console.log(colors.length); // 2
     console.log(colors[3]); // undefined
     console.log(colors[2]); // undefined
     console.log(colors[1]); // "green"

当 colors[3] 被赋值时, length 属性被自动增加到 4 ;
当 length 属性被设置为 2 时,数组的最后两个元素被自动移除了。
当想要重现内置数组的工作方式时,仅需模拟这两个行为即可。

检测数组的索引

数组最多可以包含43亿左右的项(4 294 967 295),然而在JavaScript中数组的索引不能超过一个数,也就是2的32次方-1,只有小于等于这个数才能作为数组的索引(最大的安全整数是2的53次方-1,也就是Number.MAX_SAFE_INTEGER)

     //检索是否符合数组索引 可以则 isArrayIndex(key)返回true
     function toUint32(value) { 
         return Math.floor(Math.abs(Number(value))) % Math.pow(2,32)
     }
     function isArrayIndex(key) {
         let numerickey=toUint32(key)
         return String(numerickey)==key && numerickey<(Math.pow(2,32)-1)
      }

模拟类似的操作

     //检索是否符合数组索引
     function toUint32(value) { 
         return Math.floor(Math.abs(Number(value))) % Math.pow(2,32)
     }
     function isArrayIndex(key) {
         let numerickey=toUint32(key)
         return String(numerickey)==key && numerickey<(Math.pow(2,32)-1)
      }
    
     function createMyArray(length=0){
         //缩写{length:length}
         return new Proxy({length},{
             set(trapTarget,key,value){
                let currentLength = Reflect.get(trapTarget, "length");
                //特殊情况
                if (isArrayIndex(key)){
                    let numericKey = Number(key);
                    if (numericKey >= currentLength) {
                        Reflect.set(trapTarget, "length", numericKey + 1); //未做其他处理,只是递增
                    }
                }
                return Reflect.set(trapTarget, key, value);
             }
         })
     }

     let colors = createMyArray(3); //length:3
     console.log(colors.length); // 3
     colors[0] = "red"; 
     colors[1] = "green";
     colors[2] = "blue";
     console.log(colors); // Proxy {0: "red", 1: "green", 2: "blue", length: 3}
     colors[3] = "black";
     console.log(colors.length); // 4
     console.log(colors[3]); // "black"

其他情况可以自己模拟一下!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • Proxy 概述 Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(met...
    pauljun阅读 3,250评论 0 1
  • Proxy代理是一个共通的概念,可以起到拦截的作用。ES6里将Proxy标准化了,提供了Proxy构造函数,用来生...
    张歆琳阅读 10,152评论 1 7
  • 概述 Proxy 实例的方法 Proxy.revocable() this 问题 实例:Web 服务的客户端 1 ...
    Android_冯星阅读 676评论 0 0
  • 笔记,完全摘录自阮一峰 proxy的概念 proxy英文原意是代理的意思,在ES6中,可以翻译为"代理器"。它主要...
    布蕾布蕾阅读 22,463评论 0 8
  • 时间:2019-06-21 绿地 21:55 日语原声 几个小问题:1.为什么白龙会记得千寻?2.千寻来这里是命中...
    Marsee阅读 203评论 0 0