看著文件研究了一下vue的雙向資料繫結,列印出Vue例項下的data物件裡的屬性時,發現了一個有趣的事情:
它的每個屬性都有兩個相對應的get和set方法,我覺的這是多此一舉的,於是去網上查了查Vue雙向繫結的實現原理,才發現它和Angular.js雙向繫結的實現原理完全不同,Angular是用的資料髒檢測,當Model發生變化,會檢測所有檢視是否繫結了相關資料,再更改檢視。而Vue使用的釋出訂閱模式,是點對點的繫結資料。
Vue的資料繫結只有兩個步驟,compile=>link。
我一直在想,vue是通過什麼去監聽使用者對Model的修改,直到我發現Vue的data裡,每個屬性都有set和get屬性,我才明白過來。
在平時,我們建立一個物件,並修改它的屬性,是這樣的:
var obj = { val:99 } obj.val = 100; console.log(obj.val)//100
沒有任何問題,但是如果要你去監測,當我修改了這個物件的屬性時,要去做一些事,你會怎麼做?
這就要用到getter和setter了。
假設我現在要給一個碼農物件新增一個name屬性,而且每次更新name屬性時,我要去完成一些事,我們可以這樣做:
var Coder = function() { var that = this; return { get name(){ if(that.name){ return that.name } return '你還沒有取名' }, set name(val){ console.log('你把名字修成了'+val) that.name = val } } } var isMe = new Coder() console.log(isMe.name) isMe.name = '周神' console.log(isMe.name) console.log(isMe)
輸出:
你會發現這個物件和最上面的Vue中的data物件,列印出來的效果是一樣的,都擁有get和set屬性。
我們來一步步分析下上面的程式碼,很有趣。
我們先建立一個物件字面量:
var Coder = function() {...}
再把this快取一下:
var that = this;
接下來是最重要的,我們return了一個物件回去:
{
get name(){...},
set name(val){...}
}
顧名思義,get為取值,set為賦值,正常情況下,我們取值和賦值是用obj.prop的方式,但是這樣做有一個問題,我如何知道物件的值改變了?所以就輪到set登場了。
你可以把get和set理解為function,當然,只是可以這麼理解,這是完全不一樣的兩個東西。
接下來建立一個碼農的例項,isMe;此時,isMe是沒有name屬性的,當我們呼叫isMe.name時,我們會進入到get name(){...}中,先判斷isMe是否有name屬性,答案是否定的,那麼就新增一個name屬性,並給它賦值:"你還沒有取名";如果有name屬性,那就返回name屬性。
看到這裡你一定知道get怎麼使用了,對,你可以把get看成一個取值的函式,函式的返回值就是它拿到的值。
我感覺比較重要的是set屬性,當我給例項賦值:
isMe.name="周神"
此時,會進入set name(val){...};形參val就是我賦給name屬性的值,在這個函式裡,我就可以做很多事了,比如雙向繫結!因為這個值的每次改變都必須經過set,其他方式是改變不了它的,相當於一個萬能的監聽器。
還有另一種方法可以實現這個功能。
ES5的物件原型有兩個新的屬性__defineGetter__和__defineSetter__,專門用來給物件繫結get和set。可以這樣書寫:
var Coder = function() { } Coder.prototype.__defineGetter__('name', function() { if (this.name) { return this.name }else{ return '你還沒有取名' } }) Coder.prototype.__defineSetter__('name', function(val) { this.name = val }) var isMe = new Coder() console.log(isMe.name) isMe.name = '周神' console.log(isMe.name) console.log(isMe)
效果是一樣的,建議使用下面這種方式,因為是在原型上書寫,所以可以繼承和重用,最近想寫點小框架,才發現知識不夠用,大家一起加油吧!