es5 (Object.definePropety)
我们先来了解一下Object.defineProperty,这个方法是es5中提出用来限制用户对对象属性与方法的操作。其实在js内部的对象中有很多地方都是限制用户操作的。例如:
var obj = {}
console.log(obj.__proto__);
/* constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__() */
//有这么多的属性 但是。。。
for (var ele in obj.__proto__) {
console.log(ele); // undefined
}
what?? 因为该对象中的属性,不可枚举,这可以通过Objetc.defineProperty设置:
var obj = {
name: 'oldDeng'
}
//设置哪个对象 哪个属性
//Object.defineProperty(obj, 'age', {
//描述符
// value: 18, //属性值
// writable: true, //是否可写
// enumerable: true, //是否可枚举
// configurable: true //是否可配置
//})
//这里要注意只要用defineProperty进行设置的属性三个描述符默认都是false
//例子
Object.defineProperty(obj, 'age', {
//描述符
value: 18, //属性值
});
for (var ele in obj) {
console.log(ele); //只有name
}
obj.age = 22;
console.log(obj.age); // 依然是 18
delete obj.age;
console.log(obj.age); // 18 并没有删除掉
//obj的age属性不可枚举,不可配置(就是删除),不可写
ok,Object.defineProperty可以利用描述符对对象的属性进行相关的配置,是否可枚举(enumerable),是否可写(writable),是否可配置(configurable),
一定要注意三个属性默认都是false,在这里我就不多做实验了。
那这玩意和数据劫持有啥关系呢?当然Object.defineProperty不只有这几个描述符,还有两个最重要的东西,getter,setter。怎么用呢?有啥用呢?上代码
var obj = {
name: 'oldLiu'
}
var value = 18;
Object.defineProperty(obj, 'age', {
get () {
console.log('来读取了');
return value // 这里的返回值就是当你obj.age的值
},
set (newValue) { // 这里接受的参数就是当你obj.age = xx 的 xx
console.log('数据更新了');
value = newValue;
},
//如果你想此属性可配置,可枚举,别忘了enumerable与configurable
//当使用get和set时value与writable不能使用了因为使用get与set就默认此属性可写了,value肯定也是动态给的
enumerable: true,
configurable: true
});
console.log(obj.age) //来读取了 18
obj.age = 22 // 数据更新了 22
ok这就是getter与setter的具体用法,要注意的是,当使用get和set时value与writable不能使用了因为使用getter与setter就默认此属性可写了,value肯定也是动态给的。
每次读取值时get都会执行一次,每次赋值时set都会执行一次,我们就要在这里做文章。
数据劫持
数据劫持听起来好像高大上的样子,其实就是当一个属性值改变时我们可以监听到。
说到数据劫持的应用最经典的肯定时vue的双向数据绑定。我们就来简单的模拟一下:
- html代码部分
<html>
<body>
<input type="text" id="demo" />
<p id="show"></p>
</body>
</html>
css部分就不在这里写了呢。。。
- js部分
- 首先我们要获取两个标签
var oIput = document.getElementById('demo');
var oP = document.getElementById('show');
- 初始化数据
- 要绑定哪个对象
var oData = {
value: 'hi'
}
- 标签绑定相应事件
oInput.oninput = function () {
oData.value = this.value;
}
function upDate () {
oInput.value = oData.value;
oP.innerHtml = oData.value;
}
upDate();
- 监控函数
function Observer (data) {
//如果要监控的属性的层级觉深可以递归
if (!data || typeof data !== 'object') return data;
//便利对象的key进行监控
Object.keys(data).forEach(function (key) {
watchValue(data, key, data[key]);
});
}
function watchValue (data, key, val) {
//先监控一下 如果val是引用值则递归下去
Observer(val)
Object.defineProperty(data, key, {
get () {
return val;
},
set (newVal) {
// 两次的值相等则不更新
if (val === newVal) return;
val = newVal
upDate()
}
})
}
Observer(oData)
ok,这样数据劫持就做完了,当然只是简单的模拟,效果还是ok的可检测对象的深层属性,不过这个方法对数组的pop呀.push这些方法,没法检测,不过那就不在今天的讨论范围之内了。
gitHub地址:https://github.com/lifei5859/Observer.git
demo展示地址:http://www.fgdemoshow.cn/smallDemo/js/Observer