亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Underscore源碼解析(四)

高勝山 / 2324人閱讀

摘要:本文同步自我得博客我在這個(gè)系列的第一篇文章說(shuō)過(guò),我學(xué)是為了在學(xué)的時(shí)候少一些阻礙,從第一篇的寫(xiě)作時(shí)間到今天,大概也有個(gè)十幾二十天,感覺(jué)拖得有點(diǎn)久,所以今天將會(huì)是源碼解析系列的最后一篇文章,我會(huì)在這篇文章中介紹剩下的所有函數(shù)。

本文同步自我得博客:http://www.joeray61.com

我在這個(gè)系列的第一篇文章說(shuō)過(guò),我學(xué)underscore是為了在學(xué)backbone的時(shí)候少一些阻礙,從第一篇的寫(xiě)作時(shí)間到今天,大概也有個(gè)十幾二十天,感覺(jué)拖得有點(diǎn)久,所以今天將會(huì)是underscore源碼解析系列的最后一篇文章,我會(huì)在這篇文章中介紹underscore剩下的所有函數(shù)。
先附上前三篇文章的地址:Underscore源碼解析(一)、Underscore源碼解析(二)、Underscore源碼解析(三)

_.zip
_.zip = function() {
    // 將參數(shù)轉(zhuǎn)換為數(shù)組, 此時(shí)args是一個(gè)二維數(shù)組
    var args = slice.call(arguments);
    // 計(jì)算每一個(gè)數(shù)組的長(zhǎng)度, 并返回其中最大長(zhǎng)度值
    var length = _.max(_.pluck(args, "length"));
    // 依照最大長(zhǎng)度值創(chuàng)建一個(gè)新的空數(shù)組, 該數(shù)組用于存儲(chǔ)處理結(jié)果
    var results = new Array(length);
    // 循環(huán)最大長(zhǎng)度, 在每次循環(huán)將調(diào)用pluck方法獲取每個(gè)數(shù)組中相同位置的數(shù)據(jù)(依次從0到最后位置)
    // 將獲取到的數(shù)據(jù)存儲(chǔ)在一個(gè)新的數(shù)組, 放入results并返回
    for(var i = 0; i < length; i++)
    results[i] = _.pluck(args, "" + i);
    // 返回的結(jié)果是一個(gè)二維數(shù)組
    return results;
};

這個(gè)函數(shù)將每個(gè)數(shù)組的相同位置的數(shù)據(jù)作為一個(gè)新的二維數(shù)組返回, 返回的數(shù)組長(zhǎng)度以傳入?yún)?shù)中最大的數(shù)組長(zhǎng)度為準(zhǔn), 其它數(shù)組的空白位置使用undefined填充。zip函數(shù)應(yīng)該包含多個(gè)參數(shù), 且每個(gè)參數(shù)應(yīng)該均為數(shù)組。

_.indexOf
_.indexOf = function(array, item, isSorted) {
    if(array == null)
        return -1;
    var i, l;
    // 若數(shù)組已經(jīng)經(jīng)過(guò)排序,則調(diào)用sortedIndex方法,獲取元素插入數(shù)組中所處位置的索引號(hào)
    if(isSorted) {
        i = _.sortedIndex(array, item);
        return array[i] === item ? i : -1;
    }
    // 優(yōu)先調(diào)用宿主環(huán)境提供的indexOf方法
    if(nativeIndexOf && array.indexOf === nativeIndexOf)
        return array.indexOf(item);
    // 循環(huán)并返回元素首次出現(xiàn)的位置
    for( i = 0, l = array.length; i < l; i++)
    if( i in array && array[i] === item)
        return i;
    // 沒(méi)有找到元素, 返回-1
    return -1;
}; 

這個(gè)函數(shù)的作用是搜索一個(gè)元素在數(shù)組中首次出現(xiàn)的位置, 如果元素不存在則返回 -1,搜索時(shí)使用 === 對(duì)元素進(jìn)行匹配

_.lastIndexOf
_.lastIndexOf = function(array, item) {
    if(array == null)
        return -1;
    // 優(yōu)先調(diào)用宿主環(huán)境提供的lastIndexOf方法
    if(nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf)
        return array.lastIndexOf(item);
    var i = array.length;
    // 循環(huán)并返回元素最后出現(xiàn)的位置
    while(i--)
    if( i in array && array[i] === item)
        return i;
    // 沒(méi)有找到元素, 返回-1
    return -1;
};

這個(gè)函數(shù)返回一個(gè)元素在數(shù)組中最后一次出現(xiàn)的位置, 如果元素不存在則返回 -1,搜索時(shí)使用 === 對(duì)元素進(jìn)行匹配

_.range
_.range = function(start, stop, step) {
    // 參數(shù)控制
    if(arguments.length <= 1) {
        // 如果沒(méi)有參數(shù), 則start = 0, stop = 0, 在循環(huán)中不會(huì)生成任何數(shù)據(jù), 將返回一個(gè)空數(shù)組
        // 如果有1個(gè)參數(shù), 則參數(shù)指定給stop, start = 0
        stop = start || 0;
        start = 0;
    }
    // 生成整數(shù)的步長(zhǎng)值, 默認(rèn)為1
    step = arguments[2] || 1;

    // 根據(jù)區(qū)間和步長(zhǎng)計(jì)算將生成的最大值
    var len = Math.max(Math.ceil((stop - start) / step), 0);
    var idx = 0;
    var range = new Array(len);

    // 生成整數(shù)列表, 并存儲(chǔ)到range數(shù)組
    while(idx < len) {
        range[idx++] = start;
        start += step;
    }

    // 返回列表結(jié)果
    return range;
};

這個(gè)函數(shù)根據(jù)區(qū)間和步長(zhǎng), 生成一系列整數(shù), 并作為數(shù)組返回,start參數(shù)表示最小數(shù),stop參數(shù)表示最大數(shù),step參數(shù)表示步長(zhǎng)

_.bind
_.bind = function bind(func, context) {
    var bound, args;
    // 優(yōu)先調(diào)用宿主環(huán)境提供的bind方法
    if(func.bind === nativeBind && nativeBind)
        return nativeBind.apply(func, slice.call(arguments, 1));
    // func參數(shù)必須是一個(gè)函數(shù)(Function)類型
    if(!_.isFunction(func))
        throw new TypeError;
    // args變量存儲(chǔ)了bind方法第三個(gè)開(kāi)始的參數(shù)列表, 每次調(diào)用時(shí)都將傳遞給func函數(shù)
    args = slice.call(arguments, 2);
    return bound = function() {
        if(!(this instanceof bound))
            return func.apply(context, args.concat(slice.call(arguments)));
        ctor.prototype = func.prototype;
        var self = new ctor;
        var result = func.apply(self, args.concat(slice.call(arguments)));
        if(Object(result) === result)
            return result;
        return self;
    };
};

這個(gè)函數(shù)為一個(gè)函數(shù)綁定執(zhí)行上下文, 任何情況下調(diào)用該函數(shù), 函數(shù)中的this均指向context對(duì)象,綁定函數(shù)時(shí), 可以同時(shí)給函數(shù)傳遞調(diào)用形參

_.bindAll
_.bindAll = function(obj) {
    // 第二個(gè)參數(shù)開(kāi)始表示需要綁定的函數(shù)名稱
    var funcs = slice.call(arguments, 1);
    // 如果沒(méi)有指定特定的函數(shù)名稱, 則默認(rèn)綁定對(duì)象本身所有類型為Function的屬性
    if(funcs.length == 0)
        funcs = _.functions(obj);
    // 循環(huán)并將所有的函數(shù)上下本設(shè)置為obj對(duì)象本身
    // each方法本身不會(huì)遍歷對(duì)象原型鏈中的方法, 但此處的funcs列表是通過(guò)_.functions方法獲取的, 它已經(jīng)包含了原型鏈中的方法
    each(funcs, function(f) {
        obj[f] = _.bind(obj[f], obj);
    });
    return obj;
};

這個(gè)函數(shù)將指定的函數(shù), 或?qū)ο蟊旧淼乃泻瘮?shù)上下本綁定到對(duì)象本身, 被綁定的函數(shù)在被調(diào)用時(shí), 上下文對(duì)象始終指向?qū)ο蟊旧?/p> _.memoize

_.memoize = function(func, hasher) {
    // 用于存儲(chǔ)緩存結(jié)果的memo對(duì)象
    var memo = {};
    // hasher參數(shù)應(yīng)該是一個(gè)function, 它用于返回一個(gè)key, 該key作為讀取緩存的標(biāo)識(shí)
    // 如果沒(méi)有指定key, 則默認(rèn)使用函數(shù)的第一個(gè)參數(shù)作為key, 如果函數(shù)的第一個(gè)參數(shù)是復(fù)合數(shù)據(jù)類型, 可能會(huì)返回類似[Object object]的key, 這個(gè)key可能會(huì)造成后續(xù)計(jì)算的數(shù)據(jù)不正確
    hasher || ( hasher = _.identity);
    // 返回一個(gè)函數(shù), 該函數(shù)首先通過(guò)檢查緩存, 再對(duì)沒(méi)有緩存過(guò)的數(shù)據(jù)進(jìn)行調(diào)用
    return function() {
        var key = hasher.apply(this, arguments);
        return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
    };
};

這個(gè)函數(shù)將返回一個(gè)函數(shù), 該函數(shù)集成了緩存功能, 將經(jīng)過(guò)計(jì)算的值緩存到局部變量并在下次調(diào)用時(shí)直接返回

_.delay
_.delay = function(func, wait) {
    var args = slice.call(arguments, 2);
    // 通過(guò)setTimeout來(lái)延時(shí)執(zhí)行
    return setTimeout(function() {
        return func.apply(null, args);
    }, wait);
};

這個(gè)函數(shù)的作用是延時(shí)執(zhí)行一個(gè)函數(shù),wait單位為ms, 第3個(gè)參數(shù)開(kāi)始將被依次傳遞給執(zhí)行函數(shù)

_.defer
_.defer = function(func) {
    // 相當(dāng)于_.delay(func, 1, [arguments]);
    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};

這個(gè)函數(shù)的作用是延遲1ms執(zhí)行函數(shù),javascript是一個(gè)單線程的程序,setTimeout(func, time)作用是把func放到處理任務(wù)的隊(duì)列末尾,在其他任務(wù)都完成之后的time ms 后執(zhí)行func

_.throttle
_.throttle = function(func, wait) {
    var context, args, timeout, throttling, more, result;
    // whenDone變量調(diào)用了debounce方法, 因此在多次連續(xù)調(diào)用函數(shù)時(shí), 最后一次調(diào)用會(huì)覆蓋之前調(diào)用的定時(shí)器, 清除狀態(tài)函數(shù)也僅會(huì)被執(zhí)行一次
    // whenDone函數(shù)在最后一次函數(shù)執(zhí)行的時(shí)間間隔截止時(shí)調(diào)用, 清除節(jié)流和調(diào)用過(guò)程中記錄的一些狀態(tài)
    var whenDone = _.debounce(function() {
        more = throttling = false;
    }, wait);
    // 返回一個(gè)函數(shù), 并在函數(shù)內(nèi)進(jìn)行節(jié)流控制
    return function() {
        // 保存函數(shù)的執(zhí)行上下文和參數(shù)
        context = this;
        args = arguments;
        // later函數(shù)在上一次函數(shù)調(diào)用時(shí)間間隔截止時(shí)執(zhí)行
        var later = function() {
            // 清除timeout句柄, 方便下一次函數(shù)調(diào)用
            timeout = null;
            // more記錄了在上一次調(diào)用至?xí)r間間隔截止之間, 是否重復(fù)調(diào)用了函數(shù)
            // 如果重復(fù)調(diào)用了函數(shù), 在時(shí)間間隔截止時(shí)將自動(dòng)再次調(diào)用函數(shù)
            if(more)
                func.apply(context, args);
            // 調(diào)用whenDone, 用于在時(shí)間間隔后清除節(jié)流狀態(tài)
            whenDone();
        };
        // timeout記錄了上一次函數(shù)執(zhí)行的時(shí)間間隔句柄
        // timeout時(shí)間間隔截止時(shí)調(diào)用later函數(shù), later中將清除timeout, 并檢查是否需要再次調(diào)用函數(shù)
        if(!timeout)
            timeout = setTimeout(later, wait);
        // throttling變量記錄上次調(diào)用的時(shí)間間隔是否已經(jīng)結(jié)束, 即是否處于節(jié)流過(guò)程中
        // throttling在每次函數(shù)調(diào)用時(shí)設(shè)為true, 表示需要進(jìn)行節(jié)流, 在時(shí)間間隔截止時(shí)設(shè)置為false(在whenDone函數(shù)中實(shí)現(xiàn))
        if(throttling) {
            // 節(jié)流過(guò)程中進(jìn)行了多次調(diào)用, 在more中記錄一個(gè)狀態(tài), 表示在時(shí)間間隔截止時(shí)需要再次自動(dòng)調(diào)用函數(shù)
            more = true;
        } else {
            // 沒(méi)有處于節(jié)流過(guò)程, 可能是第一次調(diào)用函數(shù), 或已經(jīng)超過(guò)上一次調(diào)用的間隔, 可以直接調(diào)用函數(shù)
            result = func.apply(context, args);
        }
        // 調(diào)用whenDone, 用于在時(shí)間間隔后清除節(jié)流狀態(tài)
        whenDone();
        // throttling變量記錄函數(shù)調(diào)用時(shí)的節(jié)流狀態(tài)
        throttling = true;
        // 返回調(diào)用結(jié)果
        return result;
    };
};

這是函數(shù)節(jié)流方法, throttle方法主要用于控制函數(shù)的執(zhí)行頻率, 在被控制的時(shí)間間隔內(nèi), 頻繁調(diào)用函數(shù)不會(huì)被多次執(zhí)行,在時(shí)間間隔內(nèi)如果多次調(diào)用了函數(shù), 時(shí)間隔截止時(shí)會(huì)自動(dòng)調(diào)用一次, 不需要等到時(shí)間截止后再手動(dòng)調(diào)用(自動(dòng)調(diào)用時(shí)不會(huì)有返回值),throttle函數(shù)一般用于處理復(fù)雜和調(diào)用頻繁的函數(shù), 通過(guò)節(jié)流控制函數(shù)的調(diào)用頻率, 節(jié)省處理資源

_.debounce
_.debounce = function(func, wait, immediate) {
    // timeout用于記錄函數(shù)上一次調(diào)用的執(zhí)行狀態(tài)(定時(shí)器句柄)
    // 當(dāng)timeout為null時(shí), 表示上一次調(diào)用已經(jīng)結(jié)束
    var timeout;
    // 返回一個(gè)函數(shù), 并在函數(shù)內(nèi)進(jìn)行節(jié)流控制
    return function() {
        // 保持函數(shù)的上下文對(duì)象和參數(shù)
        var context = this, args = arguments;
        var later = function() {
            // 設(shè)置timeout為null
            // later函數(shù)會(huì)在允許的時(shí)間截止時(shí)被調(diào)用
            // 調(diào)用該函數(shù)時(shí), 表明上一次函數(shù)執(zhí)行時(shí)間已經(jīng)超過(guò)了約定的時(shí)間間隔, 此時(shí)之后再進(jìn)行調(diào)用都是被允許的
            timeout = null;
            if(!immediate)
                func.apply(context, args);
        };
        // 如果函數(shù)被設(shè)定為立即執(zhí)行, 且上一次調(diào)用的時(shí)間間隔已經(jīng)過(guò)去, 則立即調(diào)用函數(shù)
        if(immediate && !timeout)
            func.apply(context, args);
        // 創(chuàng)建一個(gè)定時(shí)器用于檢查和設(shè)置函數(shù)的調(diào)用狀態(tài)
        // 創(chuàng)建定時(shí)器之前先清空上一次setTimeout句柄, 無(wú)論上一次綁定的函數(shù)是否已經(jīng)被執(zhí)行
        // 如果本次函數(shù)在調(diào)用時(shí), 上一次函數(shù)執(zhí)行還沒(méi)有開(kāi)始(一般是immediate設(shè)置為false時(shí)), 則函數(shù)的執(zhí)行時(shí)間會(huì)被推遲, 因此timeout句柄會(huì)被重新創(chuàng)建
        clearTimeout(timeout);
        // 在允許的時(shí)間截止時(shí)調(diào)用later函數(shù)
        timeout = setTimeout(later, wait);
    };
};

debounce與throttle方法類似, 用于函數(shù)節(jié)流, 它們的不同之處在于:

-- throttle關(guān)注函數(shù)的執(zhí)行頻率, 在指定頻率內(nèi)函數(shù)只會(huì)被執(zhí)行一次
-- debounce函數(shù)更關(guān)注函數(shù)執(zhí)行的間隔, 即函數(shù)兩次的調(diào)用時(shí)間不能小于指定時(shí)間

如果兩次函數(shù)的執(zhí)行間隔小于wait, 定時(shí)器會(huì)被清除并重新創(chuàng)建, 這意味著連續(xù)頻繁地調(diào)用函數(shù), 函數(shù)一直不會(huì)被執(zhí)行, 直到某一次調(diào)用與上一次調(diào)用的時(shí)間不小于wait毫秒

_.once
_.once = function(func) {
    // ran記錄函數(shù)是否被執(zhí)行過(guò)
    // memo記錄函數(shù)最后一次執(zhí)行的結(jié)果
    var ran = false, memo;
    return function() {
        // 如果函數(shù)已被執(zhí)行過(guò), 則直接返回第一次執(zhí)行的結(jié)果
        if(ran)
            return memo;
        ran = true;
        return memo = func.apply(this, arguments);
    };
};

這個(gè)函數(shù)創(chuàng)建一個(gè)只會(huì)被執(zhí)行一次的函數(shù), 如果該函數(shù)被重復(fù)調(diào)用, 將返回第一次執(zhí)行的結(jié)果

_.wrap
_.wrap = function(func, wrapper) {
    return function() {
        // 將當(dāng)前函數(shù)作為第一個(gè)參數(shù), 傳遞給wrapper函數(shù)
        var args = [func].concat(slice.call(arguments, 0));
        // 返回wrapper函數(shù)的處理結(jié)果
        return wrapper.apply(this, args);
    };
};

這個(gè)函數(shù)返回一個(gè)函數(shù), 該函數(shù)會(huì)將當(dāng)前函數(shù)作為參數(shù)傳遞給一個(gè)包裹函數(shù),在包裹函數(shù)中可以通過(guò)第一個(gè)參數(shù)調(diào)用當(dāng)前函數(shù), 并返回結(jié)果

_.compose
_.compose = function() {
    // 獲取函數(shù)列表, 所有參數(shù)需均為Function類型
    var funcs = arguments;
    // 返回一個(gè)供調(diào)用的函數(shù)句柄
    return function() {
        // 從后向前依次執(zhí)行函數(shù), 并將記錄的返回值作為參數(shù)傳遞給前一個(gè)函數(shù)繼續(xù)處理
        var args = arguments;
        for(var i = funcs.length - 1; i >= 0; i--) {
            args = [funcs[i].apply(this, args)];
        }
        // 返回最后一次調(diào)用函數(shù)的返回值
        return args[0];
    };
};

這個(gè)函數(shù)將多個(gè)函數(shù)組合到一起, 按照參數(shù)傳遞的順序, 后一個(gè)函數(shù)的返回值會(huì)被依次作為參數(shù)傳遞給前一個(gè)函數(shù)作為參數(shù)繼續(xù)處理,_.compose(A, B, C)等同于 A(B(C()))

_.after
_.after = function(times, func) {
    // 如果沒(méi)有指定或指定無(wú)效次數(shù), 則func被直接調(diào)用
    if(times <= 0)
        return func();
    // 返回一個(gè)計(jì)數(shù)器函數(shù)
    return function() {
        // 每次調(diào)用計(jì)數(shù)器函數(shù)times減1, 調(diào)用times次之后執(zhí)行func函數(shù)并返回func函數(shù)的返回值
        if(--times < 1) {
            return func.apply(this, arguments);
        }
    };
};

after返回一個(gè)函數(shù), 該函數(shù)作為調(diào)用計(jì)數(shù)器, 當(dāng)該函數(shù)被調(diào)用times次(或超過(guò)times次)后, func函數(shù)將被執(zhí)行

_.keys
_.keys = nativeKeys ||
function(obj) {
    if(obj !== Object(obj))
        throw new TypeError("Invalid object");
    var keys = [];
    // 記錄并返回對(duì)象的所有屬性名
    for(var key in obj)
    if(_.has(obj, key))
        keys[keys.length] = key;
    return keys;
};

這個(gè)函數(shù)用于獲取一個(gè)對(duì)象的屬性名列表(不包含原型鏈中的屬性)

_.values
_.values = function(obj) {
    return _.map(obj, _.identity);
};

這個(gè)函數(shù)返回一個(gè)對(duì)象中所有屬性的值列表(不包含原型鏈中的屬性)

_.functions / _.methods
_.functions = _.methods = function(obj) {
    var names = [];
    for(var key in obj) {
        if(_.isFunction(obj[key]))
            names.push(key);
    }
    return names.sort();
};

這個(gè)函數(shù)獲取一個(gè)對(duì)象中所有屬性值為Function類型的key列表, 并按key名進(jìn)行排序(包含原型鏈中的屬性)

_.extend
_.extend = function(obj) {
    // each循環(huán)參數(shù)中的一個(gè)或多個(gè)對(duì)象
    each(slice.call(arguments, 1), function(source) {
        // 將對(duì)象中的全部屬性復(fù)制或覆蓋到obj對(duì)象
        for(var prop in source) {
            obj[prop] = source[prop];
        }
    });
    return obj;
};

這個(gè)函數(shù)將一個(gè)或多個(gè)對(duì)象的屬性(包含原型鏈中的屬性), 復(fù)制到obj對(duì)象, 如果存在同名屬性則覆蓋

_.pick
_.pick = function(obj) {
    // 創(chuàng)建一個(gè)對(duì)象, 存放復(fù)制的指定屬性
    var result = {};
    // 從第二個(gè)參數(shù)開(kāi)始合并為一個(gè)存放屬性名列表的數(shù)組
    each(_.flatten(slice.call(arguments, 1)), function(key) {
        // 循環(huán)屬性名列表, 如果obj中存在該屬性, 則將其復(fù)制到result對(duì)象
        if( key in obj)
            result[key] = obj[key];
    });
    // 返回復(fù)制結(jié)果
    return result;
};

這個(gè)函數(shù)返回一個(gè)新對(duì)象, 并從obj中復(fù)制指定的屬性到新對(duì)象中,第2個(gè)參數(shù)開(kāi)始為指定的需要復(fù)制的屬性名

_.defaults
_.defaults = function(obj) {
    // 從第二個(gè)參數(shù)開(kāi)始可指定多個(gè)對(duì)象, 這些對(duì)象中的屬性將被依次復(fù)制到obj對(duì)象中(如果obj對(duì)象中不存在該屬性的話)
    each(slice.call(arguments, 1), function(source) {
        // 遍歷每個(gè)對(duì)象中的所有屬性
        for(var prop in source) {
            // 如果obj中不存在或?qū)傩灾缔D(zhuǎn)換為Boolean類型后值為false, 則將屬性復(fù)制到obj中
            if(obj[prop] == null)
                obj[prop] = source[prop];
        }
    });
    return obj;
};

這個(gè)函數(shù)將obj中不存在或轉(zhuǎn)換為Boolean類型后值為false的屬性, 從參數(shù)中指定的一個(gè)或多個(gè)對(duì)象中復(fù)制到obj,一般用于給對(duì)象指定默認(rèn)值

_.clone
_.clone = function(obj) {
    // 不支持非數(shù)組和對(duì)象類型的數(shù)據(jù)
    if(!_.isObject(obj))
        return obj;
    // 復(fù)制并返回?cái)?shù)組或?qū)ο?    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

這個(gè)函數(shù)創(chuàng)建一個(gè)obj的副本, 返回一個(gè)新的對(duì)象, 該對(duì)象包含obj中的所有屬性和值的狀態(tài)

_.tap
_.tap = function(obj, interceptor) {
    interceptor(obj);
    return obj;
};

這個(gè)函數(shù)執(zhí)行一個(gè)函數(shù), 并將obj作為參數(shù)傳遞給該函數(shù), 函數(shù)執(zhí)行完畢后最終返回obj對(duì)象

eq
function eq(a, b, stack) {
    // 檢查兩個(gè)簡(jiǎn)單數(shù)據(jù)類型的值是否相等
    // 對(duì)于復(fù)合數(shù)據(jù)類型, 如果它們來(lái)自同一個(gè)引用, 則認(rèn)為其相等
    // 如果被比較的值其中包含0, 則檢查另一個(gè)值是否為-0, 因?yàn)?0 === -0 是成立的
    // 而 1 / 0 == 1 / -0 是不成立的(1 / 0值為Infinity, 1 / -0值為-Infinity, 而Infinity不等于-Infinity)
    if(a === b)
        return a !== 0 || 1 / a == 1 / b;
    // 將數(shù)據(jù)轉(zhuǎn)換為布爾類型后如果值為false, 將判斷兩個(gè)值的數(shù)據(jù)類型是否相等(因?yàn)閚ull與undefined, false, 0, 空字符串, 在非嚴(yán)格比較下值是相等的)
    if(a == null || b == null)
        return a === b;
    // 如果進(jìn)行比較的數(shù)據(jù)是一個(gè)Underscore封裝的對(duì)象(具有_chain屬性的對(duì)象被認(rèn)為是Underscore對(duì)象)
    // 則將對(duì)象解封后獲取本身的數(shù)據(jù)(通過(guò)_wrapped訪問(wèn)), 然后再對(duì)本身的數(shù)據(jù)進(jìn)行比較
    // 它們的關(guān)系類似與一個(gè)jQuery封裝的DOM對(duì)象, 和瀏覽器本身創(chuàng)建的DOM對(duì)象
    if(a._chain)
        a = a._wrapped;
    if(b._chain)
        b = b._wrapped;
    // 如果對(duì)象提供了自定義的isEqual方法(此處的isEqual方法并非Undersocre對(duì)象的isEqual方法, 因?yàn)樵谏弦徊揭呀?jīng)對(duì)Undersocre對(duì)象進(jìn)行了解封)
    // 則使用對(duì)象自定義的isEqual方法與另一個(gè)對(duì)象進(jìn)行比較
    if(a.isEqual && _.isFunction(a.isEqual))
        return a.isEqual(b);
    if(b.isEqual && _.isFunction(b.isEqual))
        return b.isEqual(a);
    // 對(duì)兩個(gè)數(shù)據(jù)的數(shù)據(jù)類型進(jìn)行驗(yàn)證
    // 獲取對(duì)象a的數(shù)據(jù)類型(通過(guò)Object.prototype.toString方法)
    var className = toString.call(a);
    // 如果對(duì)象a的數(shù)據(jù)類型與對(duì)象b不匹配, 則認(rèn)為兩個(gè)數(shù)據(jù)值也不匹配
    if(className != toString.call(b))
        return false;
    // 執(zhí)行到此處, 可以確保需要比較的兩個(gè)數(shù)據(jù)均為復(fù)合數(shù)據(jù)類型, 且數(shù)據(jù)類型相等
    // 通過(guò)switch檢查數(shù)據(jù)的數(shù)據(jù)類型, 針對(duì)不同數(shù)據(jù)類型進(jìn)行不同的比較
    // (此處不包括對(duì)數(shù)組和對(duì)象類型, 因?yàn)樗鼈兛赡馨顚哟蔚臄?shù)據(jù), 將在后面進(jìn)行深層比較)
    switch (className) {
        case "[object String]":
            // 如果被比較的是字符串類型(其中a的是通過(guò)new String()創(chuàng)建的字符串)
            // 則將B轉(zhuǎn)換為String對(duì)象后進(jìn)行匹配(這里匹配并非進(jìn)行嚴(yán)格的數(shù)據(jù)類型檢查, 因?yàn)樗鼈儾⒎莵?lái)自同一個(gè)對(duì)象的引用)
            // 在調(diào)用 == 進(jìn)行比較時(shí), 會(huì)自動(dòng)調(diào)用對(duì)象的toString()方法, 返回兩個(gè)簡(jiǎn)單數(shù)據(jù)類型的字符串
            return a == String(b);
        case "[object Number]":
            // 通過(guò)+a將a轉(zhuǎn)成一個(gè)Number, 如果a被轉(zhuǎn)換之前與轉(zhuǎn)換之后不相等, 則認(rèn)為a是一個(gè)NaN類型
            // 因?yàn)镹aN與NaN是不相等的, 因此當(dāng)a值為NaN時(shí), 無(wú)法簡(jiǎn)單地使用a == b進(jìn)行匹配, 而是用相同的方法檢查b是否為NaN(即 b != +b)
            // 當(dāng)a值是一個(gè)非NaN的數(shù)據(jù)時(shí), 則檢查a是否為0, 因?yàn)楫?dāng)b為-0時(shí), 0 === -0是成立的(實(shí)際上它們?cè)谶壿嬌蠈儆趦蓚€(gè)不同的數(shù)據(jù))
            return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
        case "[object Date]":
        // 對(duì)日期類型沒(méi)有使用return或break, 因此會(huì)繼續(xù)執(zhí)行到下一步(無(wú)論數(shù)據(jù)類型是否為Boolean類型, 因?yàn)橄乱徊綄?duì)Boolean類型進(jìn)行檢查)
        case "[object Boolean]":
            // 將日期或布爾類型轉(zhuǎn)換為數(shù)字
            // 日期類型將轉(zhuǎn)換為數(shù)值類型的時(shí)間戳(無(wú)效的日期格式將被換轉(zhuǎn)為NaN)
            // 布爾類型中, true被轉(zhuǎn)換為1, false被轉(zhuǎn)換為0
            // 比較兩個(gè)日期或布爾類型被轉(zhuǎn)換為數(shù)字后是否相等
            return +a == +b;
        case "[object RegExp]":
            // 正則表達(dá)式類型, 通過(guò)source訪問(wèn)表達(dá)式的字符串形式
            // 檢查兩個(gè)表達(dá)式的字符串形式是否相等
            // 檢查兩個(gè)表達(dá)式的全局屬性是否相同(包括g, i, m)
            // 如果完全相等, 則認(rèn)為兩個(gè)數(shù)據(jù)相等
            return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase;
    }
    // 當(dāng)執(zhí)行到此時(shí), ab兩個(gè)數(shù)據(jù)應(yīng)該為類型相同的對(duì)象或數(shù)組類型
    if( typeof a != "object" || typeof b != "object")
        return false;
    // stack(堆)是在isEqual調(diào)用eq函數(shù)時(shí)內(nèi)部傳遞的空數(shù)組, 在后面比較對(duì)象和數(shù)據(jù)的內(nèi)部迭代中調(diào)用eq方法也會(huì)傳遞
    // length記錄堆的長(zhǎng)度
    var length = stack.length;
    while(length--) {
        // 如果堆中的某個(gè)對(duì)象與數(shù)據(jù)a匹配, 則認(rèn)為相等
        if(stack[length] == a)
            return true;
    }
    // 將數(shù)據(jù)a添加到堆中
    stack.push(a);
    // 定義一些局部變量
    var size = 0, result = true;
    // 通過(guò)遞歸深層比較對(duì)象和數(shù)組
    if(className == "[object Array]") {
        // 被比較的數(shù)據(jù)為數(shù)組類型
        // size記錄數(shù)組的長(zhǎng)度
        // result比較兩個(gè)數(shù)組的長(zhǎng)度是否一致, 如果長(zhǎng)度不一致, 則方法的最后將返回result(即false)
        size = a.length;
        result = size == b.length;
        // 如果兩個(gè)數(shù)組的長(zhǎng)度一致
        if(result) {
            // 調(diào)用eq方法對(duì)數(shù)組中的元素進(jìn)行迭代比較(如果數(shù)組中包含二維數(shù)組或?qū)ο? eq方法會(huì)進(jìn)行深層比較)
            while(size--) {
                // 在確保兩個(gè)數(shù)組都存在當(dāng)前索引的元素時(shí), 調(diào)用eq方法深層比較(將堆數(shù)據(jù)傳遞給eq方法)
                // 將比較的結(jié)果存儲(chǔ)到result變量, 如果result為false(即在比較中得到某個(gè)元素的數(shù)據(jù)不一致), 則停止迭代
                if(!( result = size in a == size in b && eq(a[size], b[size], stack)))
                    break;
            }
        }
    } else {
        // 被比較的數(shù)據(jù)為對(duì)象類型
        // 如果兩個(gè)對(duì)象不是同一個(gè)類的實(shí)例(通過(guò)constructor屬性比較), 則認(rèn)為兩個(gè)對(duì)象不相等
        if("constructor" in a != "constructor" in b || a.constructor != b.constructor)
            return false;
        // 深層比較兩個(gè)對(duì)象中的數(shù)據(jù)
        for(var key in a) {
            if(_.has(a, key)) {
                // size用于記錄比較過(guò)的屬性數(shù)量, 因?yàn)檫@里遍歷的是a對(duì)象的屬性, 并比較b對(duì)象中該屬性的數(shù)據(jù)
                // 當(dāng)b對(duì)象中的屬性數(shù)量多余a對(duì)象時(shí), 此處的邏輯成立, 但兩個(gè)對(duì)象并不相等
                size++;
                // 迭代調(diào)用eq方法, 深層比較兩個(gè)對(duì)象中的屬性值
                // 將比較的結(jié)果記錄到result變量, 當(dāng)比較到不相等的數(shù)據(jù)時(shí)停止迭代
                if(!( result = _.has(b, key) && eq(a[key], b[key], stack)))
                    break;
            }
        }
        // 深層比較完畢, 這里已經(jīng)可以確保在對(duì)象a中的所有數(shù)據(jù), 對(duì)象b中也存在相同的數(shù)據(jù)
        // 根據(jù)size(對(duì)象屬性長(zhǎng)度)檢查對(duì)象b中的屬性數(shù)量是否與對(duì)象a相等
        if(result) {
            // 遍歷對(duì)象b中的所有屬性
            for(key in b) {
                // 當(dāng)size已經(jīng)到0時(shí)(即對(duì)象a中的屬性數(shù)量已經(jīng)遍歷完畢), 而對(duì)象b中還存在有屬性, 則對(duì)象b中的屬性多于對(duì)象a
                if(_.has(b, key) && !(size--))
                    break;
            }
            // 當(dāng)對(duì)象b中的屬性多于對(duì)象a, 則認(rèn)為兩個(gè)對(duì)象不相等
            result = !size;
        }
    }
    // 函數(shù)執(zhí)行完畢時(shí), 從堆中移除第一個(gè)數(shù)據(jù)(在比較對(duì)象或數(shù)組時(shí), 會(huì)迭代eq方法, 堆中可能存在多個(gè)數(shù)據(jù))
    stack.pop();
    // 返回的result記錄了最終的比較結(jié)果
    return result;
}

eq函數(shù)只在isEqual方法中調(diào)用, 用于比較兩個(gè)數(shù)據(jù)的值是否相等,與 === 不同在于, eq更關(guān)注數(shù)據(jù)的值,如果進(jìn)行比較的是兩個(gè)復(fù)合數(shù)據(jù)類型, 不僅僅比較是否來(lái)自同一個(gè)引用, 且會(huì)進(jìn)行深層比較(對(duì)兩個(gè)對(duì)象的結(jié)構(gòu)和數(shù)據(jù)進(jìn)行比較)

_.isEqual
_.isEqual = function(a, b) {
    return eq(a, b, []);
};

不多說(shuō)了,就是內(nèi)部函數(shù)eq的外部方法

_.isEmpty
_.isEmpty = function(obj) {
    // obj被轉(zhuǎn)換為Boolean類型后值為false
    if(obj == null)
        return true;
    // 檢查對(duì)象或字符串長(zhǎng)度是否為0
    if(_.isArray(obj) || _.isString(obj))
        return obj.length === 0;
    // 檢查對(duì)象(使用for in循環(huán)時(shí)將首先循環(huán)對(duì)象本身的屬性, 其次是原型鏈中的屬性), 因此如果第一個(gè)屬性是屬于對(duì)象本身的, 那么該對(duì)象不是一個(gè)空對(duì)象
    for(var key in obj)
    if(_.has(obj, key))
        return false;
    // 所有數(shù)據(jù)類型均沒(méi)有通過(guò)驗(yàn)證, 是一個(gè)空數(shù)據(jù)
    return true;
};

這個(gè)函數(shù)用于檢查數(shù)據(jù)是否為空值, 包含"", false, 0, null, undefined, NaN, 空數(shù)組(數(shù)組長(zhǎng)度為0)和空對(duì)象(對(duì)象本身沒(méi)有任何屬性)

_.isElement
_.isElement = function(obj) {
    return !!(obj && obj.nodeType == 1);
};

這個(gè)函數(shù)用于驗(yàn)證對(duì)象是否是一個(gè)DOM對(duì)象

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

這個(gè)函數(shù)用于驗(yàn)證一個(gè)變量是否是數(shù)組

_.isObject
_.isObject = function(obj) {
    return obj === Object(obj);
};

這個(gè)函數(shù)用于驗(yàn)證對(duì)象是否是一個(gè)復(fù)合數(shù)據(jù)類型的對(duì)象(即非基本數(shù)據(jù)類型String, Boolean, Number, null, undefined)

_.isArguments
_.isArguments = function(obj) {
    return toString.call(obj) == "[object Arguments]";
};
// 驗(yàn)證isArguments函數(shù), 如果運(yùn)行環(huán)境無(wú)法正常驗(yàn)證arguments類型的數(shù)據(jù), 則重新定義isArguments方法
if(!_.isArguments(arguments)) {
    // 對(duì)于環(huán)境無(wú)法通過(guò)toString驗(yàn)證arguments類型的, 則通過(guò)調(diào)用arguments獨(dú)有的callee方法來(lái)進(jìn)行驗(yàn)證
    _.isArguments = function(obj) {
        // callee是arguments的一個(gè)屬性, 指向?qū)rguments所屬函數(shù)自身的引用
        return !!(obj && _.has(obj, "callee"));
    };
}

這個(gè)函數(shù)用于檢查一個(gè)數(shù)據(jù)是否是一個(gè)arguments參數(shù)對(duì)象

_.isFunction / _.isString / _.isNumber / _.isDate / _.isRegExp

這幾個(gè)我就放在一起說(shuō)了,他們都是通過(guò)Object.prototype.toString.call(obj)的值來(lái)進(jìn)行判斷的

_.isFinite
_.isFinite = function(obj) {
    return _.isNumber(obj) && isFinite(obj);
};

這個(gè)函數(shù)用于檢查一個(gè)數(shù)字是否為有效數(shù)字且有效范圍(Number類型, 值在負(fù)無(wú)窮大 - 正無(wú)窮大之間)

_.isNaN
_.isNaN = function(obj) {
    return obj !== obj;
};

在js里,所有數(shù)據(jù)中只有NaN與NaN不相等

_.isBoolean
_.isBoolean = function(obj) {
    // 支持字面量和對(duì)象形式的Boolean數(shù)據(jù)
    return obj === true || obj === false || toString.call(obj) == "[object Boolean]";
};

這個(gè)函數(shù)用于檢查數(shù)據(jù)是否是Boolean類型

_.isNull
_.isNull = function(obj) {
    return obj === null;
};

這個(gè)函數(shù)用于檢查數(shù)據(jù)是否是Null值

_.isUndefined
_.isUndefined = function(obj) {
    return obj === void 0;
};

這個(gè)函數(shù)用于檢查數(shù)據(jù)是否是Undefined值

_.has
_.has = function(obj, key) {
    return hasOwnProperty.call(obj, key);
};

這個(gè)函數(shù)檢查一個(gè)屬性是否屬于對(duì)象本身, 而非原型鏈中

_.noConflict
_.noConflict = function() {
    // previousUnderscore變量記錄了Underscore定義前_(下劃線)的值
    root._ = previousUnderscore;
    return this;
};

這個(gè)函數(shù)一般用于避免命名沖突或規(guī)范命名方式,放棄_(下劃線)命名的Underscore對(duì)象, 并返回Underscore對(duì)象

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

這個(gè)函數(shù)返回與參數(shù)相同的值, 一般用于將一個(gè)數(shù)據(jù)的獲取方式轉(zhuǎn)換為函數(shù)獲取方式(內(nèi)部用于構(gòu)建方法時(shí)作為默認(rèn)處理器函數(shù))

_.times
_.times = function(n, iterator, context) {
    for(var i = 0; i < n; i++)
    iterator.call(context, i);
};

這個(gè)函數(shù)的作用是使指定的函數(shù)迭代執(zhí)行n次(無(wú)參數(shù))

_.escape
_.escape = function(string) {
    return ("" + string).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/"/g, "'").replace(///g, "/");
};

這個(gè)函數(shù)用于將HTML字符串中的特殊字符轉(zhuǎn)換為HTML實(shí)體, 包含 & < > " "

_.result
_.result = function(object, property) {
    if(object == null)
        return null;
    // 獲取對(duì)象的值
    var value = object[property];
    // 如果值是一個(gè)函數(shù), 則執(zhí)行并返回, 否則將直接返回
    return _.isFunction(value) ? value.call(object) : value;
};

這個(gè)函數(shù)指定一個(gè)對(duì)象的屬性, 返回該屬性對(duì)應(yīng)的值, 如果該屬性對(duì)應(yīng)的是一個(gè)函數(shù), 則會(huì)執(zhí)行該函數(shù)并返回結(jié)果

_.mixin
_.mixin = function(obj) {
    // obj是一個(gè)集合一系列自定義方法的對(duì)象, 此處通過(guò)each遍歷對(duì)象的方法
    each(_.functions(obj), function(name) {
        // 通過(guò)addToWrapper函數(shù)將自定義方法添加到Underscore構(gòu)建的對(duì)象中, 用于支持對(duì)象式調(diào)用
        // 同時(shí)將方法添加到 _ 本身, 用于支持函數(shù)式調(diào)用
        addToWrapper(name, _[name] = obj[name]);
    });
};

這個(gè)函數(shù)添加一系列自定義方法到Underscore對(duì)象中, 用于擴(kuò)展Underscore插件

_.template
_.template = function(text, data, settings) {
    // 模板配置, 如果沒(méi)有指定配置項(xiàng), 則使用templateSettings中指定的配置項(xiàng)
    settings = _.defaults(settings || {}, _.templateSettings);

    // 開(kāi)始將模板解析為可執(zhí)行源碼
    var source = "__p+="" + text.replace(escaper, function(match) {
        // 將特殊符號(hào)轉(zhuǎn)移為字符串形式
        return "" + escapes[match];
    }).replace(settings.escape || noMatch, function(match, code) {
        // 解析escape形式標(biāo)簽 <%- %>, 將變量中包含的HTML通過(guò)_.escape函數(shù)轉(zhuǎn)換為HTML實(shí)體
        return ""+
_.escape(" + unescape(code) + ")+
"";
    }).replace(settings.interpolate || noMatch, function(match, code) {
        // 解析interpolate形式標(biāo)簽 <%= %>, 將模板內(nèi)容作為一個(gè)變量與其它字符串連接起來(lái), 則會(huì)作為一個(gè)變量輸出
        return ""+
(" + unescape(code) + ")+
"";
    }).replace(settings.evaluate || noMatch, function(match, code) {
        // 解析evaluate形式標(biāo)簽 <% %>, evaluate標(biāo)簽中存儲(chǔ)了需要執(zhí)行的JavaScript代碼, 這里結(jié)束當(dāng)前的字符串拼接, 并在新的一行作為JavaScript語(yǔ)法執(zhí)行, 并將后面的內(nèi)容再次作為字符串的開(kāi)始, 因此evaluate標(biāo)簽內(nèi)的JavaScript代碼就能被正常執(zhí)行
        return "";
" + unescape(code) + "
;__p+="";
    }) + "";
";
    if(!settings.variable)
        source = "with(obj||{}){
" + source + "}
";
    source = "var __p="";" + "var print=function(){__p+=Array.prototype.join.call(arguments, "")};
" + source + "return __p;
";

    // 創(chuàng)建一個(gè)函數(shù), 將源碼作為函數(shù)執(zhí)行體, 將obj和Underscore作為參數(shù)傳遞給該函數(shù)
    var render = new Function(settings.variable || "obj", "_", source);
    // 如果指定了模板的填充數(shù)據(jù), 則替換模板內(nèi)容, 并返回替換后的結(jié)果
    if(data)
        return render(data, _);
    // 如果沒(méi)有指定填充數(shù)據(jù), 則返回一個(gè)函數(shù), 該函數(shù)用于將接收到的數(shù)據(jù)替換到模板
    // 如果在程序中會(huì)多次填充相同模板, 那么在第一次調(diào)用時(shí)建議不指定填充數(shù)據(jù), 在獲得處理函數(shù)的引用后, 再直接調(diào)用會(huì)提高運(yùn)行效率
    var template = function(data) {
        return render.call(this, data, _);
    };
    // 將創(chuàng)建的源碼字符串添加到函數(shù)對(duì)象中, 一般用于調(diào)試和測(cè)試
    template.source = "function(" + (settings.variable || "obj") + "){
" + source + "}";
    // 沒(méi)有指定填充數(shù)據(jù)的情況下, 返回處理函數(shù)句柄
    return template;
};

這個(gè)我要介紹的最后一個(gè)函數(shù),也是我個(gè)人認(rèn)為比較重要的,它是Underscore模板解析方法, 用于將數(shù)據(jù)填充到一個(gè)模板字符串中,在模板體內(nèi), 可通過(guò)argments獲取2個(gè)參數(shù), 分別為填充數(shù)據(jù)(名稱為obj)和Underscore對(duì)象(名稱為_(kāi))

小結(jié)

今天一口氣把剩下的所有函數(shù)都介紹完了,真是累感不愛(ài)啊,不過(guò)在寫(xiě)作這幾篇博客的過(guò)程中,我也從Underscore這個(gè)框架中學(xué)到了很多東西,包括它的優(yōu)雅的代碼風(fēng)格(至少比我自己寫(xiě)的優(yōu)雅),還有一個(gè)優(yōu)秀的庫(kù)整個(gè)的架構(gòu)是怎么搭建起來(lái)的。
以后我還會(huì)繼續(xù)為大家分享其他的前端知識(shí)和學(xué)習(xí)心得,thx for reading, hope u enjoy

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/87556.html

相關(guān)文章

  • Underscore源碼解析(一)

    摘要:本文同步自我得博客最近準(zhǔn)備折騰一下,在事先了解了之后,我知道了對(duì)這個(gè)庫(kù)有著強(qiáng)依賴,正好之前也沒(méi)使用過(guò),于是我就想先把徹底了解一下,這樣之后折騰的時(shí)候也少一點(diǎn)阻礙。 本文同步自我得博客:http://www.joeray61.com 最近準(zhǔn)備折騰一下backbone.js,在事先了解了backbone之后,我知道了backbone對(duì)underscore這個(gè)庫(kù)有著強(qiáng)依賴,正好undersc...

    neu 評(píng)論0 收藏0
  • Underscore源碼解析(二)

    摘要:本文同步自我得博客最近十幾天都在忙畢業(yè)論文的事,所以上一次為大家介紹完這個(gè)框架的結(jié)構(gòu)或者說(shuō)是這個(gè)框架的設(shè)計(jì)思路之后就一直沒(méi)動(dòng)靜了,今天我又滿血復(fù)活了,讓我們繼續(xù)來(lái)探索的源碼奧秘吧。 本文同步自我得博客:http://www.joeray61.com 最近十幾天都在忙畢業(yè)論文的事,所以上一次為大家介紹完underscore這個(gè)框架的結(jié)構(gòu)(或者說(shuō)是這個(gè)框架的設(shè)計(jì)思路)之后就一直沒(méi)動(dòng)靜了,今...

    騫諱護(hù) 評(píng)論0 收藏0
  • JS基礎(chǔ)篇-underscore源碼解析

    摘要:總想找個(gè)機(jī)會(huì)夯實(shí)一下自己的基礎(chǔ),正好最近略有清閑,看視頻讀書(shū)擼代碼我選擇了第三者怎么感覺(jué)有點(diǎn)別扭,看視頻的話效率不高適合入門(mén),看書(shū)的話一本你不知道的推薦給大家,選擇繼續(xù)看書(shū)的話還是算了吧,畢竟讀萬(wàn)卷書(shū)不如行萬(wàn)里路是吧。 總想找個(gè)機(jī)會(huì)夯實(shí)一下自己的JS基礎(chǔ),正好最近略有清閑,看視頻?讀書(shū)?擼代碼?我選擇了第三者(怎么感覺(jué)有點(diǎn)別扭),看視頻的話效率不高適合入門(mén),看書(shū)的話,一本《你不知道的J...

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

    摘要:所以,剛開(kāi)始,我從源碼比較短的包含注釋只有行開(kāi)始學(xué)習(xí)起。一般,在客戶端瀏覽器環(huán)境中,即為,暴露在全局中。學(xué)習(xí)以后判斷直接使用看起來(lái)也優(yōu)雅一點(diǎn)滑稽臉。在的函數(shù)視線中,的作用執(zhí)行一個(gè)傳入函數(shù)次,并返回由每次執(zhí)行結(jié)果組成的數(shù)組。 前言 最近在社區(qū)瀏覽文章的時(shí)候,看到了一位大四學(xué)長(zhǎng)在尋求前端工作中的面經(jīng),看完不得不佩服,掌握知識(shí)點(diǎn)真是全面,無(wú)論是前端后臺(tái)還是其他,都有涉獵。 在他寫(xiě)的文章中,有...

    gclove 評(píng)論0 收藏0
  • Underscore源碼中文注釋(轉(zhuǎn))

    摘要:創(chuàng)建一個(gè)全局對(duì)象在瀏覽器中表示為對(duì)象在中表示對(duì)象保存下劃線變量被覆蓋之前的值如果出現(xiàn)命名沖突或考慮到規(guī)范可通過(guò)方法恢復(fù)被占用之前的值并返回對(duì)象以便重新命名創(chuàng)建一個(gè)空的對(duì)象常量便于內(nèi)部共享使用將內(nèi)置對(duì)象的原型鏈緩存在局部變量方便快速調(diào)用將 // Underscore.js 1.3.3 // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc....

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<