OR proxy data for data hijacking of research references ------------

 

   Data hijacking, also known as proxy data.

   The so-called data hijacking, means that when you access or modify a property of an object, a piece of code to intercept this behavior, perform additional operations or modify returned results. More typical is Object.defineProperty () and ES2015 in the new Proxy objects. Another has been abandoned Object.observe (), abandoned reason is the emergence of Proxy, so here we will not continue this discussion has been deleted the browser method.

The most famous application data hijacking comes as two-way binding, which has been discussed is a rotten interview p3. For example Vue 2.x using Object.defineProperty () (Vue Proxy instead be realized after the 3.x version). Furthermore in order to ensure immer.js immutable attribute data used to block the Proxy routine modification operation, an application data is hijacked.

Let's look at the pros and cons of these two methods respectively.

       

Object.defineProperty

Vue two-way binding has been upgraded to the front end of the interview will be questions, principle I will not repeat it, a large online. It is simply the use of Object.defineProperty (), and the decoupling of the internal Observer, Dep, and use the connected Watcher.

Object.defineProperty () There are three main problems:

   

You can not monitor the changes in the array

   Look at the following code:

        

let arr = [1,2,3]
let obj = {}
Object.defineProperty(obj, 'arr', {
 get () {
   console.log('get arr')
   return arr
 },
 set (newVal) {
   console.log('set', newVal)
   arr = newVal
 }
})
obj.arr.push(4) // 只会打印 get arr, 不会打印 set
obj.arr = [1,2,3,4] // 这个能正常 set

  The following method will not trigger an array of set:

  • push

  • pop

  • shift

  • unshift

  • splice

  • sort

  • reverse

Vue these methods defined mutation method (mutation method), it refers to a method modifies the original array. The corresponding non-variant methods (non-mutating method), e.g. filter, concat, slice, etc., they do not modify the original array, and returns a new array. Vue official website of the relevant documents about this problem.

Vue practice is to rewrite these methods to achieve the hijacking of an array. A minimalist achieve the following:

const aryMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
const arrayAugmentations = [];
aryMethods.forEach((method)=> {
 // 这里是原生 Array 的原型方法
 let original = Array.prototype[method];
 // 将 push, pop 等封装好的方法定义在对象 arrayAugmentations 的属性上
 // 注意:是实例属性而非原型属性
 arrayAugmentations[method] = function () {
   console.log('我被改变啦!');
   // 调用对应的原生方法并返回结果
   return original.apply(this, arguments);
 };
});
let list = ['a', 'b', 'c'];
// 将我们要监听的数组的原型指针指向上面定义的空数组对象
// 这样就能在调用 push, pop 这些方法时走进我们刚定义的方法,多了一句 console.log
list.__proto__ = arrayAugmentations;
list.push('d');  // 我被改变啦!
// 这个 list2 是个普通的数组,所以调用 push 不会走到我们的方法里面。
let list2 = ['a', 'b', 'c'];
list2.push('d');  // 不输出内容
You must traverse the object of each property

Use Object.defineProperty () to be most fitting Object.keys () and traverse, so multi-layer nested. Such as:

Object.keys(obj).forEach(key => {
 Object.defineProperty(obj, key, {
   // ...
 })
})
You must be traversed deep nested object

The so-called nested objects, refer to similar

let obj = {
 info: {
   name: 'eason'
 }
}

If this type of nested objects, you must traverse the layer by layer, until each property of each object are calling Object.defineProperty () so far. Vue's source code to find such logic (called the walk method).

Proxy

Proxy was officially added to the ES2015 specification, its support, although not as Object.defineProperty (), but in fact the basic support (in addition to a few other IE and Opera Mini browser, data from caniuse), so the problem is unlikely to use Big.

For objects

In the data hijacking this problem, Proxy can be considered Object.defineProperty () upgrade. Outside access to an object, must go through this layer interception. So it is for the  entire object , rather than  a property of the object , so there is no need for keys to traverse. This solves the Object.defineProperty () second question.

let obj = {
 name: 'Eason',
 age: 30
}
let handler = {
 get (target, key, receiver) {
   console.log('get', key)
   return Reflect.get(target, key, receiver)
 },
 set (target, key, value, receiver) {
   console.log('set', key, value)
   return Reflect.set(target, key, value, receiver)
 }
}
let proxy = new Proxy(obj, handler)
proxy.name = 'Zoe' // set name Zoe
proxy.age = 18 // set age 18

As the code, Proxy against the obj. So no matter how many internal obj contains key, you can be walked into the set. (Save a Object.keys () traversal)

Also this Reflect.get and Reflect.set can be understood as class inheritance in the super, that is, call the original method. Detailed Reflect can see here, it will not be expanded.

Support arrays
let arr = [1,2,3]
let proxy = new Proxy(arr, {
   get (target, key, receiver) {
       console.log('get', key)
       return Reflect.get(target, key, receiver)
   },
   set (target, key, value, receiver) {
       console.log('set', key, value)
       return Reflect.set(target, key, value, receiver)
   }
})
proxy.push(4)

Proxy does not require an array of methods overloaded, eliminating the need for numerous hack, reduce the amount of code is equivalent to reducing maintenance costs, and the standard is the best.

Nested Support

Essentially, Proxy also does not support nested this point and Object.defineProperty () is the same. Therefore, it needs to be addressed by layer by layer traversal. Proxy wording is get inside and return the Proxy recursive call, as follows:

let obj = {
 info: {
   name: 'eason',
   blogs: ['webpack', 'babel', 'cache']
 }
}
let handler = {
 get (target, key, receiver) {
   console.log('get', key)
   // 递归创建并返回
   if (typeof target[key] === 'object' && target[key] !== null) {
     return new Proxy(target[key], handler)
   }
   return Reflect.get(target, key, receiver)
 },
 set (target, key, value, receiver) {
   console.log('set', key, value)
   return Reflect.set(target, key, value, receiver)
 }
}
let proxy = new Proxy(obj, handler)
// 以下两句都能够进入 set
proxy.info.name = 'Zoe'
proxy.info.blogs.push('proxy')
Other differences

    In addition to the above two points, Proxy also has the following advantages:

  • Proxy second parameter can have 13 kinds of interception method, which compared Object.defineProperty () is much richer

  • Proxy by focusing on performance and optimization of browser vendors as a new standard, compared Object.defineProperty () is an existing old method.

   This second advantage stems from it is a new standard. But the new standard also has disadvantages, that is:

  • Proxy compatibility is better Object.defineProperty () (caniuse data show, QQ browser and the browser does not support Proxy Baidu, which is the domestic mobile development is not expected to be accepted, but both support Object.defineProperty ())

  • You can not be used to handle the compatibility polyfill

These comparisons only for the purposes of "hijacking of data to achieve" this demand. Object.defineProperty () In addition to defining the get and set, but also for other functions, so even without considering the compatibility of the situation, I do not mean a completely eliminated another.

application

Talk about the technology itself without talking about scenarios are basically bullying. Only those with a technical application scenarios, really valuable.

As said at the beginning, data hijacking often seen inside the frame, such as Vue, immer and the like, but these seem far removed from our ordinary programmers. Aside from these, I have listed a few possible scenarios, you may be able to think more in daily work.

A face questions

In fact, in addition to reading the data binding source outside the Vue, the second time I had about this technology in the developer community is small burst of fire through a strange topic:

What kind of a meet (a === 1 && a === 2 && a === 3) === true it? (Note that 3 =, which is strictly equal)

Since it is a strictly equal, what type conversion is basically not considered. A natural idea is that each time you access the value of a return are not the same, then surely think of data hijacking. (There may be other solution, but here they talk about data hijacking method)

let current = 0
Object.defineProperty(window, 'a', {
 get () {
   current++
   return current
 }
})
console.log(a === 1 && a === 2 && a === 3) // true

Use Proxy can, but because the syntax Proxy is to return a new object, so to do a === 1 may be more difficult, so obj.a === 1 or OK, anyway, the principle is the same, also do not tangle too much.

Multiple Inheritance

Javascript inheritance through prototype chain, a normal object (or class) can inherit an object (or class). But these two methods can realize a black science and technology, it allows an object to inherit two objects. The following example uses Proxy implemented.

let foo = {
 foo () {
   console.log('foo')
 }
}
let bar = {
 bar () {
   console.log('bar')
 }
}
// 正常状态下,对象只能继承一个对象,要么有 foo(),要么有 bar()
let sonOfFoo = Object.create(foo);
sonOfFoo.foo();     // foo
let sonOfBar = Object.create(bar);
sonOfBar.bar();     // bar
// 黑科技开始
let sonOfFooBar = new Proxy({}, {
 get (target, key) {
   return target[key] || foo[key] || bar[key];
 }
})
// 我们创造了一个对象同时继承了两个对象,foo() 和 bar() 同时拥有
sonOfFooBar.foo();   // foo 有foo方法,继承自对象foo
sonOfFooBar.bar();   // bar 也有bar方法,继承自对象bar
Hide private variables
function getObject(rawObj, privateKeys) {
 return new Proxy(rawObj, {
   get (target, key, receiver) {
     if (privateKeys.indexOf(key) !== -1) {
       throw new ReferenceError(`${key} 是私有属性,不能访问。`)
     }
     return target[key]
   }
 })
}
let rawObj = {
 name: 'Zoe',
 age: 18,
 isFemale: true
}
let obj = getObject(rawObj, ['age'])
console.log(obj.name) // Zoe
console.log(obj.age) // 报错
When the check object properties set

If there are certain properties of the object type requires only accept the value of a particular type, namely by Proxy we can give an error when setting, rather than in the use of re-unified recursively traverse checking. So in terms of the efficiency in the use or friendliness are better.

let person = {
 name: 'Eason',
 age: 30
}
let handler = {
 set (target, key, value, receiver) {
   if (key === 'name' && typeof value !== 'string') {
     throw new Error('用户姓名必须是字符串类型')
   }
   if (key === 'age' && typeof value !== 'number') {
     throw new Error('用户年龄必须是数字类型')
   }
   return Reflect.set(target, key, value, receiver)
 }
}
let personForUser = new Proxy(person, handler)
personForUser.name = 'Zoe' // OK
personForUser.age = '18' // 报错
All kinds of fault tolerance check

We often sends a request to the rear end, and waits for a response in response to data processing, and the code robustness for, there is usually a lot of determination, such as:

// 发送请求代码省略,总之获取到了 response 对象了。
if (!response.data) {
 console.log('响应体没有信息')
 return
} else if (!response.data.message) {
 console.log('后端没有返回信息')
 return
} else if (!response.data.message.from || !response.data.message.text) {
 console.log('后端返回的信息不完整')
 return
} else {
 console.log(`你收到了来自 ${response.data.message.from} 的信息:${response.data.message.text}`)
}

The essence of the code is to get response.data.message.from and response.data.message.text, but need hierarchy to determine otherwise, JS will error.

We can consider the use of Proxy to transform the code to make it a little prettier. '

// 故意设置一个错误的 data1,即 response.data = undefined
let response = {
 data1: {
   message: {
     from: 'Eason',
     text: 'Hello'
   }
 }
}
/ 也可以根据 key 的不同给出更友好的提示
let dealError = key => console.log('Error key', key)
let isOK = obj => !obj['HAS_ERROR']
let handler = {
 get (target, key, receiver) {
   // 基本类型直接返回
   if (target[key] !== undefined && typeof target[key] !== 'object') {
     return Reflect.get(target, key, receiver)
   }
   // 如果是 undefined,把访问的的 key 传递到错误处理函数 dealError 里面
   if (!target[key]) {
     if (!target['HAS_ERROR']) {
       dealError(key)
     }
     return new Proxy({HAS_ERROR: true}, handler)
   }
   // 正常的话递归创建 Proxy
   return new Proxy(target[key], handler)
 }
}
let resp = new Proxy(response, handler)
if (isOK(resp.data.message.text) && isOK(resp.data.message.from)) {
 console.log(`你收到了来自 ${response.data.message.from} 的信息:${response.data.message.text}`)
}

Because we deliberately set response.data = undefined, and therefore will enter dealError methods, parameters, key value data.

Although the amount of codes longer than the above point of view if inspection, but isOK, handler is defined and new Proxy can be multiplexed and to be moved to a separate file, only a few methods can be exposed. Therefore, the actual code is only defined dealError and the last one if only.

More application scenarios

Set Object Defaults - creates an object, it comes with a default value of certain properties.

  • Optimized enumerated types - enumeration type key error immediately when an error instead of silently return undefined, due to coding errors caused rewrite, delete, etc. can also be intercepted.

  • Track changes in objects and arrays - throw event when an element of the array and object / property changes. This may apply to undo, redo, or directly back to a history of the state.

  • Property access to the object increase the cache to improve the speed - the recorded value of a property when the object is set to return directly access the property without actually visit. Checking mechanism to increase TTL (Time To Live, survival time) to prevent memory leaks.
    Key words in support of an array - by setting method has the internal call array.includes. When used directly console.log ( 'key' in someArr) .

 

 Example single-mode - construct by providing a method, in performing the same new operator always returns a single embodiment, thus creating a single embodiment mode.

Cookie type conversion - document.cookie is used; string divided. We can put it into an object, and re-define settings and delete operations are set by Proxy and deleteProperty, Foreign exposed to an operational Cookie objects, easy to use.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin www.cnblogs.com/zhouyideboke/p/11804252.html