响应式原理是Vue的核心思想,vue2.x使用的是Object.defineProperty(),3.x使用了Proxy代理。本文主要描述了defineProperty和Proxy的使用原理和特点。
defineProperty
名词解释
define: 定义
property: 属性
defineProperty: 定义属性的方法
用法
Object.defineProperty(obj, prop, description)
function defineProperty(){
const _obj = {};
Object.defineProperty(_obj, 'a', {
value: 22
})
return _obj;
}
const obj = defineProperty()
console.log(obj);
function defineProperty2(){
const _obj = {};
Object.defineProperties(_obj, {
a:{
value: 22222
},
b:{
value: 666
}
})
return _obj;
}
const obj = defineProperty2()
直接使用value定义的属性,不可修改 不可枚举 不可删除
function defineProperty2(){
const _obj = {};
Object.defineProperties(_obj, {
a:{
value: 1
},
b:{
value:2
}
})
return _obj;
}
const obj = defineProperty2()
obj.a = 555;
console.log(obj); //不可修改
for(var k in obj){
console.log(obj[k]); //不可枚举
}
delete obj.a; //不可删除
console.log(obj);
defineProperty的配置项
function defineProperty2(){
const _obj = {};
Object.defineProperties(_obj, {
a:{
value: 1,
writable: true, //默认为false
enumerable: true, //默认false
configurable: true //默认false 是否可删除
},
b:{
value:2
}
})
return _obj;
}
const obj = defineProperty2()
obj.a = 555;
console.log('修改后', obj); //不可修改
for(var k in obj){
console.log('枚举', obj[k]); //不可枚举
}
delete obj.a; //不可删除
console.log('删除后', obj);
defineProperty的set和get
function defineProperty(){
const _obj = {};
var a =1;
Object.defineProperties(_obj, {
a:{
get(){
console.log('get', a);
return a;
},
set(newVal){
a = newVal;
console.log('set', a);
}
}
})
return _obj;
}
const ee = defineProperty()
注意:set get不能和valve writable一起用,一般都像上面这样,将值从外面复用进来,里面只写set get !
使用案例一:利用property实现一个对对象赋值,就可以添加到数组中的方法
function dataArray(){
var _val = null,
_arr = [];
Object.defineProperty(this, 'val', {
get(){
return _val;
},
set(newVal){
_val = newVal;
_arr.push({'val': _val})
}
});
this.getArr = ()=>{
return _arr;
}
}
var arr = new dataArray();
arr.val =1111;
arr.val =22222;
console.log(arr.getArr());
defineProperty的特点
1.defineProperty 是js比较底层的能力 官方应该是不太建议去使用的,之所以有它是为了弥补js在枚举,可写等的缺陷 对属性的控制不够彻底;
2.proxy是大的方向
3.defineProperty还有一个类似于bug的特点 就是:set get不能和valve writable一起用
案例二:demo-用defineProperty实现一个计算器
github.com/lijieJack/C…
defineProperty 数据劫持 给对象扩展属性 属性进行设置
proxy
proxy对对象、数组、方法的代理
// let obj = new Proxy(target, handler)
// target 目标对象 要处理的对象
// handler 容器 无数可以处理对象属性的方法
// 作用: 自定义对象属性的获取 赋值 枚举 函数调用等功能
const target = {
a:1,
b: 2
}
let proxy = new Proxy(target, {
get(target, prop){
console.log('this property:' + target[prop]);
return target[prop];
},
set(target, prop, value){
target[prop] = value;
console.log('set ', target[prop]);
}
})
console.log(target.a);
console.log(proxy.a);
proxy.b =3
// proxy可以代理 对象 数组 方法
// 代理数组
const arr = [
{name: 'sss', age:18},
{name: 'sss', age:18},
{name: 'sss', age:18},
{name: 'sss', age:18}
]
const arrProxy = new Proxy(arr, {
get(arr, prop){
console.log('prop:',prop);
return arr[prop];
},
set(arr, prop, value){
arr[prop] = value;
}
})
console.log('arrProxy[1]', arrProxy[5]);
arrProxy[2] = {bb:111}
console.log('arrProxy', arrProxy, 'arr', arr);
//代理方法
function func (){
console.log(111);
}
func.a = 888;
const funcProxy = new Proxy(func,{
get(func, prop){
return func[prop]
},
set(func, prop, value){
func[prop] = value
}
})
console.log(funcProxy.a);
funcProxy.a = 7777
console.log(funcProxy.a);
console.log('func', func, 'funcProxy', funcProxy);
Proxy实现的剖析
Proxy是浏览器基本的api实现,已有很好的兼容性。目前主要IE不支持,我们使用Object.defineProiperty() 来实现一个Proxy
//手动实现一个Proxy
// Proxy 是浏览器自带的api功能 当浏览器不支持时 如IE, 可以自己手动构建实现一个
function myProxy(target, handle) {
const _target = deepClone(target);
Object.keys(_target).forEach((key)=>{
console.log('key', key);
Object.defineProperty(_target, key, {
get(){
console.log('defineProperty get:', key);
return handle.get && handle.get(target, key);
},
set(newVal){
handle.set && handle.set(target, key, newVal);
}
})
})
return _target;
// 深拷贝
function deepClone(org, tar){
tar = tar || {};
for(let key in org){
if(org.hasOwnProperty(key)){
if( typeof org[key] === 'object' && org[key]!==null ){
if(Object.prototype.toString.call(org[key]) === '[object Array]'){
//是数组
tar[key] = [];
}else{
tar[key] = {};
}
deepClone(org[key], tar[key])
}else{
tar[key] = org[key]
}
}
}
return tar;
}
}
const target = {
a: {
s:333
},
b: 2
}
const proxyTarget = new myProxy(target, {
get(target, prop){
console.log('proxy target:',target, prop);
console.log(222222222);
return target[prop]
},
set(target, prop, value){
target[prop] = value;
}
})
Proxy其他可操作的方法
除了get set 还有其他如:has,deleteProperty等共十四种操作对象的方法。
defineProperty 与 Proxy的对比:
defineProperty原则上是给对象添加属性时使用的,在修改数组的长度,用索引去设置元素的值的时候 ,数组的push pop时 是无法触发defineProperty的set方法的。vue2.0对数组的操作是vue自己重新写的,而不是原生写的,这样导致vue2.0的代码很重。
Proxy 没有这个问题,功能更加强大。
defineProperty是拦截
Proxy是代理
为什么在vue2.0没有使用proxy?
Proxy是ES6的语法 是一个构造函数,当时考虑到兼容性 在2.x版本就没用Proxy 3.0时 es6的支持更好,Vue在代码的编译过程中做了代码的转换,proxy转化为DefineProperty,解决兼容问题。Proxy自身支持IE9以上。
在2.x中data:
data(){
return {
}
}
设计为函数返回值的形式是为了:
1.防止引用的时候被篡改
2.直接返回一个对象 这个对象直接用来做defineProperty处理
3.x中
data:{
}
是用来被代理操作的 不直接操作它,故不用函数返回值的形式。
使用Reflect
使用Reflect实现 函数式的方法操作 而不是,声明式的操作,会更方便维护和使用。
Reflect的好处:
1.js受原来一切都是对象影响,很多方法挂在Object(object.prototype.constract)上设计不完善.很多方法将移到Reflect。
2.Reflect有函数操作的返回值,true false更方便操作
3.兼容性:es6
4.handle中的方法都可以用Reflect实现。
5.全局可用。
下面是Reflect下面的方法:
总结
本文分别描述了Vue双向绑定的核心:Object.defineProperty()和Proxy的用法,如何手动自己去实现一个Proxy,对两者进行了对比,最好描述了使用Reflect的一些好处。
如果对你有帮助 欢迎交流~
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!