詳解JavaScript中的鏈?zhǔn)秸{(diào)用
鏈模式
鏈模式是一種鏈?zhǔn)秸{(diào)用的方式,準(zhǔn)確來說不屬于通常定義的設(shè)計模式范疇,但鏈?zhǔn)秸{(diào)用是一種非常有用的代碼構(gòu)建技巧。
描述
鏈?zhǔn)秸{(diào)用在JavaScript語言中很常見,如jQuery、Promise等,都是使用的鏈?zhǔn)秸{(diào)用,當(dāng)我們在調(diào)用同一對象多次其屬性或方法的時候,我們需要多次書寫對象進(jìn)行.或()操作,鏈?zhǔn)秸{(diào)用是一種簡化此過程的一種編碼方式,使代碼簡潔、易讀。
鏈?zhǔn)秸{(diào)用通常有以下幾種實現(xiàn)方式,但是本質(zhì)上相似,都是通過返回對象供之后進(jìn)行調(diào)用。
- this的作用域鏈,jQuery的實現(xiàn)方式,通常鏈?zhǔn)秸{(diào)用都是采用這種方式。
- 返回對象本身, 同this的區(qū)別就是顯示返回鏈?zhǔn)綄ο蟆?/li>
- 閉包返回對象的方式實現(xiàn),這種方式與柯里化有相似之處。
var Person = function() {}; Person.prototype.setAge = function(age){ this.age = age; return this; } Person.prototype.setWeight = function(weight){ this.weight = weight; return this; } Person.prototype.get = function(){ return `{age: ${this.age}, weight: ${this.weight}}`; } var person = new Person(); var des = person.setAge(10).setWeight(30).get(); console.log(des); // {age: 10, weight: 30}
var person = { age: null, weight: null, setAge: function(age){ this.age = age; return this; }, setWeight: function(weight){ this.weight = weight; return this; }, get: function(){ return `{age: ${this.age}, weight: ${this.weight}}`; } }; var des = person.setAge(10).setWeight(30).get(); console.log(des); // {age: 10, weight: 30}
function numsChain(num){ var nums = num; function chain(num){ nums = `${nums} -> ${num}`; return chain; } chain.get = () => nums; return chain; } var des = numsChain(1)(2)(3).get(); console.log(des); // 1 -> 2 -> 3
可選鏈操作符
說到鏈?zhǔn)秸{(diào)用,就有必要說一下JavaScript的可選鏈操作符,屬于ES2020新特性運(yùn)算符?.、??、??=,可選鏈操作符?.允許讀取位于連接對象鏈深處的屬性的值,而不必明確驗證鏈中的每個引用是否有效。?.操作符的功能類似于.鏈?zhǔn)讲僮鞣?,不同之處在于在引用為空nullish即null或者undefined的情況下不會引起錯誤,該表達(dá)式短路返回值是undefined。與函數(shù)調(diào)用一起使用時,如果給定的函數(shù)不存在,則返回undefined。當(dāng)嘗試訪問可能不存在的對象屬性時,可選鏈操作符將會使表達(dá)式更短更簡明。在探索一個對象的內(nèi)容時,如果不能確定哪些屬性必定存在,可選鏈操作符也是很有幫助的。
語法
obj?.prop obj?.[expr] arr?.[index] func?.(args)
示例
const obj = {a: {}}; console.log(obj.a); // {} console.log(obj.a.b); // undefined // console.log(obj.a.b.c); // Uncaught TypeError: Cannot read property 'c' of undefined console.log(obj && obj.a); // {} console.log(obj && obj.a && obj.a.b && obj.a.b.c); // undefined console.log(obj?.a?.b?.c); // undefined const test = void 0; const prop = "a"; console.log(test); // undefined console.log(test?.a); // undefined console.log(test?.[prop]); // undefined console.log(test?.[0]); // undefined console.log(test?.()); // undefined
jQuery中的鏈?zhǔn)秸{(diào)用
jQuery是一個高端而不失奢華的框架,其中有許多非常精彩的方法和邏輯,雖然現(xiàn)在非常流行于類似于Vue、React的MVVM模式的框架,但是jQuery的設(shè)計實在是棒,非常值得學(xué)習(xí),在這里以最基礎(chǔ)的實例化jQuery為例探查一下jQuery如何通過this實現(xiàn)的鏈?zhǔn)秸{(diào)用。
首先定義一個最基本的類,通過原型鏈去繼承方法。
function _jQuery(){} _jQuery.prototype = { constructor: _jQuery, length: 2, size: function(){ return this.length; } } var instance = new _jQuery(); console.log(instance.size()); // 2 // _jQuery.size() // Uncaught TypeError: _jQuery.size is not a function // _jQuery().size() / /Uncaught TypeError: Cannot read property 'size' of undefined
通過定義一個類并且實現(xiàn)實例化之后,在實例之間可以共享原型上的方法,而直接通過_jQuery類直接去調(diào)用顯然是不行的,拋出的第一種異常是因為在_jQuery類上不存在靜態(tài)方法,第二種異常是因為_jQuery作為函數(shù)執(zhí)行后未返回值,通過這里可以看出jQuery在通過$()方式調(diào)用的時候是返回了一個包含多個方法的對象的,而只是通過自己是訪問不到的,我們就借助另一個變量去訪問。
function _jQuery(){ return _fn; } var _fn = _jQuery.prototype = { constructor: _jQuery, length: 2, size: function(){ return this.length; } } console.log(_jQuery().size()); // 2
實際上jQuery為了減少變量的創(chuàng)建,直接將_fn看做了_jQuery的一個屬性。
function _jQuery(){ return _jQuery.fn; } _jQuery.fn = _jQuery.prototype = { constructor: _jQuery, length: 2, size: function(){ return this.length; } } console.log(_jQuery().size()); // 2
到這里確實能夠?qū)崿F(xiàn)_jQuery()方式調(diào)用原型上的方法,但是在jQuery中$()的主要目標(biāo)還是作為選擇器用來選擇元素,而現(xiàn)在返回的是一個_jQuery.fn對象,顯然是達(dá)不到要求的,為了能夠取得返回的元素,那就在原型上定義一個init方法去獲取元素,這里為了省事直接使用了document.querySelector,實際上jQuery的選擇器構(gòu)建是很復(fù)雜的。
function _jQuery(selector){ return _jQuery.fn.init(selector); } _jQuery.fn = _jQuery.prototype = { constructor: _jQuery, init: function(selector){ return document.querySelector(selector); }, length: 3, size: function(){ return this.length; } } console.log(_jQuery("body")); // <body>...</body>
是似乎這樣又把鏈?zhǔn)秸{(diào)用的this給漏掉了,這里就需要利用this的指向了,因為在調(diào)用時this總是指向調(diào)用他的對象,所以我們在這里將選擇的元素掛載到this對象上即可。
function _jQuery(selector){ return _jQuery.fn.init(selector); } _jQuery.fn = _jQuery.prototype = { constructor: _jQuery, init: function(selector){ this[0] = document.querySelector(selector); this.length = 1; return this; }, length: 3, size: function(){ return this.length; } } var body = _jQuery("body"); console.log(body); // {0: body, length: 1, constructor: ƒ, init: ƒ, size: ƒ} console.log(body.size()); // 1 console.log(_jQuery.fn); // {0: body, length: 1, constructor: ƒ, init: ƒ, size: ƒ}
但是此時又出現(xiàn)了一個問題,我們的選擇器選擇的元素是直接掛載到了_jQuery.fn上,這樣的話由于原型是共享的,在之后的定義的選擇器就會將前邊定義的選擇器覆蓋掉,這樣顯然是不行的,于是我們使用new操作符新建一個對象。
function _jQuery(selector){ return new _jQuery.fn.init(selector); } _jQuery.fn = _jQuery.prototype = { constructor: _jQuery, init: function(selector){ this[0] = document.querySelector(selector); this.length = 1; return this; }, length: 3, size: function(){ return this.length; } } var body = _jQuery("body"); console.log(body); // init {0: body, length: 1} // console.log(body.size()); // Uncaught TypeError: body.size is not a function
這樣又出現(xiàn)了問題,當(dāng)我們使用new實例化_jQuery.fn.init時返回的this指向的是_jQuery.fn.init的實例,我們就不能進(jìn)行鏈?zhǔn)秸{(diào)用了,jQuery用了一個非常巧妙的方法解決了這個問題,直接將_jQuery.fn.init的原型指向_jQuery.prototype,雖然會有循環(huán)引用的問題,但是相對來說這一點性能消耗并不算什么,由此我們完成了jQuery選擇器以及鏈?zhǔn)秸{(diào)用的實現(xiàn)。
function _jQuery(selector){ return new _jQuery.fn.init(selector); } _jQuery.fn = _jQuery.prototype = { constructor: _jQuery, init: function(selector){ this[0] = document.querySelector(selector); this.length = 1; return this; }, length: 3, size: function(){ return this.length; } } _jQuery.fn.init.prototype = _jQuery.fn; var body = _jQuery("body"); console.log(body); // init {0: body, length: 1} console.log(body.size()); // 1 console.log(_jQuery.fn.init.prototype.init.prototype.init.prototype === _jQuery.fn); // true
每日一題
https://github.com/WindrunnerMax/EveryDay
以上就是詳解JavaScript中的鏈?zhǔn)秸{(diào)用的詳細(xì)內(nèi)容,更多關(guān)于JavaScript 鏈?zhǔn)秸{(diào)用的資料請關(guān)注腳本之家其它相關(guān)文章!
- JavaScript鏈?zhǔn)秸{(diào)用原理與實現(xiàn)方法詳解
- 《javascript設(shè)計模式》學(xué)習(xí)筆記四:Javascript面向?qū)ο蟪绦蛟O(shè)計鏈?zhǔn)秸{(diào)用實例分析
- 在JavaScript中實現(xiàn)鏈?zhǔn)秸{(diào)用的實現(xiàn)
- JavaScript的級聯(lián)函數(shù)用法簡單示例【鏈?zhǔn)秸{(diào)用】
- JavaScript鏈?zhǔn)秸{(diào)用實例淺析
- javascript簡單鏈?zhǔn)秸{(diào)用案例分析
- 學(xué)習(xí)JavaScript設(shè)計模式(鏈?zhǔn)秸{(diào)用)
- JavaScript DSL 流暢接口(使用鏈?zhǔn)秸{(diào)用)實例
- JS鏈?zhǔn)秸{(diào)用的實現(xiàn)方法
- JavaScript中兩種鏈?zhǔn)秸{(diào)用實現(xiàn)代碼
- javascript中的鏈?zhǔn)秸{(diào)用
- javascript 支持鏈?zhǔn)秸{(diào)用的異步調(diào)用框架Async.Operation
- JavaScript 異步調(diào)用框架 (Part 5 - 鏈?zhǔn)綄崿F(xiàn))
相關(guān)文章
JS 通過系統(tǒng)時間限定動態(tài)添加 select option的實例代碼
這篇文章主要介紹了JS 通過系統(tǒng)時間限定 動態(tài)添加 select option的實例代碼,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-06-06javascript結(jié)合Canvas 實現(xiàn)簡易的圓形時鐘
本文給大家分享的是javascript結(jié)合Canvas 實現(xiàn)簡易的圓形時鐘,主要是對自己前段時間學(xué)習(xí)html5的canvas的一次小檢驗,這里推薦給小伙伴們,有需要的可以參考下。2015-03-03在WordPress中加入Google搜索功能的簡單步驟講解
這篇文章主要介紹了在WordPress中加入Google搜索的簡單步驟講解,谷歌搜索很有用,但同時也要注意在國內(nèi)使用cse的連通性,需要的朋友可以參考下2016-01-01Javascript showModalDialog兩個窗體之間傳值
前一篇文章Javascript怎么在兩個窗體之間傳值中講到了如何利用window.open()方法打開新窗體,并在兩個窗體之間傳值的方法。2009-09-09