通过Object.defineProperty来认识/设置
for (let key in obj){
Object.defineProperty(target,key,{
value: obj[key], // 值
writable:true, // 是否可写
enumerable: false,// 是否可遍历
configurable: false, // 是否可删除
get(){ // 注意,定义了这个方法的时候,就不能加value和writable属性
return obj[key]
},
set(val){ // 注意,定义了这个方法的时候,就不能加value和writable属性
console.log(val)
}
})
}
delete target.count;//删除count属性
console.log(target); // 删不掉
所有有时候,我们打印一个对象,里面是没有x属性的,但是你打印obj.x却是有值的,这是因为给这个x属性设置了get方法。 下面是一个使用上的例子,来自vue2源码,巧妙应用了闭包和属性的特性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id='app'>
<div>1</div>
<div>
<h3>姓名</h3>
<p>{{name}}</p>
<h3>年龄</h3>
<p>{{age}}</p>
</div>
</div>
</body>
</html>
<script>
document.addEventListener('DOMContentLoaded', function () {
let obj = { el: '#app', data: { name: '检索中', age: 30 } }
let vm = new Vue(obj)
setTimeout(() => {
obj.data.name = '王永峰'
}, 200);
}, false)
class Vue {
constructor(obj) {
this.obj = obj
this.observe(obj.data)
let root = document.querySelector(obj.el)
this.compile(root)
}
// 为响应式对象 data 里的每一个 key 绑定一个观察者对象
observe(data) {
Object.keys(data).forEach(key => {
let obv = new Observer() // 注意,是每一个属性都注册了一个观察者
data["_" + key] = data[key]
// 通过 getter setter 暴露 for 循环中作用域下的 obv,闭包产生
Object.defineProperty(data, key, {
get() {
console.log('getgetget', data['_' + key], Observer.target)
Observer.target && obv.addSubNode(Observer.target);
return data['_' + key]
},
set(newVal) {
console.log('setsetset', newVal)
obv.update(newVal)
data['_' + key] = newVal
}
})
})
}
// 初始化页面,遍历 DOM,收集每一个key变化时,随之调整的位置,以观察者方法存放起来
compile(node) {
[].forEach.call(node.childNodes, child => {
if (!child.firstElementChild && /\{\{(.*)\}\}/.test(child.innerHTML)) {
// 这个正则匹配的是{{}}括号
let key = RegExp.$1.trim()
console.log('child1', child, key)
child.innerHTML = child.innerHTML.replace(new RegExp('\\{\\{\\s*' + key + '\\s*\\}\\}', 'gm'), this.obj.data[`_${key}`]) // 这样我觉得更合适,不调用get方法
Observer.target = child
this.obj.data[key] // 这里是触发get方法
Observer.target = null
}
else if (child.firstElementChild) {
console.log('child2', child)
this.compile(child)
}
})
}
}
// 常规观察者类
class Observer {
constructor() {
this.subNode = [] // 这个subNode就是订阅了属性的节点们
}
addSubNode(node) {
this.subNode.push(node)
console.log('addnode', this.subNode)
}
update(newVal) {
console.log('this.subNode', this.subNode)
this.subNode.forEach(node => {
node.innerHTML = newVal
})
}
}
</script>