校招前端面试必问问题之一:vue
双向绑定原理。javascript
wt?
我怎么知道?不是会用就能够了嘛?我管它怎么实现。vue
双向绑定是经过数据劫持实现的,经过劫持对象的 getter
和 setter
实现。Object.defineProperty
来劫持对象属性的 setter
和 getter
操做,当触发 getter
时收集依赖,当触发 setter
时执行一些操做。今天咱们的主角,就是 defineProperty
,以及它的兄弟 proxy
。前端
defineProperty
?从名字上看,能够拆分为 define
和 property
,分别是 定义
和 属性
的意思。因此 defineProperty
是用来定义一个属性的。它接受的参数依次为 obj
、prop
、descriptor
。vue
Object.defineProperty(obj, prop, descriptor)
复制代码
其中第一和第二个参数比较简单,obj
为属性所在的对象,prop
为属性名,常见写法以下java
const o = {}
Object.defineProperty(o, 'key', {
value: 'value'
})
console.log(o) // { key: 'value' }
复制代码
descriptor
: 翻译过来为 属性描述符
,顾名思义,就是用来描述这个属性的详细信息,具体信息以下:es6
configurable
:当且仅当 configurable
为 true
时,该属性描述符才能被改变,同时该属性也能从对应的对象上删除,默认为 false
const o = {}
Object.defineProperty(o, 'name', {
configurable: false,
value: 'name',
});
Object.defineProperty(o, 'age', {
configurable: true,
value: 23,
});
console.log(o); // { name: 'name', age: 23 }
delete o.name
delete o.age
console.log(o); // { name: 'name' } 其中 name 属性没法被删除
复制代码
enumerable
:当且仅当 enumerable
为 true
时,该属性才能出如今对象的枚举属性中,默认为 false
const o = {}
Object.defineProperty(o, 'name', {
enumerable: false,
value: 'name',
});
Object.defineProperty(o, 'age', {
enumerable: true,
value: 23,
});
console.log(Object.keys(o)); // ["age"] 其中 name 不可被枚举
复制代码
value
:属性的初始值,默认为 undefined
const o = {}
Object.defineProperty(o, 'name', {
value: 'name',
});
Object.defineProperty(o, 'age', {});
console.log(o); // { name: 'name', age: undefined }
复制代码
writable
:该属性可否被赋值运算符改变,默认为 false
const o = {}
Object.defineProperty(o, 'name', {
writable: false,
value: 'name',
});
Object.defineProperty(o, 'age', {
writable: true,
value: 23
});
console.log(o); // { name: 'name', age: 23 }
o.name = 'Jim';
o.age = 30;
console.log(o); // { name: 'name', age: 30 }
复制代码
get
:存取描述符之一,给属性提供一个 getter
方法,当访问属性时会被触发。const o = {}
Object.defineProperty(o, 'name', {
get: () => {
console.log('get o.name')
return 'hello'
}
});
console.log(o.name) // 'get o.name' 'hello'
复制代码
set
:存取描述符之一,给属性提供一个 setter
方法,当给属性赋值时被触发。const o = {}
Object.defineProperty(o, 'name', {
get: function() {
console.log('getter');
return this._name
},
set: function(newVal) {
console.log('setter', newVal);
this._name = newVal
}
});
o.name = 10; // 'setter 10'
console.log(o.name); // 'getter' 10
复制代码
proxy
proxy
译为 代理
,能够拦截属性的一些行为来作一些特殊处理,下面为一个简单的例子面试
const _target = {
name: 'Jim',
}
const handler = {
get: (obj, prop) => obj[prop] || 'no value'
}
const target = new Proxy(_target, handler);
console.log(target.name, target.age); // 'Jim' 'no value'
复制代码
proxy
能够拦截十几种行为,下面进行了简单罗列,具体使用方式请 点击查看app
handler.get
:访问属性时触发函数
handler.set
:属性被赋值时触发性能
handler.has
:拦截 in
操做,如 'name' in target
ui
handler.apply
:拦截函数调用,如 target(args)
handler.construct
:拦截 new
操做,如 new Target()
handler.deleteProperty
:拦截 delete
操做,如 delete obj.name
handler.defineProperty
:拦截 defineProperty
操做
defineProterty
和 proxy
的对比defineProterty
是 es5
的标准,proxy
是 es6
的标准;defineProterty
实现双向数据绑定( vue2.x
采用的核心)proxy
实现双向数据绑定( vue3.x
会采用)