Underscore源码剖析(四)

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

我在这个系列的第一篇文章说过,我学underscore是为了在学backbone的时刻少一些障碍,从第一篇的写作时候到本日,也许也有个十几二十天,觉得拖得有点久,所以本日将会是underscore源码剖析系列的末了一篇文章,我会在这篇文章中引见underscore剩下的一切函数。
先附上前三篇文章的地点:Underscore源码剖析(一)Underscore源码剖析(二)Underscore源码剖析(三)

_.zip

_.zip = function() {
    // 将参数转换为数组, 此时args是一个二维数组
    var args = slice.call(arguments);
    // 盘算每一个数组的长度, 并返回个中最大长度值
    var length = _.max(_.pluck(args, 'length'));
    // 遵照最大长度值建立一个新的空数组, 该数组用于存储处置惩罚效果
    var results = new Array(length);
    // 轮回最大长度, 在每次轮回将挪用pluck要领猎取每一个数组中雷同位置的数据(顺次从0到末了位置)
    // 将猎取到的数据存储在一个新的数组, 放入results并返回
    for(var i = 0; i < length; i++)
    results[i] = _.pluck(args, "" + i);
    // 返回的效果是一个二维数组
    return results;
};

这个函数将每一个数组的雷同位置的数据作为一个新的二维数组返回, 返回的数组长度以传入参数中最大的数组长度为准, 别的数组的空缺位置运用undefined添补。zip函数应当包含多个参数, 且每一个参数应当均为数组。

_.indexOf

_.indexOf = function(array, item, isSorted) {
    if(array == null)
        return -1;
    var i, l;
    // 若数组已经由排序,则挪用sortedIndex要领,猎取元素插进去数组中所处位置的索引号
    if(isSorted) {
        i = _.sortedIndex(array, item);
        return array[i] === item ? i : -1;
    }
    // 优先挪用宿主环境供应的indexOf要领
    if(nativeIndexOf && array.indexOf === nativeIndexOf)
        return array.indexOf(item);
    // 轮回并返回元素初次涌现的位置
    for( i = 0, l = array.length; i < l; i++)
    if( i in array && array[i] === item)
        return i;
    // 没有找到元素, 返回-1
    return -1;
}; 

这个函数的作用是搜刮一个元素在数组中初次涌现的位置, 假如元素不存在则返回 -1,搜刮时运用 === 对元素举行婚配

_.lastIndexOf

_.lastIndexOf = function(array, item) {
    if(array == null)
        return -1;
    // 优先挪用宿主环境供应的lastIndexOf要领
    if(nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf)
        return array.lastIndexOf(item);
    var i = array.length;
    // 轮回并返回元素末了涌现的位置
    while(i--)
    if( i in array && array[i] === item)
        return i;
    // 没有找到元素, 返回-1
    return -1;
};

这个函数返回一个元素在数组中末了一次涌现的位置, 假如元素不存在则返回 -1,搜刮时运用 === 对元素举行婚配

_.range

_.range = function(start, stop, step) {
    // 参数掌握
    if(arguments.length <= 1) {
        // 假如没有参数, 则start = 0, stop = 0, 在轮回中不会天生任何数据, 将返回一个空数组
        // 假如有1个参数, 则参数指定给stop, start = 0
        stop = start || 0;
        start = 0;
    }
    // 天生整数的步长值, 默以为1
    step = arguments[2] || 1;

    // 依据区间和步长盘算将天生的最大值
    var len = Math.max(Math.ceil((stop - start) / step), 0);
    var idx = 0;
    var range = new Array(len);

    // 天生整数列表, 并存储到range数组
    while(idx < len) {
        range[idx++] = start;
        start += step;
    }

    // 返回列表效果
    return range;
};

这个函数依据区间和步长, 天生一系列整数, 并作为数组返回,start参数示意最小数,stop参数示意最大数,step参数示意步长

_.bind

_.bind = function bind(func, context) {
    var bound, args;
    // 优先挪用宿主环境供应的bind要领
    if(func.bind === nativeBind && nativeBind)
        return nativeBind.apply(func, slice.call(arguments, 1));
    // func参数必需是一个函数(Function)范例
    if(!_.isFunction(func))
        throw new TypeError;
    // args变量存储了bind要领第三个最先的参数列表, 每次挪用时都将通报给func函数
    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;
    };
};

这个函数为一个函数绑定实行高低文, 任何情况下挪用该函数, 函数中的this均指向context对象,绑定函数时, 能够同时给函数通报挪用形参

_.bindAll

_.bindAll = function(obj) {
    // 第二个参数最先示意须要绑定的函数称号
    var funcs = slice.call(arguments, 1);
    // 假如没有指定特定的函数称号, 则默许绑定对象本身一切范例为Function的属性
    if(funcs.length == 0)
        funcs = _.functions(obj);
    // 轮回并将一切的函数高低本设置为obj对象本身
    // each要领本身不会遍历对象原型链中的要领, 但此处的funcs列表是经由过程_.functions要领猎取的, 它已包含了原型链中的要领
    each(funcs, function(f) {
        obj[f] = _.bind(obj[f], obj);
    });
    return obj;
};

这个函数将指定的函数, 或对象本身的一切函数高低本绑定到对象本身, 被绑定的函数在被挪用时, 高低文对象一向指向对象本身

_.memoize

_.memoize = function(func, hasher) {
    // 用于存储缓存效果的memo对象
    var memo = {};
    // hasher参数应当是一个function, 它用于返回一个key, 该key作为读取缓存的标识
    // 假如没有指定key, 则默许运用函数的第一个参数作为key, 假如函数的第一个参数是复合数据范例, 能够会返回相似[Object object]的key, 这个key能够会形成后续盘算的数据不正确
    hasher || ( hasher = _.identity);
    // 返回一个函数, 该函数起首经由过程搜检缓存, 再对没有缓存过的数据举行挪用
    return function() {
        var key = hasher.apply(this, arguments);
        return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
    };
};

这个函数将返回一个函数, 该函数集成了缓存功用, 将经由盘算的值缓存到局部变量并在下次挪用时直接返回

_.delay

_.delay = function(func, wait) {
    var args = slice.call(arguments, 2);
    // 经由过程setTimeout来延时实行
    return setTimeout(function() {
        return func.apply(null, args);
    }, wait);
};

这个函数的作用是延时实行一个函数,wait单元为ms, 第3个参数最先将被顺次通报给实行函数

_.defer

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

这个函数的作用是耽误1ms实行函数,javascript是一个单线程的递次,setTimeout(func, time)作用是把func放到处置惩罚使命的行列末端,在其他使命都完成今后的time ms 后实行func

_.throttle

_.throttle = function(func, wait) {
    var context, args, timeout, throttling, more, result;
    // whenDone变量挪用了debounce要领, 因而在屡次一连挪用函数时, 末了一次挪用会掩盖之前挪用的定时器, 消灭状况函数也仅会被实行一次
    // whenDone函数在末了一次函数实行的时候距离住手时挪用, 消灭撙节和挪用过程当中纪录的一些状况
    var whenDone = _.debounce(function() {
        more = throttling = false;
    }, wait);
    // 返回一个函数, 并在函数内举行撙节掌握
    return function() {
        // 保留函数的实行高低文和参数
        context = this;
        args = arguments;
        // later函数在上一次函数挪用时候距离住手时实行
        var later = function() {
            // 消灭timeout句柄, 轻易下一次函数挪用
            timeout = null;
            // more纪录了在上一次挪用至时候距离住手之间, 是不是反复挪用了函数
            // 假如反复挪用了函数, 在时候距离住手时将自动再次挪用函数
            if(more)
                func.apply(context, args);
            // 挪用whenDone, 用于在时候距离后消灭撙节状况
            whenDone();
        };
        // timeout纪录了上一次函数实行的时候距离句柄
        // timeout时候距离住手时挪用later函数, later中将消灭timeout, 并搜检是不是须要再次挪用函数
        if(!timeout)
            timeout = setTimeout(later, wait);
        // throttling变量纪录上次挪用的时候距离是不是已终了, 即是不是处于撙节过程当中
        // throttling在每次函数挪用时设为true, 示意须要举行撙节, 在时候距离住手时设置为false(在whenDone函数中完成)
        if(throttling) {
            // 撙节过程当中举行了屡次挪用, 在more中纪录一个状况, 示意在时候距离住手时须要再次自动挪用函数
            more = true;
        } else {
            // 没有处于撙节过程, 多是第一次挪用函数, 或已凌驾上一次挪用的距离, 能够直接挪用函数
            result = func.apply(context, args);
        }
        // 挪用whenDone, 用于在时候距离后消灭撙节状况
        whenDone();
        // throttling变量纪录函数挪用时的撙节状况
        throttling = true;
        // 返回挪用效果
        return result;
    };
};

这是函数撙节要领, throttle要领主要用于掌握函数的实行频次, 在被掌握的时候距离内, 频仍挪用函数不会被屡次实行,在时候距离内假如屡次挪用了函数, 时候隔住手时会自动挪用一次, 不须要比及时候住手后再手动挪用(自动挪用时不会有返回值),throttle函数平常用于处置惩罚庞杂和挪用频仍的函数, 经由过程撙节掌握函数的挪用频次, 节约处置惩罚资本

_.debounce

_.debounce = function(func, wait, immediate) {
    // timeout用于纪录函数上一次挪用的实行状况(定时器句柄)
    // 当timeout为null时, 示意上一次挪用已终了
    var timeout;
    // 返回一个函数, 并在函数内举行撙节掌握
    return function() {
        // 坚持函数的高低文对象和参数
        var context = this, args = arguments;
        var later = function() {
            // 设置timeout为null
            // later函数会在许可的时候住手时被挪用
            // 挪用该函数时, 表明上一次函数实行时候已凌驾了商定的时候距离, 此时今后再举行挪用都是被许可的
            timeout = null;
            if(!immediate)
                func.apply(context, args);
        };
        // 假如函数被设定为马上实行, 且上一次挪用的时候距离已过去, 则马上挪用函数
        if(immediate && !timeout)
            func.apply(context, args);
        // 建立一个定时器用于搜检和设置函数的挪用状况
        // 建立定时器之前先清空上一次setTimeout句柄, 不管上一次绑定的函数是不是已被实行
        // 假如本次函数在挪用时, 上一次函数实行还没有最先(平常是immediate设置为false时), 则函数的实行时候会被推延, 因而timeout句柄会被从新建立
        clearTimeout(timeout);
        // 在许可的时候住手时挪用later函数
        timeout = setTimeout(later, wait);
    };
};

debounce与throttle要领相似, 用于函数撙节, 它们的差别之处在于:

-- throttle关注函数的实行频次, 在指定频次内函数只会被实行一次
-- debounce函数更关注函数实行的距离, 即函数两次的挪用时候不能小于指定时候

假如两次函数的实行距离小于wait, 定时器会被消灭并从新建立, 这意味着一连频仍地挪用函数, 函数一向不会被实行, 直到某一次挪用与上一次挪用的时候不小于wait毫秒

_.once

_.once = function(func) {
    // ran纪录函数是不是被实行过
    // memo纪录函数末了一次实行的效果
    var ran = false, memo;
    return function() {
        // 假如函数已被实行过, 则直接返回第一次实行的效果
        if(ran)
            return memo;
        ran = true;
        return memo = func.apply(this, arguments);
    };
};

这个函数建立一个只会被实行一次的函数, 假如该函数被反复挪用, 将返回第一次实行的效果

_.wrap

_.wrap = function(func, wrapper) {
    return function() {
        // 将当前函数作为第一个参数, 通报给wrapper函数
        var args = [func].concat(slice.call(arguments, 0));
        // 返回wrapper函数的处置惩罚效果
        return wrapper.apply(this, args);
    };
};

这个函数返回一个函数, 该函数会将当前函数作为参数通报给一个包裹函数,在包裹函数中能够经由过程第一个参数挪用当前函数, 并返回效果

_.compose

_.compose = function() {
    // 猎取函数列表, 一切参数需均为Function范例
    var funcs = arguments;
    // 返回一个供挪用的函数句柄
    return function() {
        // 从后向前顺次实行函数, 并将纪录的返回值作为参数通报给前一个函数继承处置惩罚
        var args = arguments;
        for(var i = funcs.length - 1; i >= 0; i--) {
            args = [funcs[i].apply(this, args)];
        }
        // 返回末了一次挪用函数的返回值
        return args[0];
    };
};

这个函数将多个函数组合到一同, 根据参数通报的递次, 后一个函数的返回值会被顺次作为参数通报给前一个函数作为参数继承处置惩罚,_.compose(A, B, C)等同于 A(B(C()))

_.after

_.after = function(times, func) {
    // 假如没有指定或指定无效次数, 则func被直接挪用
    if(times <= 0)
        return func();
    // 返回一个计数器函数
    return function() {
        // 每次挪用计数器函数times减1, 挪用times次今后实行func函数并返回func函数的返回值
        if(--times < 1) {
            return func.apply(this, arguments);
        }
    };
};

after返回一个函数, 该函数作为挪用计数器, 当该函数被挪用times次(或凌驾times次)后, func函数将被实行

_.keys

_.keys = nativeKeys ||
function(obj) {
    if(obj !== Object(obj))
        throw new TypeError('Invalid object');
    var keys = [];
    // 纪录并返回对象的一切属性名
    for(var key in obj)
    if(_.has(obj, key))
        keys[keys.length] = key;
    return keys;
};

这个函数用于猎取一个对象的属性名列表(不包含原型链中的属性)

_.values

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

这个函数返回一个对象中一切属性的值列表(不包含原型链中的属性)

_.functions / _.methods

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

这个函数猎取一个对象中一切属性值为Function范例的key列表, 并按key名举行排序(包含原型链中的属性)

_.extend

_.extend = function(obj) {
    // each轮回参数中的一个或多个对象
    each(slice.call(arguments, 1), function(source) {
        // 将对象中的悉数属性复制或掩盖到obj对象
        for(var prop in source) {
            obj[prop] = source[prop];
        }
    });
    return obj;
};

这个函数将一个或多个对象的属性(包含原型链中的属性), 复制到obj对象, 假如存在同名属性则掩盖

_.pick

_.pick = function(obj) {
    // 建立一个对象, 寄存复制的指定属性
    var result = {};
    // 从第二个参数最先合并为一个寄存属性名列表的数组
    each(_.flatten(slice.call(arguments, 1)), function(key) {
        // 轮回属性名列表, 假如obj中存在该属性, 则将其复制到result对象
        if( key in obj)
            result[key] = obj[key];
    });
    // 返回复制效果
    return result;
};

这个函数返回一个新对象, 并从obj中复制指定的属性到新对象中,第2个参数最先为指定的须要复制的属性名

_.defaults

_.defaults = function(obj) {
    // 从第二个参数最先可指定多个对象, 这些对象中的属性将被顺次复制到obj对象中(假如obj对象中不存在该属性的话)
    each(slice.call(arguments, 1), function(source) {
        // 遍历每一个对象中的一切属性
        for(var prop in source) {
            // 假如obj中不存在或属性值转换为Boolean范例后值为false, 则将属性复制到obj中
            if(obj[prop] == null)
                obj[prop] = source[prop];
        }
    });
    return obj;
};

这个函数将obj中不存在或转换为Boolean范例后值为false的属性, 从参数中指定的一个或多个对象中复制到obj,平常用于给对象指定默许值

_.clone

_.clone = function(obj) {
    // 不支撑非数组和对象范例的数据
    if(!_.isObject(obj))
        return obj;
    // 复制并返回数组或对象
    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

这个函数建立一个obj的副本, 返回一个新的对象, 该对象包含obj中的一切属性和值的状况

_.tap

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

这个函数实行一个函数, 并将obj作为参数通报给该函数, 函数实行终了后终究返回obj对象

eq

function eq(a, b, stack) {
    // 搜检两个简朴数据范例的值是不是相称
    // 关于复合数据范例, 假如它们来自同一个援用, 则以为其相称
    // 假如被比较的值个中包含0, 则搜检另一个值是不是为-0, 由于 0 === -0 是建立的
    // 而 1 / 0 == 1 / -0 是不建立的(1 / 0值为Infinity, 1 / -0值为-Infinity, 而Infinity不等于-Infinity)
    if(a === b)
        return a !== 0 || 1 / a == 1 / b;
    // 将数据转换为布尔范例后假如值为false, 将推断两个值的数据范例是不是相称(由于null与undefined, false, 0, 空字符串, 在非严厉比较下值是相称的)
    if(a == null || b == null)
        return a === b;
    // 假如举行比较的数据是一个Underscore封装的对象(具有_chain属性的对象被以为是Underscore对象)
    // 则将对象解封后猎取本身的数据(经由过程_wrapped接见), 然后再对本身的数据举行比较
    // 它们的关联相似与一个jQuery封装的DOM对象, 和浏览器本身建立的DOM对象
    if(a._chain)
        a = a._wrapped;
    if(b._chain)
        b = b._wrapped;
    // 假如对象供应了自定义的isEqual要领(此处的isEqual要领并不是Undersocre对象的isEqual要领, 由于在上一步已对Undersocre对象举行了解封)
    // 则运用对象自定义的isEqual要领与另一个对象举行比较
    if(a.isEqual && _.isFunction(a.isEqual))
        return a.isEqual(b);
    if(b.isEqual && _.isFunction(b.isEqual))
        return b.isEqual(a);
    // 对两个数据的数据范例举行考证
    // 猎取对象a的数据范例(经由过程Object.prototype.toString要领)
    var className = toString.call(a);
    // 假如对象a的数据范例与对象b不婚配, 则以为两个数据值也不婚配
    if(className != toString.call(b))
        return false;
    // 实行到此处, 能够确保须要比较的两个数据均为复合数据范例, 且数据范例相称
    // 经由过程switch搜检数据的数据范例, 针对差别数据范例举行差别的比较
    // (此处不包含对数组和对象范例, 由于它们能够包含更深条理的数据, 将在背面举行深层比较)
    switch (className) {
        case '[object String]':
            // 假如被比较的是字符串范例(个中a的是经由过程new String()建立的字符串)
            // 则将B转换为String对象后举行婚配(这里婚配并不是举行严厉的数据范例搜检, 由于它们并不是来自同一个对象的援用)
            // 在挪用 == 举行比较时, 会自动挪用对象的toString()要领, 返回两个简朴数据范例的字符串
            return a == String(b);
        case '[object Number]':
            // 经由过程+a将a转成一个Number, 假如a被转换之前与转换今后不相称, 则以为a是一个NaN范例
            // 由于NaN与NaN是不相称的, 因而当a值为NaN时, 没法简朴地运用a == b举行婚配, 而是用雷同的要领搜检b是不是为NaN(即 b != +b)
            // 当a值是一个非NaN的数据时, 则搜检a是不是为0, 由于当b为-0时, 0 === -0是建立的(实际上它们在逻辑上属于两个差别的数据)
            return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
        case '[object Date]':
        // 对日期范例没有运用return或break, 因而会继承实行到下一步(不管数据范例是不是为Boolean范例, 由于下一步将对Boolean范例举行搜检)
        case '[object Boolean]':
            // 将日期或布尔范例转换为数字
            // 日期范例将转换为数值范例的时候戳(无效的日期花样将被换转为NaN)
            // 布尔范例中, true被转换为1, false被转换为0
            // 比较两个日期或布尔范例被转换为数字后是不是相称
            return +a == +b;
        case '[object RegExp]':
            // 正则表达式范例, 经由过程source接见表达式的字符串情势
            // 搜检两个表达式的字符串情势是不是相称
            // 搜检两个表达式的全局属性是不是雷同(包含g, i, m)
            // 假如完整相称, 则以为两个数据相称
            return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase;
    }
    // 当实行到此时, ab两个数据应当为范例雷同的对象或数组范例
    if( typeof a != 'object' || typeof b != 'object')
        return false;
    // stack(堆)是在isEqual挪用eq函数时内部通报的空数组, 在背面比较对象和数据的内部迭代中挪用eq要领也会通报
    // length纪录堆的长度
    var length = stack.length;
    while(length--) {
        // 假如堆中的某个对象与数据a婚配, 则以为相称
        if(stack[length] == a)
            return true;
    }
    // 将数据a增加到堆中
    stack.push(a);
    // 定义一些局部变量
    var size = 0, result = true;
    // 经由过程递归深层比较对象和数组
    if(className == '[object Array]') {
        // 被比较的数据为数组范例
        // size纪录数组的长度
        // result比较两个数组的长度是不是一致, 假如长度不一致, 则要领的末了将返回result(即false)
        size = a.length;
        result = size == b.length;
        // 假如两个数组的长度一致
        if(result) {
            // 挪用eq要领对数组中的元素举行迭代比较(假如数组中包含二维数组或对象, eq要领会举行深层比较)
            while(size--) {
                // 在确保两个数组都存在当前索引的元素时, 挪用eq要领深层比较(将堆数据通报给eq要领)
                // 将比较的效果存储到result变量, 假如result为false(即在比较中取得某个元素的数据不一致), 则住手迭代
                if(!( result = size in a == size in b && eq(a[size], b[size], stack)))
                    break;
            }
        }
    } else {
        // 被比较的数据为对象范例
        // 假如两个对象不是同一个类的实例(经由过程constructor属性比较), 则以为两个对象不相称
        if('constructor' in a != 'constructor' in b || a.constructor != b.constructor)
            return false;
        // 深层比较两个对象中的数据
        for(var key in a) {
            if(_.has(a, key)) {
                // size用于纪录比较过的属性数目, 由于这里遍历的是a对象的属性, 并比较b对象中该属性的数据
                // 当b对象中的属性数目过剩a对象时, 此处的逻辑建立, 但两个对象并不相称
                size++;
                // 迭代挪用eq要领, 深层比较两个对象中的属性值
                // 将比较的效果纪录到result变量, 当比较到不相称的数据时住手迭代
                if(!( result = _.has(b, key) && eq(a[key], b[key], stack)))
                    break;
            }
        }
        // 深层比较终了, 这里已能够确保在对象a中的一切数据, 对象b中也存在雷同的数据
        // 依据size(对象属性长度)搜检对象b中的属性数目是不是与对象a相称
        if(result) {
            // 遍历对象b中的一切属性
            for(key in b) {
                // 当size已到0时(即对象a中的属性数目已遍历终了), 而对象b中还存在有属性, 则对象b中的属性多于对象a
                if(_.has(b, key) && !(size--))
                    break;
            }
            // 当对象b中的属性多于对象a, 则以为两个对象不相称
            result = !size;
        }
    }
    // 函数实行终了时, 从堆中移除第一个数据(在比较对象或数组时, 会迭代eq要领, 堆中能够存在多个数据)
    stack.pop();
    // 返回的result纪录了终究的比较效果
    return result;
}

eq函数只在isEqual要领中挪用, 用于比较两个数据的值是不是相称,与 === 差别在于, eq更关注数据的值,假如举行比较的是两个复合数据范例, 不单单议比较是不是来自同一个援用, 且会举行深层比较(对两个对象的构造和数据举行比较)

_.isEqual

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

不多说了,就是内部函数eq的外部要领

_.isEmpty

_.isEmpty = function(obj) {
    // obj被转换为Boolean范例后值为false
    if(obj == null)
        return true;
    // 搜检对象或字符串长度是不是为0
    if(_.isArray(obj) || _.isString(obj))
        return obj.length === 0;
    // 搜检对象(运用for in轮回时将起首轮回对象本身的属性, 其次是原型链中的属性), 因而假如第一个属性是属于对象本身的, 那末该对象不是一个空对象
    for(var key in obj)
    if(_.has(obj, key))
        return false;
    // 一切数据范例均没有经由过程考证, 是一个空数据
    return true;
};

这个函数用于搜检数据是不是为空值, 包含”, false, 0, null, undefined, NaN, 空数组(数组长度为0)和空对象(对象本身没有任何属性)

_.isElement

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

这个函数用于考证对象是不是是一个DOM对象

_.isArray

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

这个函数用于考证一个变量是不是是数组

_.isObject

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

这个函数用于考证对象是不是是一个复合数据范例的对象(即非基础数据范例String, Boolean, Number, null, undefined)

_.isArguments

_.isArguments = function(obj) {
    return toString.call(obj) == '[object Arguments]';
};
// 考证isArguments函数, 假如运转环境没法一般考证arguments范例的数据, 则从新定义isArguments要领
if(!_.isArguments(arguments)) {
    // 关于环境没法经由过程toString考证arguments范例的, 则经由过程挪用arguments独占的callee要领来举行考证
    _.isArguments = function(obj) {
        // callee是arguments的一个属性, 指向对arguments所属函数本身的援用
        return !!(obj && _.has(obj, 'callee'));
    };
}

这个函数用于搜检一个数据是不是是一个arguments参数对象

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

这几个我就放在一同说了,他们都是经由过程Object.prototype.toString.call(obj)的值来举行推断的

_.isFinite

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

这个函数用于搜检一个数字是不是为有用数字且有用局限(Number范例, 值在负无穷大 – 正无穷大之间)

_.isNaN

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

在js里,一切数据中只要NaN与NaN不相称

_.isBoolean

_.isBoolean = function(obj) {
    // 支撑字面量和对象情势的Boolean数据
    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};

这个函数用于搜检数据是不是是Boolean范例

_.isNull

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

这个函数用于搜检数据是不是是Null值

_.isUndefined

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

这个函数用于搜检数据是不是是Undefined值

_.has

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

这个函数搜检一个属性是不是属于对象本身, 而非原型链中

_.noConflict

_.noConflict = function() {
    // previousUnderscore变量纪录了Underscore定义前_(下划线)的值
    root._ = previousUnderscore;
    return this;
};

这个函数平常用于防止定名争执或范例定名体式格局,摒弃_(下划线)定名的Underscore对象, 并返回Underscore对象

_.identity

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

这个函数返回与参数雷同的值, 平常用于将一个数据的猎取体式格局转换为函数猎取体式格局(内部用于构建要领时作为默许处置惩罚器函数)

_.times

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

这个函数的作用是使指定的函数迭代实行n次(无参数)

_.escape

_.escape = function(string) {
    return ('' + string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g, '&#x2F;');
};

这个函数用于将HTML字符串中的特别字符转换为HTML实体, 包含 & < > ” ‘ \

_.result

_.result = function(object, property) {
    if(object == null)
        return null;
    // 猎取对象的值
    var value = object[property];
    // 假如值是一个函数, 则实行并返回, 否则将直接返回
    return _.isFunction(value) ? value.call(object) : value;
};

这个函数指定一个对象的属性, 返回该属性对应的值, 假如该属性对应的是一个函数, 则会实行该函数并返回效果

_.mixin

_.mixin = function(obj) {
    // obj是一个鸠合一系列自定义要领的对象, 此处经由过程each遍历对象的要领
    each(_.functions(obj), function(name) {
        // 经由过程addToWrapper函数将自定义要领增加到Underscore构建的对象中, 用于支撑对象式挪用
        // 同时将要领增加到 _ 本身, 用于支撑函数式挪用
        addToWrapper(name, _[name] = obj[name]);
    });
};

这个函数增加一系列自定义要领到Underscore对象中, 用于扩大Underscore插件

_.template

_.template = function(text, data, settings) {
    // 模板设置, 假如没有指定设置项, 则运用templateSettings中指定的设置项
    settings = _.defaults(settings || {}, _.templateSettings);

    // 最先将模板剖析为可实行源码
    var source = "__p+='" + text.replace(escaper, function(match) {
        // 将特别符号转移为字符串情势
        return '\\' + escapes[match];
    }).replace(settings.escape || noMatch, function(match, code) {
        // 剖析escape情势标签 <%- %>, 将变量中包含的HTML经由过程_.escape函数转换为HTML实体
        return "'+\n_.escape(" + unescape(code) + ")+\n'";
    }).replace(settings.interpolate || noMatch, function(match, code) {
        // 剖析interpolate情势标签 <%= %>, 将模板内容作为一个变量与别的字符串连接起来, 则会作为一个变量输出
        return "'+\n(" + unescape(code) + ")+\n'";
    }).replace(settings.evaluate || noMatch, function(match, code) {
        // 剖析evaluate情势标签 <% %>, evaluate标签中存储了须要实行的JavaScript代码, 这里终了当前的字符串拼接, 并在新的一行作为JavaScript语法实行, 并将背面的内容再次作为字符串的最先, 因而evaluate标签内的JavaScript代码就可以被一般实行
        return "';\n" + unescape(code) + "\n;__p+='";
    }) + "';\n";
    if(!settings.variable)
        source = 'with(obj||{}){\n' + source + '}\n';
    source = "var __p='';" + "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + source + "return __p;\n";

    // 建立一个函数, 将源码作为函数实行体, 将obj和Underscore作为参数通报给该函数
    var render = new Function(settings.variable || 'obj', '_', source);
    // 假如指定了模板的添补数据, 则替代模板内容, 并返回替代后的效果
    if(data)
        return render(data, _);
    // 假如没有指定添补数据, 则返回一个函数, 该函数用于将接收到的数据替代到模板
    // 假如在递次中会屡次添补雷同模板, 那末在第一次挪用时发起不指定添补数据, 在取得处置惩罚函数的援用后, 再直接挪用会进步运转效力
    var template = function(data) {
        return render.call(this, data, _);
    };
    // 将建立的源码字符串增加到函数对象中, 平常用于调试和测试
    template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
    // 没有指定添补数据的情况下, 返回处置惩罚函数句柄
    return template;
};

这个我要引见的末了一个函数,也是我个人以为比较主要的,它是Underscore模板剖析要领, 用于将数据添补到一个模板字符串中,在模板体内, 可经由过程argments猎取2个参数, 分别为添补数据(称号为obj)和Underscore对象(称号为_)

小结

本日一口气把剩下的一切函数都引见完了,真是累感不爱啊,不过在写作这几篇博客的过程当中,我也从Underscore这个框架中学到了许多东西,包含它的文雅的代码作风(最少比我本身写的文雅),另有一个优异的库全部的架构是怎样搭建起来的。
今后我还会继承为人人分享其他的前端学问和学习心得,thx for reading, hope u enjoy

    原文作者:JoeRay61
    原文地址: https://segmentfault.com/a/1190000000536882
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞