摘要:在上篇文章整體架構(gòu)分析中,我們講過(guò)上面的方法有兩種掛載方式,一個(gè)是掛載到構(gòu)造函數(shù)上以的形式直接調(diào)用在后文上統(tǒng)稱構(gòu)造函數(shù)調(diào)用,另一種則是掛到上以的形式被實(shí)例調(diào)用在后文上統(tǒng)稱原型調(diào)用。
underscore源碼分析之基礎(chǔ)方法
本文是underscore源碼剖析系列的第二篇,主要介紹underscore中一些基礎(chǔ)方法的實(shí)現(xiàn)。
mixin在上篇文章underscore整體架構(gòu)分析中,我們講過(guò)_上面的方法有兩種掛載方式,一個(gè)是掛載到_構(gòu)造函數(shù)上以_.map(arr)的形式直接調(diào)用(在后文上統(tǒng)稱構(gòu)造函數(shù)調(diào)用),另一種則是掛到_.prototype上以_(arr).map()的形式被實(shí)例調(diào)用(在后文上統(tǒng)稱原型調(diào)用)。
翻一遍underscore源碼你會(huì)發(fā)現(xiàn)underscore中的方法都是直接掛到_構(gòu)造函數(shù)上實(shí)現(xiàn)的,但是會(huì)通過(guò)mixin方法來(lái)將_上面的方法擴(kuò)展到_.prototype上面,這樣這些方法既可以直接調(diào)用,又可以通過(guò)實(shí)例來(lái)調(diào)用。
_.mixin = function(obj) { // 遍歷obj上所有的方法 _.each(_.functions(obj), function(name) { // 保存方法的引用 var func = _[name] = obj[name]; _.prototype[name] = function() { // 將一開(kāi)始傳入的值放到數(shù)組中 var args = [this._wrapped]; // 將方法的參數(shù)一起push到數(shù)組中(這里處理的很好,保證了func方法參數(shù)的順序) push.apply(args, arguments); // 這里先用apply方法執(zhí)行了func,并將結(jié)果傳給了result return result(this, func.apply(_, args)); }; }); }; _.mixin(_);
從這段代碼中我們可以看出,mixin方法將_上的所有方法通過(guò)遍歷的形式掛載到了_.prototype上面。
細(xì)心觀察一下,構(gòu)造函數(shù)調(diào)用和原型調(diào)用的區(qū)別在哪里?
沒(méi)錯(cuò),區(qū)別就在于調(diào)用方式和傳參,構(gòu)造函數(shù)調(diào)用時(shí)一般會(huì)把要處理的值當(dāng)做第一個(gè)參數(shù)傳入,而原型調(diào)用的時(shí)候會(huì)把要處理的值傳入_構(gòu)造函數(shù)來(lái)創(chuàng)建一個(gè)實(shí)例。
var arr = [1, 2, 3] var func = function(item) { console.log(item); } // 構(gòu)造函數(shù)調(diào)用時(shí)arr被傳入第一個(gè)參數(shù) _.each(arr, func) // 原型調(diào)用的時(shí)候,arr被當(dāng)做參數(shù)傳給_方法來(lái)創(chuàng)建一個(gè)實(shí)例 _(arr).each(func) // 鏈?zhǔn)秸{(diào)用,和上面類似 _.chain(arr).each(func)
從上一節(jié)中我們知道,在創(chuàng)建一個(gè)_的實(shí)例時(shí),會(huì)用this._wrapped將傳入的值保存起來(lái),所以在mixin里面這一句:var args = [this._wrapped];是將我們傳給_的值放到args數(shù)組第一項(xiàng)中,之后再將arguments也放入args數(shù)組中,借助apply方法執(zhí)行當(dāng)前遍歷的方法(在這個(gè)例子中是each),這個(gè)時(shí)候傳給each方法的是arr和func,正好和原來(lái)直接_.each調(diào)用each傳入?yún)?shù)的順序是一樣的(underscore中的方法第一項(xiàng)基本上都是要處理的數(shù)據(jù))。
鏈?zhǔn)秸{(diào)用那么上面最后return result(this, func.apply(_, args)),result又是做什么的呢?
首先來(lái)看result源碼:
var result = function(instance, obj) { // 首先判斷是否使用鏈?zhǔn)秸{(diào)用,如果是,那就繼續(xù)將剛剛執(zhí)行后返回的結(jié)果鏈?zhǔn)秸{(diào)用一下,如果不是,則直接返回執(zhí)行后的結(jié)果 return instance._chain ? _(obj).chain() : obj; }; _.chain = function(obj) { // 創(chuàng)建一個(gè)實(shí)例 var instance = _(obj); // 給這個(gè)實(shí)例加個(gè)_chain屬性來(lái)表明這是鏈?zhǔn)秸{(diào)用 instance._chain = true; return instance; };
我們知道underscore中也是有和jQuery類似的鏈?zhǔn)秸{(diào)用,來(lái)看一下鏈?zhǔn)秸{(diào)用的例子:
var arr = [1, 2, 3] var newArr = _.chain(a).map(function(item) { return item + 1 }).filter(function(item) { return item > 2 }).value()
鏈?zhǔn)秸{(diào)用的關(guān)鍵在于每次執(zhí)行方法后都需要返回一個(gè)實(shí)例,以確保能夠繼續(xù)調(diào)用其他方法。
chain方法會(huì)用傳入的obj創(chuàng)建一個(gè)_的實(shí)例,這個(gè)實(shí)例可以調(diào)用原型上的方法。從上面mixin的實(shí)現(xiàn)來(lái)看,每次調(diào)用原型方法后會(huì)將執(zhí)行后的結(jié)果傳給result方法,在result內(nèi)部會(huì)判斷你是否使用了鏈?zhǔn)秸{(diào)用(chain),如果是鏈?zhǔn)降?,那么就?huì)將返回結(jié)果鏈?zhǔn)交▊魅隿hain中創(chuàng)建新的實(shí)例)。
鏈?zhǔn)秸{(diào)用一定要在結(jié)尾執(zhí)行value方法,不然最后返回的是一個(gè)對(duì)象(最后一次創(chuàng)建的_實(shí)例)
underscore構(gòu)造方法上面并沒(méi)有直接對(duì)push、pop、shift等數(shù)組方法進(jìn)行實(shí)現(xiàn),但是鏈?zhǔn)秸{(diào)用的時(shí)候往往需要用到這些方法,所以在原型上對(duì)這些方法做了一些封裝,實(shí)現(xiàn)方法和mixin類似,這里不再多做解釋。
_.each(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); // 這句是好像是為了解決ie上的bug? if ((name === "shift" || name === "splice") && obj.length === 0) delete obj[0]; return result(this, obj); }; }); _.each(["concat", "join", "slice"], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { return result(this, method.apply(this._wrapped, arguments)); }; });
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/93519.html
摘要:我這里有個(gè)不夠準(zhǔn)確但容易理解的說(shuō)法,就是檢查一個(gè)對(duì)象是否為另一個(gè)構(gòu)造函數(shù)的實(shí)例,為了更容易理解,下面將全部以是的實(shí)例的方式來(lái)說(shuō)。 underscore源碼分析之整體架構(gòu) 最近打算好好看看underscore源碼,一個(gè)是因?yàn)樽约捍_實(shí)水平不夠,另一個(gè)是underscore源碼比較簡(jiǎn)單,比較易讀。本系列打算對(duì)underscore1.8.3中關(guān)鍵函數(shù)源碼進(jìn)行分析,希望做到最詳細(xì)的源碼分析。今...
摘要:所以它與其他系列的文章并不沖突,完全可以在閱讀完這個(gè)系列后,再跟著其他系列的文章接著學(xué)習(xí)。如何閱讀我在寫(xiě)系列的時(shí)候,被問(wèn)的最多的問(wèn)題就是該怎么閱讀源碼我想簡(jiǎn)單聊一下自己的思路。感謝大家的閱讀和支持,我是冴羽,下個(gè)系列再見(jiàn)啦 前言 別名:《underscore 系列 8 篇正式完結(jié)!》 介紹 underscore 系列是我寫(xiě)的第三個(gè)系列,前兩個(gè)系列分別是 JavaScript 深入系列、...
摘要:遍歷中的所有元素,按順序用遍歷輸出每個(gè)元素。如果傳遞了參數(shù),則把綁定到對(duì)象上。返回以方便鏈?zhǔn)秸{(diào)用。 each _.each(list, iteratee, [context])?Alias:?forEach?遍歷list中的所有元素,按順序用遍歷輸出每個(gè)元素。如果傳遞了context參數(shù),則把iteratee綁定到context對(duì)象上。每次調(diào)用iteratee都會(huì)傳遞三個(gè)參數(shù):(ele...
摘要:直接來(lái)分析返回的匿名函數(shù)部分。我第一次調(diào)用事件函數(shù)是在,按照設(shè)定,之后才能調(diào)用第二次方法,在這秒內(nèi),任何調(diào)用都是不執(zhí)行的。這個(gè)難點(diǎn)解決了,其他就都好說(shuō)。恩,那這個(gè)的解讀就結(jié)束了,有什么地方我沒(méi)寫(xiě)清楚的話,請(qǐng)給我留言。 剛寫(xiě)完一篇debounce(防抖)函數(shù)的實(shí)現(xiàn),我又看了下underscore.js的實(shí)現(xiàn)方法。算是趁熱打鐵,分析一下underscore里實(shí)現(xiàn)的套路。 先貼上源碼: _....
摘要:寫(xiě)在前面專題系列是我寫(xiě)的第二個(gè)系列,第一個(gè)系列是深入系列。專題系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫(xiě)在前面 JavaScript 專題系列是我寫(xiě)的第二個(gè)系列,第一個(gè)系列是 JavaScript 深入系列。 JavaScript 專題系列共計(jì) 20 篇,主要研究日常開(kāi)發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖、節(jié)流、去重、類型判斷、拷貝、最值、扁平、柯里...
閱讀 939·2021-11-23 09:51
閱讀 998·2021-11-18 10:02
閱讀 1935·2021-09-10 11:27
閱讀 3151·2021-09-10 10:51
閱讀 789·2019-08-29 15:13
閱讀 2072·2019-08-29 11:32
閱讀 2507·2019-08-29 11:25
閱讀 3053·2019-08-26 11:46