准备知识点
1、[].slice.call()/Array.prototype.slice.call() 将伪数组转换成真数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="fragment_test">
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>
<script>
const lis = document.getElementsByTagName('li')
console.log(lis instanceof Array, lis[1].innerHTML, lis.forEach) //false 'test2' undefined
const lis2 = Array.prototype.slice.call(lis)
console.log(lis2 instanceof Array, lis2[1].innerHTML, lis2.forEach) //true 'test2' function
</script>
</body>
</html>
2、node.nodeType 得到节点类型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test">哈哈哈</div>
<script>
const elementNode = document.getElementById('test')
const attrNode = elementNode.getAttributeNode('id')
const textNode = elementNode.firstChild
console.log(elementNode.nodeType, attrNode.nodeType, textNode.nodeType) //1 2 3
</script>
</body>
</html>
3、Object.defineProperty(obj, propertyName, {}) 给对象添加属性(指定描述符)ES5语法,不支持IE8
属性描述符:
- 数据描述符
configurable:是否可以重新定义
enumerable:是否可枚举
value:初始值
writable:是否可以修改属性值
- 访问描述符
get:回调函数,根据其它相关的属性动态计算得到当前属性值
set:回调函数,监视当前属性值的变化,更新其它相关的属性值
const obj = {
firstName: 'A',
lastName: 'B'
}
Object.defineProperty(obj, 'fullName', {
configurable: false,
enumerable: true,
get () {
return this.firstName + ' ' + this.lastName
},
set (value) {
const names = value.split(' ')
this.firstName = names[0]
this.lastName = names[1]
}
})
4、Object.keys(obj) 得到自身可枚举属性组成的数组
const names = Object.keys(obj)
console.log(names)
5、obj.hasOwnProperty(prop) 判断prop是否是自身的属性
console.log(obj.hasOwnProperty('fullname'), obj.hasOwnProperty('toString'))
6、DocumentFragment 文档碎片(高效更新多个节点)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test">哈哈哈</div>
<ul id="fragment_test">
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>
<script>
const ul = document.getElementById('fragment_test')
const fragment = document.createDocumentFragment()
let child
while(child=ul.firstChild){ // 一个节点只能有一个父亲
fragment.appendChild(child)
}
Array.prototype.slice.call(fragment.childNodes).forEach(node => {
if (node === 1) { // 元素节点<li>
node.textContent = 'xixixi'
}
})
ul.appendChild(fragment)
</script>
</body>
</html>
数据代理
- 数据代理:通过一个对象代理另一个对象中属性的操作(读/写)
- vue数据代理:通过vm对象来代理data对象中所有属性的操作
- 好处:更方便的操作data中的数据
- 基本实现流程:
- 通过Object.defineProperty()给vm添加与data对象的属性对应的属性描述符
- 所有添加的属性都包含getter/setter
- getter/setter内部去操作data中对应的属性数据
function MVVM (options) {
this.$options = options
var data = this._data = this.$options.data
var me = this
// 实现vm.xxx -> vm._data.xxx
Object.keys(data).forEach(function(key){
me.proxy(key)
})
}
MVVM.prototype = {
_proxy: function(key) {
var me = this
Object.defineProperty(me, key, {
configurable: false,
enumerable: true,
// 当通过vm.xxx读取属性值时可调用,从data中获取对应的属性值返回,代理读操作
get: function proxyGetter () {
return me._data[key]
},
// 当通过vm.xxx = value时,value被保存到data中对应的属性上,代理写操作
set: function proxySetter (value) {
me._data[key] = value
}
})
}
}