監(jiān)控屬性、監(jiān)控?cái)?shù)組

在javascript的世界里尾序,屬性和數(shù)組是構(gòu)成對(duì)象的基本元素,也是數(shù)據(jù)結(jié)構(gòu)的基本元素躯砰,因此knockout針對(duì)屬性和數(shù)組進(jìn)行了動(dòng)態(tài)的監(jiān)控每币,也是靠著這個(gè)特性完成了MVVM的流程。

創(chuàng)建一個(gè)View Models

只需要聲明任意的JavaScript object字面量形式或者構(gòu)造函數(shù)形式均可琢歇。例如:

var myViewModel = {
    personName: 'Bob',
    personAge: 123
};

你可以為view model創(chuàng)建一個(gè)聲明式綁定的簡(jiǎn)單view兰怠。例如:下面的代碼顯示personName 值:

The name is <span data-bind="text: personName"></span>

完成以上兩部并不能完成MVVM梦鉴,需要將html的內(nèi)容與viewmode綁定起來(lái),代碼如下:

ko.applyBindings(myViewModel);

你可能奇怪ko.applyBindings使用的是什么樣的參數(shù),

  • 第一個(gè)參數(shù)是你想用于聲明式綁定
  • 第二個(gè)參數(shù)(可選)揭保,可以聲明成使用data-bind的HTML元素或者容器肥橙。例如, ko.applyBindings(myViewModel, document.getElementById('someElementId'))秸侣。它的現(xiàn)在是只有作為someElementId 的元素和子元素才能激活KO功能存筏。 好處是你可以在同一個(gè)頁(yè)面聲明多個(gè)view model,用來(lái)區(qū)分區(qū)域味榛。

激活綁定需要注意一下幾點(diǎn):

  1. 每個(gè)dom節(jié)點(diǎn)只能綁定一個(gè)viewmodel椭坚,該節(jié)點(diǎn)的內(nèi)部也不能再次綁定任何viewmodel.
  2. 上述給出的例子沒(méi)有第二個(gè)參數(shù),那么這個(gè)viewmode就綁定了整個(gè)body上搏色,這個(gè)是默認(rèn)的善茎。那么這種情況在這個(gè)頁(yè)面中就只能存在一個(gè)viewmodel。
  3. 雖然頁(yè)面只能存在一個(gè)viewmodel频轿,但是viewmodel里面可以綁定組件巾表。

監(jiān)控屬性(Observables)

現(xiàn)在已經(jīng)知道如何創(chuàng)建一個(gè)簡(jiǎn)單的view model并且通過(guò)binding顯示它的屬性了。但是KO一個(gè)重要的功能是當(dāng)你的view model改變的時(shí)候能自動(dòng)更新你的界面略吨。當(dāng)你的view model部分改變的時(shí)候KO是如何知道的呢?答案是:你需要將你的model屬性聲明成observable的, 因?yàn)樗欠浅L厥獾腏avaScript objects考阱,能夠通知訂閱者它的改變以及自動(dòng)探測(cè)到相關(guān)的依賴翠忠。
例如:將上述例子的view model改成如下代碼:

var myViewModel = {
    personName: ko.observable('Bob'),
    personAge: ko.observable(123)
};

你根本不需要修改view – 所有的data-bind語(yǔ)法依然工作,不同的是他能監(jiān)控到變化乞榨,當(dāng)值改變時(shí)秽之,view會(huì)自動(dòng)更新。

監(jiān)控屬性(observables)的讀和寫(xiě)

不是所有的瀏覽器都支持JavaScript的 getters and setters (比如IE),吃既,所以為了兼容性考榨,使用ko.observable監(jiān)控的對(duì)象都是真實(shí)的function函數(shù)。

  • 讀取監(jiān)控屬性(observable)的值鹦倚,只需要直接調(diào)用監(jiān)控屬性(observable)(不需要參數(shù))河质,例如myViewModel.personName() 將返回'Bob', myViewModel.personAge() 將返回 123。
  • 寫(xiě)一個(gè)新值到監(jiān)控屬性(observable)上震叙,調(diào)用這個(gè)observable屬性并當(dāng)新值作為參數(shù)掀鹅。例如:調(diào)用 myViewModel.personName('Mary') 將更新name值為'Mary'。
  • 給一個(gè)model對(duì)象的多個(gè)屬性寫(xiě)入新值媒楼,你可以使用鏈?zhǔn)秸Z(yǔ)法乐尊。例如: myViewModel.personName('Mary').personAge(50) 將會(huì)將name更新為 'Mary' 并且 將age更新為 50.

監(jiān)控屬性(observables)的特征就是監(jiān)控(observed),例如其它代碼可以說(shuō)我需要得到對(duì)象變化的通知划址,所以KO內(nèi)部有很多內(nèi)置的綁定語(yǔ)法扔嵌。所以如果你的代碼寫(xiě)成data-bind="text: personName"限府, text綁定注冊(cè)到自身,一旦personName的值改變痢缎,它就能得到通知胁勺。
當(dāng)然調(diào)用myViewModel.personName('Mary')改變name的值,text綁定將自動(dòng)更新這個(gè)新值到相應(yīng)的DOM元素上牺弄。這就是如何將view model的改變傳播到view上的姻几。

監(jiān)控屬性(Observables)的顯式訂閱

通常情況下,你不用手工訂閱势告,所以新手可以忽略此小節(jié)蛇捌。高級(jí)用戶,如果你要注冊(cè)自己的訂閱到監(jiān)控屬性(observables)咱台,你可以調(diào)用它的subscribe 函數(shù)络拌。例如:

myViewModel.personName.subscribe(function (newValue) {
    alert("The person's new name is " + newValue);
});

這個(gè)subscribe 函數(shù)在內(nèi)部很多地方都用到的。你也可以終止自己的訂閱:首先得到你的訂閱回溺,然后調(diào)用這個(gè)對(duì)象的dispose函數(shù)春贸,例如:

var subscription = myViewModel.personName.subscribe(function (newValue) { /* do stuff */ });
// ...then later...
subscription.dispose(); // I no longer want notifications

大多數(shù)情況下,你不需要做這些遗遵,因?yàn)閮?nèi)置的綁定和模板系統(tǒng)已經(jīng)幫你做好很多事情了萍恕,可以直接使用它們。
如果要在可更改即將更改之前通知其值车要,則可以訂閱beforeChange事件允粤。 例如:

myViewModel.personName.subscribe(function(oldValue) {
    alert("The person's previous name is " + oldValue);
}, null, "beforeChange");

注意:Knockout不保證beforeChange和change事件成對(duì)出現(xiàn),因?yàn)榇a的其他部分可能會(huì)單獨(dú)引發(fā)任一事件翼岁。 如果您需要跟蹤observable的先前值类垫,則由您使用訂閱來(lái)捕獲和跟蹤它。

強(qiáng)制觀察者總是通知訂閱者

當(dāng)寫(xiě)入包含原始值(數(shù)字琅坡,字符串悉患,布爾值或null)的observable時(shí),通常只有在值實(shí)際改變時(shí)才通知observable的依賴關(guān)系榆俺。 但是售躁,可以使用內(nèi)置的notify擴(kuò)展器來(lái)確保observable的訂閱者總是在寫(xiě)入時(shí)通知,即使值是相同的茴晋。 您可以將擴(kuò)展器應(yīng)用于可觀察者迂求,如下所示:

myViewModel.personName.extend({ notify: 'always' });

延遲和/或抑制更改通知

通常,可觀察者立即通知其訂戶晃跺,只要它改變揩局。 但是如果一個(gè)observable重復(fù)更改或觸發(fā)昂貴的更新,您可以通過(guò)限制或延遲observable的更改通知獲得更好的性能掀虎。 這是使用rateLimit擴(kuò)展器實(shí)現(xiàn)這樣凌盯,其中rateLimit為延遲的參數(shù)付枫,單位毫秒:

// Ensure it notifies about changes no more than once per 50-millisecond period
myViewModel.personName.extend({ rateLimit: 50 });

監(jiān)控?cái)?shù)組

如果你要探測(cè)和響應(yīng)一個(gè)對(duì)象的變化,你應(yīng)該用observables驰怎。如果你需要探測(cè)和響應(yīng)一個(gè)集合對(duì)象的變化阐滩,你應(yīng)該用observableArray 。在很多場(chǎng)景下县忌,它都非常有用掂榔,比如你要在UI上需要顯示/編輯的一個(gè)列表數(shù)據(jù)集合,然后對(duì)集合進(jìn)行添加和刪除症杏。

var myObservableArray = ko.observableArray();    // Initially an empty array
myObservableArray.push('Some value');            // Adds the value and notifies observers

關(guān)鍵點(diǎn):監(jiān)控?cái)?shù)組跟蹤的是數(shù)組里的對(duì)象装获,而不是這些對(duì)象自身的狀態(tài)。

簡(jiǎn)單說(shuō)厉颤,將一對(duì)象放在observableArray 里不會(huì)使這個(gè)對(duì)象本身的屬性變化可監(jiān)控的穴豫。當(dāng)然你自己也可以聲明這個(gè)對(duì)象的屬性為observable的,但它就成了一個(gè)依賴監(jiān)控對(duì)象了逼友。一個(gè)observableArray 僅僅監(jiān)控他擁有的對(duì)象精肃,并在這些對(duì)象添加或者刪除的時(shí)候發(fā)出通知。

預(yù)加載一個(gè)監(jiān)控?cái)?shù)組observableArray

如果你想讓你的監(jiān)控?cái)?shù)組在開(kāi)始的時(shí)候就有一些初始值帜乞,那么在聲明的時(shí)候司抱,你可以在構(gòu)造器里加入這些初始對(duì)象。例如:

// This observable array initially contains three objects
var anotherObservableArray = ko.observableArray([
    { name: "Bungle", type: "Bear" },
    { name: "George", type: "Hippo" },
    { name: "Zippy", type: "Unknown" }
]);

從observableArray里讀取信息

一個(gè)observableArray其實(shí)就是一個(gè)observable的監(jiān)控對(duì)象黎烈,只不過(guò)他的值是一個(gè)數(shù)組(observableArray還加了很多其他特性习柠,稍后介紹)。所以你可以像獲取普通的observable的值一樣怨喘,只需要調(diào)用無(wú)參函數(shù)就可以獲取自身的值了。 例如振定,你可以像下面這樣獲取它的值:

alert('The length of the array is ' + myObservableArray().length);
alert('The first element is ' + myObservableArray()[0]);

理論上你可以使用任何原生的JavaScript數(shù)組函數(shù)來(lái)操作這些數(shù)組必怜,但是KO提供了更好的功能等價(jià)函數(shù),他們非常有用是因?yàn)椋?/p>

  1. 兼容所有瀏覽器后频。(例如indexOf不能在IE8和早期版本上使用梳庆,但KO自己的indexOf 可以在所有瀏覽器上使用)
  2. 在數(shù)組操作函數(shù)方面(例如push和splice),KO自己的方式可以自動(dòng)觸發(fā)依賴跟蹤卑惜,并且通知所有的訂閱者它的變化膏执,然后讓UI界面也相應(yīng)的自動(dòng)更新。
  3. 語(yǔ)法更方便露久,調(diào)用KO的push方法更米,只需要這樣寫(xiě):myObservableArray.push(...)。 比如原生數(shù)組的myObservableArray().push(...)好用多了毫痕。

下面講解的均是observableArray的讀取和寫(xiě)入的相關(guān)函數(shù)征峦。

indexOf

indexOf 函數(shù)返回的是第一個(gè)等于你參數(shù)數(shù)組項(xiàng)的索引迟几。例如:myObservableArray.indexOf('Blah')將返回以0為第一個(gè)索引的第一個(gè)等于Blah的數(shù)組項(xiàng)的索引。如果沒(méi)有找到相等的栏笆,將返回-1类腮。

slice

slice函數(shù)是observableArray相對(duì)于JavaScript 原生函數(shù)slice的等價(jià)函數(shù)(返回給定的從開(kāi)始索引到結(jié)束索引之間所有的對(duì)象集合)。 調(diào)用myObservableArray.slice(...)等價(jià)于調(diào)用JavaScript原生函數(shù)(例如:myObservableArray().slice(...))蛉加。

操作observableArray

observableArray 展現(xiàn)的是數(shù)組對(duì)象相似的函數(shù)并通知訂閱者的功能蚜枢。
pop, push, shift, unshift, reverse, sort, splice
所有這些函數(shù)都是和JavaScript數(shù)組原生函數(shù)等價(jià)的,唯一不同的數(shù)組改變可以通知訂閱者:

    myObservableArray.push('Some new value') 在數(shù)組末尾添加一個(gè)新項(xiàng)

    myObservableArray.pop() 刪除數(shù)組最后一個(gè)項(xiàng)并返回該項(xiàng)

    myObservableArray.unshift('Some new value') 在數(shù)組頭部添加一個(gè)項(xiàng)

    myObservableArray.shift() 刪除數(shù)組頭部第一項(xiàng)并返回該項(xiàng)

    myObservableArray.reverse() 翻轉(zhuǎn)整個(gè)數(shù)組的順序

    myObservableArray.sort() 給數(shù)組排序

默認(rèn)情況下针饥,是按照字符排序(如果是字符)或者數(shù)字排序(如果是數(shù)字)厂抽。
你可以排序傳入一個(gè)排序函數(shù)進(jìn)行排序,該排序函數(shù)需要接受2個(gè)參數(shù)(代表該數(shù)組里需要比較的項(xiàng))打厘,如果第一個(gè)項(xiàng)小于第二個(gè)項(xiàng)修肠,返回-1,大于則返回1户盯,等于返回0嵌施。例如:用lastname給person排序,你可以這樣寫(xiě):

myObservableArray.sort (function (left, right) {
  return left.lastName == right.lastName? 0: (left.lastName < right.lastName? -1: 1)
});

myObservableArray.splice() 刪除指定開(kāi)始索引和指定數(shù)目的數(shù)組對(duì)象元素莽鸭。
例如myObservableArray.splice(1, 3) 從索引1開(kāi)始刪除3個(gè)元素(第2,3,4個(gè)元素)然后將這些元素作為一個(gè)數(shù)組對(duì)象返回吗伤。
更多observableArray 函數(shù)的信息,請(qǐng)參考等價(jià)的JavaScript數(shù)組標(biāo)準(zhǔn)函數(shù)硫眨。

remove和removeAll

observableArray 添加了一些JavaScript數(shù)組默認(rèn)沒(méi)有但非常有用的函數(shù):

  • myObservableArray.remove(someItem) 刪除所有等于someItem的元素并將被刪除元素作為一個(gè)數(shù)組返回
  • myObservableArray.remove(function(item) { return item.age < 18 }) 刪除所有age屬性小于18的元素并將被刪除元素作為一個(gè)數(shù)組返回
  • myObservableArray.removeAll(['Chad', 132, undefined]) 刪除所有等于'Chad', 123, or undefined的元素并將被刪除元素作為一個(gè)數(shù)組返回
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末足淆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子礁阁,更是在濱河造成了極大的恐慌巧号,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姥闭,死亡現(xiàn)場(chǎng)離奇詭異丹鸿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)棚品,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)靠欢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人铜跑,你說(shuō)我怎么就攤上這事门怪。” “怎么了锅纺?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵掷空,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)拣帽,這世上最難降的妖魔是什么疼电? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮减拭,結(jié)果婚禮上蔽豺,老公的妹妹穿的比我還像新娘。我一直安慰自己拧粪,他們只是感情好修陡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著可霎,像睡著了一般魄鸦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上癣朗,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天拾因,我揣著相機(jī)與錄音,去河邊找鬼旷余。 笑死绢记,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的正卧。 我是一名探鬼主播蠢熄,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼炉旷!你這毒婦竟也來(lái)了签孔?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤窘行,失蹤者是張志新(化名)和其女友劉穎饥追,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體罐盔,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡但绕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了翘骂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壁熄。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡帚豪,死狀恐怖碳竟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狸臣,我是刑警寧澤莹桅,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響诈泼,放射性物質(zhì)發(fā)生泄漏懂拾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一铐达、第九天 我趴在偏房一處隱蔽的房頂上張望岖赋。 院中可真熱鬧,春花似錦瓮孙、人聲如沸唐断。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)脸甘。三九已至,卻和暖如春偏灿,著一層夾襖步出監(jiān)牢的瞬間丹诀,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工翁垂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铆遭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓沮峡,卻偏偏與公主長(zhǎng)得像疚脐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子邢疙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理棍弄,服務(wù)發(fā)現(xiàn),斷路器疟游,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • 前言 人生苦多呼畸,快來(lái) Kotlin ,快速學(xué)習(xí)Kotlin颁虐! 什么是Kotlin蛮原? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,146評(píng)論 9 118
  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說(shuō)閱讀 10,869評(píng)論 6 13
  • 本篇文章介主要紹RxJava中操作符是以函數(shù)作為基本單位,與響應(yīng)式編程作為結(jié)合使用的另绩,對(duì)什么是操作儒陨、操作符都有哪些...
    嘎啦果安卓獸閱讀 2,837評(píng)論 0 10
  • 明天就要高考啦!寫(xiě)了一篇有高三的笋籽,祝各位學(xué)子金榜題名蹦漠! 想寫(xiě)個(gè)連載的,覺(jué)得還不錯(cuò)的請(qǐng)點(diǎn)贊評(píng)論车海,阿龜需要你們的支持笛园!...
    李小龜變仙女閱讀 174評(píng)論 0 0