成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

underscore 系列之內(nèi)部函數(shù) cb 和 optimizeCb

Zhuxy / 443人閱讀

摘要:類(lèi)似于,但更加健壯和完善。當(dāng)為一個(gè)函數(shù),正常處理。系列系列目錄地址。系列預(yù)計(jì)寫(xiě)八篇左右,重點(diǎn)介紹中的代碼架構(gòu)鏈?zhǔn)秸{(diào)用內(nèi)部函數(shù)模板引擎等內(nèi)容,旨在幫助大家閱讀源碼,以及寫(xiě)出自己的。如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,?qǐng)務(wù)必給予指正,十分感謝。

前言

僅看 cb 和 optimizeCb 兩個(gè)函數(shù)的名字,你可能想不到這是用來(lái)做什么的,盡管你可能想到 cb 是 callback 的縮寫(xiě)。

如果直接講解源碼,你可能想不明白為什么要這么寫(xiě),所以我們從 _.map 函數(shù)開(kāi)始講起。

_.map

_.map 類(lèi)似于 Array.prototype.map,但更加健壯和完善。我們看下 _.map 的源碼:

// 簡(jiǎn)化過(guò),這里僅假設(shè) obj 是數(shù)組
_.map = function (obj, iteratee, context) {
    iteratee = cb(iteratee, context);

    var length = obj.length, results = Array(length);
    for (var index = 0; index < length; index++) {
        results[index] = iteratee(obj[index], index, obj);
    }

    return results;
};

map 方法除了傳入要處理的數(shù)組之外,還有兩個(gè)參數(shù) iteratee 和 context,類(lèi)似于 Array.prototype.map 中的其他兩個(gè)參數(shù),其中 iteratee 表示處理函數(shù),context 表示指定的執(zhí)行上下文,即 this 的值。

然后在源碼中,我們看到,我們將 iteratee 和 context 傳入一個(gè) cb 函數(shù),然后覆蓋掉 iteratee 函數(shù),然后將這個(gè)函數(shù)用作最終的處理函數(shù)。

實(shí)際上,需要這么麻煩嗎?不就是使用 iteratee 函數(shù)處理每次迭代的值嗎?不就是通過(guò) context 指定 this 的值嗎?我們可以直接這樣寫(xiě)吶:

_.map = function (obj, iteratee, context) {
    var length = obj.length, results = Array(length);
    for (var index = 0; index < length; index++) {
        results[index] = iteratee.call(context, obj[index], index, obj);
    }
    return results;
};

// [2, 3, 4]
console.log(_.map([1, 2, 3], function(item){
    return item + 1;
})) 

// [2, 3, 4]
console.log(_.map([1, 2, 3], function(item){
    return item + this.value;
}, {value: 1})) 

你看看也沒(méi)有什么問(wèn)題吶,可是,萬(wàn)一 iteratee 我們不傳入一個(gè)函數(shù)呢?比如我們什么也不傳,或者傳入一個(gè)對(duì)象,又或者傳入一個(gè)字符串、數(shù)字呢?

如果用我們的方法自然是會(huì)報(bào)錯(cuò)的,那 underscore 呢?

// 使用 underscore

// 什么也不傳
var result = _.map([1,2,3]); // [1, 2, 3]

// 傳入一個(gè)對(duì)象
var result = _.map([{name:"Kevin"}, {name: "Daisy", age: 18}], {name: "Daisy"}); // [false, true]

var result = _.map([{name: "Kevin"}, {name: "Daisy"}], "name"); // ["Kevin", "daisy"]

我們會(huì)發(fā)現(xiàn),underscore 竟然還能根據(jù)傳入的值的類(lèi)型不同,實(shí)現(xiàn)的效果不同。我們總結(jié)下:

當(dāng) iteratee 不傳時(shí),返回一個(gè)相同的數(shù)組。

當(dāng) iteratee 為一個(gè)函數(shù),正常處理。

當(dāng) iteratee 為一個(gè)對(duì)象,返回元素是否匹配指定的對(duì)象。

當(dāng) iteratee 為字符串,返回元素對(duì)應(yīng)的屬性值的集合。

由此,我們可以推測(cè)在 underscore 的 cb 函數(shù)中,有對(duì) iteratee 值類(lèi)型的判斷,然后根據(jù)不同的類(lèi)型,返回不同的 iteratee 函數(shù)。

cb

所以我們來(lái)看看 cb 函數(shù)的源碼:

var cb = function(value, context, argCount) {
    
    if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);

    if (value == null) return _.identity;

    if (_.isFunction(value)) return optimizeCb(value, context, argCount);

    if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);

    return _.property(value);
};

這一看就牽扯到了 8 個(gè)函數(shù)!不要害怕,我們一個(gè)一個(gè)看。

_.iteratee
if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);

我們看看 _.iteratee 的源碼:

_.iteratee = builtinIteratee = function(value, context) {
    return cb(value, context, Infinity);
};

因?yàn)?_.iteratee = builtinIteratee 的緣故,_.iteratee !== builtinIteratee 值為 false,所以正常情況下 _.iteratee(value, context) 并不會(huì)執(zhí)行。

但是如果我們?cè)谕獠啃薷牧?_.iteratee 函數(shù),結(jié)果便會(huì)為 true,cb 函數(shù)直接返回 _.iteratee(value, context)。

這個(gè)意思其實(shí)是說(shuō)用我們自定義的 _.iteratee 函數(shù)來(lái)處理 value 和 context。

試想我們并不需要現(xiàn)在 _.map 這么強(qiáng)大的功能,我只希望當(dāng) value 是一個(gè)函數(shù),就用該函數(shù)處理數(shù)組元素,如果不是函數(shù),就直接返回當(dāng)前元素,我們可以這樣修改:



    underscore map


    
    

當(dāng)然更多的情況是自定義對(duì)不同的 value 使用不同的處理函數(shù),值得注意的是,underscore 中的多個(gè)函數(shù)都是用了 cb 函數(shù),而因?yàn)?cb 函數(shù)使用了 _.iteratee 函數(shù),如果你修改這個(gè)函數(shù),其實(shí)會(huì)影響多個(gè)函數(shù),這些函數(shù)基本都屬于集合函數(shù),具體包括 map、find、filter、reject、every、some、max、min、sortBy、groupBy、indexBy、countBy、sortedIndex、partition、和 unique。

_.identity
if (value == null) return _.identity;

讓我們看看 _.identity 的源碼:

_.identity = function(value) {
    return value;
};

這也就是為什么當(dāng) map 的第二個(gè)參數(shù)什么都不傳的時(shí)候,結(jié)果會(huì)是一個(gè)相同數(shù)組的原因。

_.map([1,2,3]); // [1, 2, 3]

如果直接看這個(gè)函數(shù),可能覺(jué)得沒(méi)有什么用,但用在這里,卻又十分的合適。

optimizeCb
if (_.isFunction(value)) return optimizeCb(value, context, argCount);

當(dāng) value 是一個(gè)函數(shù)的時(shí)候,就傳入 optimizeCb 函數(shù),我們來(lái)看看 optimizeCb 函數(shù):

var optimizeCb = function(func, context, argCount) {
    // 如果沒(méi)有傳入 context,就返回 func 函數(shù)
    if (context === void 0) return func;
    switch (argCount) {
        case 1:
            return function(value) {
                return func.call(context, value);
            };
        case null:
        case 3:
            return function(value, index, collection) {
                return func.call(context, value, index, collection);
            };
        case 4:
            return function(accumulator, value, index, collection) {
                return func.call(context, accumulator, value, index, collection);
            };
    }
    return function() {
        return func.apply(context, arguments);
    };
};

也許你會(huì)好奇,為什么我要對(duì) argCount 進(jìn)行判斷呢?就不能直接返回嗎?比如這樣:

var optimizeCb = function(func, context) {
    // 如果沒(méi)有傳入 context,就返回 func 函數(shù)
    if (context === void 0) return func;
    return function() {
        return func.apply(context, arguments);
    };
};

當(dāng)然沒(méi)有問(wèn)題,但為什么 underscore 要這樣做呢?其實(shí)就是為了避免使用 arguments,提高一點(diǎn)性能而已,如果不是寫(xiě)一個(gè)庫(kù),其實(shí)還真是沒(méi)有必要做到這點(diǎn)。

而為什么當(dāng)參數(shù)是 3 個(gè)時(shí)候,參數(shù)名稱(chēng)分別是 value, index, collection ,又為什么沒(méi)有參數(shù)為 2 的情況呢?其實(shí)這都是根據(jù) underscore 函數(shù)用到的情況,沒(méi)有函數(shù)用到兩個(gè)參數(shù),于是就省略了,像 map 函數(shù)就會(huì)用到 3 個(gè)參數(shù),就根據(jù)這三個(gè)參數(shù)的名字起了這里的變量名啦。

_.matcher
if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);

這段就是用來(lái)處理當(dāng) map 的第二個(gè)參數(shù)是對(duì)象的情況:

// 傳入一個(gè)對(duì)象
var result = _.map([{name:"Kevin"}, {name: "Daisy", age: 18}], {name: "Daisy"}); // [false, true]

如果 value 是一個(gè)對(duì)象,并且不是數(shù)組,就使用 _.matcher 函數(shù)??纯锤鱾€(gè)函數(shù)的源碼:

var nativeIsArray = Array.isArray;

_.isArray = nativeIsArray || function(obj) {
    return Object.prototype.toString.call(obj) === "[object Array]";
};

_.isObject = function(obj) {
    var type = typeof obj;
    return type === "function" || type === "object" && !!obj;
};


// extend 函數(shù)可以參考 《JavaScript 專(zhuān)題之手寫(xiě)一個(gè) jQuery 的 extend》
_.matcher = function(attrs) {
    attrs = _.extend({}, attrs);
    return function(obj) {
      return _.isMatch(obj, attrs);
    };
};

// 該函數(shù)判斷 attr 對(duì)象中的鍵值是否在 object 中有并且相等

// var stooge = {name: "moe", age: 32};
// _.isMatch(stooge, {age: 32}); => true

// 其中 _.keys 相當(dāng)于 Object.keys
_.isMatch = function(object, attrs) {
    var keys = _.keys(attrs), length = keys.length;
    if (object == null) return !length;
    var obj = Object(object);
    for (var i = 0; i < length; i++) {
        var key = keys[i];
        if (attrs[key] !== obj[key] || !(key in obj)) return false;
    }
    return true;
};
_.property
return _.property(value);

這個(gè)就是處理當(dāng) value 是基本類(lèi)型的值的時(shí)候,返回元素對(duì)應(yīng)的屬性值的情況:

var result = _.map([{name: "Kevin"}, {name: "Daisy"}], "name"); // ["Kevin", "daisy"]

我們看下源碼:

_.property = function(path) {
    // 如果不是數(shù)組
    if (!_.isArray(path)) {
      return shallowProperty(path);
    }
    return function(obj) {
        return deepGet(obj, path);
    };
};

var shallowProperty = function(key) {
    return function(obj) {
        return obj == null ? void 0 : obj[key];
    };
};

// 根據(jù)路徑取出深層次的值
var deepGet = function(obj, path) {
    var length = path.length;
    for (var i = 0; i < length; i++) {
        if (obj == null) return void 0;
        obj = obj[path[i]];
    }
    return length ? obj : void 0;
};

我們好像發(fā)現(xiàn)了新大陸,原來(lái) value 還可以傳一個(gè)數(shù)組,用來(lái)取深層次的值,舉個(gè)例子:

var person1 = {
    child: {
        nickName: "Kevin"
    }
}

var person2 = {
    child: {
        nickName: "Daisy"
    }
}

var result = _.map([person1, person2], ["child", "nickName"]); 
console.log(result) // ["Kevin", "daisy"]
最后

如果你想學(xué)習(xí) underscore 的源碼,在分析集合相關(guān)的函數(shù)時(shí)一定會(huì)接觸 cb 和 optimizeCb 函數(shù),先掌握這兩個(gè)函數(shù),會(huì)幫助你更好更快的解讀源碼。

underscore 系列

underscore 系列目錄地址:https://github.com/mqyqingfeng/Blog。

underscore 系列預(yù)計(jì)寫(xiě)八篇左右,重點(diǎn)介紹 underscore 中的代碼架構(gòu)、鏈?zhǔn)秸{(diào)用、內(nèi)部函數(shù)、模板引擎等內(nèi)容,旨在幫助大家閱讀源碼,以及寫(xiě)出自己的 undercore。

如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,?qǐng)務(wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎 star,對(duì)作者也是一種鼓勵(lì)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/90089.html

相關(guān)文章

  • underscore 的源碼該如何閱讀?

    摘要:所以它與其他系列的文章并不沖突,完全可以在閱讀完這個(gè)系列后,再跟著其他系列的文章接著學(xué)習(xí)。如何閱讀我在寫(xiě)系列的時(shí)候,被問(wèn)的最多的問(wèn)題就是該怎么閱讀源碼我想簡(jiǎn)單聊一下自己的思路。感謝大家的閱讀和支持,我是冴羽,下個(gè)系列再見(jiàn)啦 前言 別名:《underscore 系列 8 篇正式完結(jié)!》 介紹 underscore 系列是我寫(xiě)的第三個(gè)系列,前兩個(gè)系列分別是 JavaScript 深入系列、...

    weknow619 評(píng)論0 收藏0
  • 窺探Underscore源碼系列-開(kāi)篇

    摘要:他指示了一個(gè)對(duì)象的屬性,返回的將用來(lái)獲得該屬性對(duì)應(yīng)的值在上面的分析中,我們知道,當(dāng)傳入的是一個(gè)函數(shù)時(shí),還要經(jīng)過(guò)一個(gè)叫的內(nèi)置函數(shù)才能獲得最終的所以此處的必然是優(yōu)化回調(diào)的作用了。 開(kāi)篇說(shuō)明 對(duì)的,讓你所見(jiàn),又開(kāi)始造輪子了。哈哈,造輪子我們是認(rèn)真的~ 源碼閱讀是必須的,Underscore是因?yàn)閯倓倢W(xué)習(xí)整理了一波函數(shù)式編程,加上自己曾經(jīng)沒(méi)有太多閱讀源碼的經(jīng)驗(yàn),先拿Underscore練練手,...

    zorpan 評(píng)論0 收藏0
  • 1625行,解開(kāi) underscore.js 的面紗 - 第二章

    摘要:第四個(gè)判斷如果是對(duì)象執(zhí)行返回一個(gè)斷言函數(shù),用來(lái)判定傳入對(duì)象是否匹配指定鍵值屬性。都不匹配最后執(zhí)行,返回傳入的對(duì)象的屬性。設(shè)置的值并生成函數(shù),等同于,使具有屬性且有值則返回,否則返回,這是一個(gè)判斷函數(shù)。 在第二小章節(jié)里面我按照源碼順序介紹幾個(gè)方法,源碼緊接著第一章繼續(xù): var builtinIteratee; builtinIteratee,內(nèi)置的 Iteratee (迭代器)。...

    yuxue 評(píng)論0 收藏0
  • underscore 系列防沖突與 Utility Functions

    摘要:你可以輕松為你的函數(shù)庫(kù)添加防沖突功能。系列系列目錄地址。如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,?qǐng)務(wù)必給予指正,十分感謝。 防沖突 underscore 使用 _ 作為函數(shù)的掛載對(duì)象,如果頁(yè)面中已經(jīng)存在了 _ 對(duì)象,underscore 就會(huì)覆蓋該對(duì)象,舉個(gè)例子: var _ = {value: 1 } // 引入 underscore 后 console.log(_.value); // un...

    qiangdada 評(píng)論0 收藏0
  • underscore源碼學(xué)習(xí)(二)

    摘要:在中,真值檢測(cè)函數(shù)的參數(shù)被命名為,有斷言的意思,非常形象。函數(shù)的功能是檢測(cè)一個(gè)對(duì)象或數(shù)組是否包含指定的某個(gè)元素。 順著underscore源碼的順序讀下來(lái),弄懂了之前underscore的基本結(jié)構(gòu),接下來(lái)看看underscore為我們提供的一些關(guān)于集合的API。 迭代 關(guān)于迭代,我們都知道ES5原生方法也提供了迭代函數(shù)供我們使用,而在underscore中的迭代則是對(duì)原生的迭代函數(shù)進(jìn)行...

    Berwin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<