随着前端界的空前繁荣,各种框架横空出世,包括各类mvvm框架百家争鸣,比如Anglar、vue、react等等,它们最大的优点就是可以实现数据绑定,再也不需要手动进行dom操作了,它们实现的原理也基本上是脏检查或数据劫持。我们先以Vue框架出发,探索其中数据劫持的奥秘。
Vue 2.0的版本所使用的数据劫持,说白了就是通过Object.defineProperty()来劫持对象属性的setter和getter操作,在数据变动时做你想要做的事情,举个栗子:
var data = {
name:'xiaoming'
}
Object.keys(data).forEach(function(key){
Object.defineProperty(data,key,{
get:function(){
console.log('get');
},
set:function(){
console.log('监听到数据发生了变化');
}
})
});
data.name //控制台会打印出 “get”
data.name = 'xiaohong' //控制台会打印出 "监听到数据发生了变化"
但是有没有比Object.defineProperty更好的实现方式呢?答案是肯定的有,那就是我们今天的主人公:Proxy
Proxy这个词的原意是代理,用在这里表示由它来代理某些操作,可以译为代理器。
也可以理解成在目标对象之前设置一层拦截,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
在生活中,代理模式的场景是十分常见的,例如我们现在如果有购买海外产品(给女朋友买一个LV的包包,前提是你要先有个女朋友,^_^)的需求,更多的是去找代购中介机构,而不是直接去国外买。此时,代购起到的作用就是代理的作用。
Proxy构造函数能够让我们轻松的使用代理模式:
var proxy = new Proxy(target, handler);
Proxy构造函数中有两个参数:
target是用Proxy包装的被代理对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler是一个对象,其声明了代理target 的一些操作,其属性是当执行一个操作时定义代理的行为的函数。
讲的通俗点,如何让代购帮你买LV的包包呢?
首先,你需要告诉代购你看好了哪款包包,这个款式就是Proxy里的第一个参数target。其次就是制定购买策略,例如国外比国内便宜20%,就买2个,便宜40%,就买4个,这个策略就是第二个参数handle。
Proxy有13种数据劫持的操作,那是相当的强大:
2.1 get方法
get方法是在你得到某对象属性值时预处理的方法,接受两个常用参数
可以代购来模拟handle中的get方法,如下
var Bao = {
name: "LV",
price:9999,
};
var proxyBao = new Proxy(Bao, {
get: function(target, key) {
if (target['price']>5000) {
return '超出客户心理价位,不买了';
} else {
return '符合客户心理预期,买买买';
}
}
});
proxyBao.price
//"超出客户心理价位,不买了"
解释一下:客户想买一个LV的包,心理价位是5000,把购买目标和需求都告诉了代购,代购询问了下国外的价格,这款LV的包是9999,超出了客户的心理价位,于是不买了。
2.2 set方法
set方法用来拦截某个属性的赋值操作,可以接受四个参数
假定Person对象有一个age属性,该属性应该是一个不大于 200 的整数,那么可以使用Proxy保证age的属性值符合要求。
let validator = {
set: function(target, key, value) {
if (key === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于满足条件的 age 属性以及其他属性,直接保存
target[key] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // 报错 The age is not an integer
person.age = 300 // 报错 The age seems invalid
上面代码中,由于设置了存值函数set,任何不符合要求的age属性赋值,都会抛出一个错误,这是数据验证的一种实现方法。
3.1 支持数组
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)
// 能够打印出很多内容
// get push (寻找 proxy.push 方法)
// get length (获取当前的 length)
// set 3 4 (设置 proxy[3] = 4)
// set length 4 (设置 proxy.length = 4)
Proxy 不需要对数组的方法进行重载,省去了众多 hack,减少代码量等于减少了维护成本,而且标准的就是最好的。
3.2 针对对象
在数据劫持这个问题上,Proxy 可以被认为是 Object.defineProperty() 的升级版。外界对某个对象的访问,都必须经过这层拦截。因此它是针对 整个对象,而不是 对象的某个属性,所以也就不需要对 keys 进行遍历。
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
3.3 嵌套支持
本质上,Proxy 也是不支持嵌套的,这点和 Object.defineProperty() 是一样的。因此也需要通过逐层遍历来解决。Proxy 的写法是在 get 里面递归调用 Proxy 并返回,代码如下:
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')
4.1 使用Proxy实现表单校验
let person = {
name: 'xiaoming',
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 boy = new Proxy(person, handler)
boy.name = 'xiaohong' // OK
boy.age = '18' // 报错 用户年龄必须是数字类型
Proxy本质上属于元编程非破坏性数据劫持,在原对象的基础上进行了功能的衍生而又不影响原对象,符合松耦合高内聚的设计理念。
通俗的说Proxy在数据外层套了个壳,然后通过这层壳访问内部的数据,就像下面的图:
Proxy让JS开发者很方便的使用代理模式,使函数更加强大,业务逻辑更加清楚。
Proxy 不但可以取代 Object.defineProperty 并且还扩增了非常多的功能。Proxy 技术支持监测数组的 push 等方法操作,支持对象属性的动态添加和删除,极大的简化了响应化的代码量。Vue 3.0的也会使用Proxy去实现部分核心代码。
在业务开发时应该注意Proxy使用场景,当对象的功能变得复杂或者我们需要进行一定的访问限制时,便可以考虑使用代理。
数据劫持即使用Object.defineProperty()实现了vue的双向绑定。先来看看它是如何实现的;proxy即代理的意思。个人理解,建立一个proxy代理对象(Proxy的实例),接受你要监听的对象和监听它的handle两个参数。
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
HTTPS通过加密传输数据来保护浏览器和网站之间的安全交互,从而防止ISP和政府对传输进行读取和篡改。服务器通过提供由证书颁发机构(CA)进行数字签名的证书来验证自己身份,而证书颁发机构就是被浏览器信任、能担保网站身份的一个实体
如何检查自己的网站是不是被劫持了?IIS7网站监控,测网站是否被劫持、DNS污染检测等信息。被劫持的网站,当打开网址的时候,会跳转到其他的网址,造成用户无法正常的访问
百度搜索流量事件是网站劫持的其中一种表现。网站劫持还会导致以下问题:用户输入正常网址跳转到其它地址,导致用户无法正常访问,网站流量受损;通过泛域名解析生成大量子域名共同指向其它地址,跳转到非法网站,造成网站权重降低;
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!