在進行實戰(zhàn)之前先簡述一下用到的比較重要的幾個對象:
Object.defineProperties(obj, props)函數(shù) 和 DocumentFragment對象
Object.defineProperties(obj, props):
它是實現(xiàn)數(shù)據(jù)代理最重要的函數(shù),所以理解它的工作原理和使用很總要
參數(shù):
- obj
在其上定義或修改屬性的對象。- props
要定義其可枚舉屬性或修改的屬性描述符的對象。對象中存在的屬性描述符主要有兩種:數(shù)據(jù)描述符和訪問器描述符(更多詳情,請參閱Object.defineProperty())。描述符具有以下鍵:- configurable
true 當且僅當該屬性描述符的類型可以被改變并且該屬性可以從對應對象中刪除。默認為 false- enumerable
true 當且僅當在枚舉相應對象上的屬性時該屬性顯現(xiàn)。默認為 false- value
與屬性關聯(lián)的值。可以是任何有效的JavaScript值(數(shù)字,對象,函數(shù)等)。默認為 undefined.- writable
true當且僅當與該屬性相關聯(lián)的值可以用assignment operator(賦值運算符)改變時。默認為 false返回值: 就是被修改的對象obj
eg1:
var obj = {};
var obj2 = Object.defineProperties(obj, { // 使用一個空對象{}來接收修改的對象
'property1': {
value: true,
writable: true // 可以被賦值操作改變
},
'property2': {
value: 'Hello',
writable: false // 不能被賦值操作改變
}
// etc. etc.
});
console.log(obj2 )
obj2.property2 = 'yes~~' //注意:可以對該屬性進行賦值操作,但不會報錯,誠然也不會修改成功
console.log(obj2 )
(復制以上代碼到調試框便可以看到打印)
其他的參數(shù)大家可以自己試試,很好玩。
然后在配置項中除了這些參數(shù)外還有兩個重要參數(shù):
- get
作為該屬性的 getter 函數(shù),如果沒有 getter 則為undefined。函數(shù)返回值將被用作屬性的值。默認為 undefined- set
作為屬性的 setter 函數(shù),如果沒有 setter 則為undefined。函數(shù)將僅接受參數(shù)賦值給該屬性的新值。默認為 undefined
var obj = {
firstName: 'A',
lastName: 'B'
}
Object.defineProperty(obj, 'fullName', {
//訪問描述符
// 當讀取對象此屬性值時自動調用, 將函數(shù)返回的值作為屬性值, this為obj
get () {
return this.firstName + "-" + this.lastName
},
// 當修改了對象的當前屬性值時自動調用, 監(jiān)視當前屬性值的變化, 修改相關的屬性, this為obj
set (value) {
const names = value.split('-')
this.firstName = names[0]
this.lastName = names[1]
}
})
console.log(obj.fullName) // A-B
obj.fullName = 'C-D'
console.log(obj.firstName, obj.lastName) // C D
(復制以上代碼到調試框便可以看到打印)
再次注意:上面的this指向的是obj,也就是第一個被修改的對象
從上面的打印可以看出,在第一次進行 fullName獲取的時候打印出來是走了get函數(shù)里面的操作,所以打印出來是A-B。
下面是對fullName屬性進行賦值操作,然后走了set函數(shù),在函數(shù)內部是對firstName和lastName進行了修改,所以取值后兩個屬性都跟著更改了。
由上面的函數(shù)可以實現(xiàn)數(shù)據(jù)代理,這個在后面會提到
DocumentFragment
接下來是DocumentFragment: 文檔碎片(高效批量更新多個節(jié)點)
首先我們來了解下document和DocumentFragment最直觀的區(qū)別
document: 對應顯示的頁面, 包含n個elment 一旦更新document內部的某個元素后,界面就會直接更新
documentFragment: 內存中保存n個element的容器對象(不與界面關聯(lián)), 如果更新framgmet中的某個element, 界面不會改變
對于documentFragment記住一點就行了,通過它來進行復雜的節(jié)點操作或創(chuàng)建時,可以避免大量的重繪回流,由此來提高整個頁面的性能。
以下是示例代碼:
const ul = document.getElementById('fragment_test')
// 1. 創(chuàng)建fragment
const fragment = document.createDocumentFragment()
// 2. 取出ul中所有子節(jié)點取出保存到fragment
let child
while(child=ul.firstChild) { // 一個節(jié)點只能有一個父親
fragment.appendChild(child) // 先將child從ul中移除, 添加為fragment子節(jié)點
}
// 3. 更新fragment中所有l(wèi)i的文本
Array.prototype.slice.call(fragment.childNodes).forEach(node => {
if (node.nodeType===1) { // 元素節(jié)點 <li>
node.textContent = 'atguigu'
}
})
// 4. 將fragment插入ul
ul.appendChild(fragment)
上面fragment可以看成是<fragment></fragment> 的節(jié)點對象,但是注意這里fragment并沒有實際意義對于document來說,在添加它到document中的時候只會添加它的子孫節(jié)點,而忽略其本身,它只是一個容器。
在后面的章節(jié)里面會用到此對象來進行界面實時更新,和模板解析
nodeType
節(jié)點類型--在我們編寫HTML的時候,會編寫很多的節(jié)點,如Document,Element,Attr,text等等類型的節(jié)點,那么我們需要判斷我們拿到的節(jié)點是什么類型的節(jié)點時,這個屬性能夠很好的幫助我們去判斷
其中Element的nodeType值是1,Attr是2,text是3,我們常用的也是這三個,不過還有其他的類型如 Comment,Document, DocumentFragment等等,具體可看MDN上的描述
<div id="title">HelloWorld</div>
<script>
window.onload=function(){
var el = document.getElementById('title')
var textNode = el.firstChild
var attrNode = el.getAttributeNode('id')
console.log(el.nodeType,attrNode.nodeType,textNode.nodeType) // 1 2 3
}
</script>
其他文章導航:
前端MVVM理論-MVC和MVP
前端MVVM理論-MVVM
前端MVVM實戰(zhàn)-常用的幾個方法和屬性
前端MVVM實戰(zhàn)-數(shù)據(jù)代理
前端MVVM實戰(zhàn)-模板解析之雙括號解析
前端MVVM實戰(zhàn)-模板解析之事件指令和一般指令
前端MVVM實戰(zhàn)-數(shù)據(jù)綁定(一)
前端MVVM實戰(zhàn)-數(shù)據(jù)綁定(二)