Underscore.js 源码分析(第五出)

前言

这篇主要是功能函数。

// 功能函数
// ------------------
// 决定以构造函数还是以普通函数的形式调用
var executeBound = function (sourceFunc, boundFunc, context, callingContext, args) {
    // 如果 callingContext 是 boundFunc 的实例,则返回 sourceFunc 的执行结果(当普通函数调用)
    if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
    // 如果前端的条件不满足,则是以构造函数的形式调用
    // 创建一个原型(prototype)指向 sourceFunc.prototype 的对象,类似继承
    var self = baseCreate(sourceFunc.prototype);
    // 保存 sourceFunc 函数(上下文绑定了 self) 的执行结果
    var result = sourceFunc.apply(self, args);
    // 如果 result 是对象,则直接返回 result
    if (_.isObject(result)) return result;
    // 如果是以构造函数的形式调用,并且 sourceFunc 的执行结果不是对象,则返回 self 实例
    return self;
};


// 绑定上下文
_.bind = restArguments(function (func, context, args) {
    // 如果不是函数,抛出异常
    if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
    var bound = restArguments(function (callArgs) {
        // 如果经 _.bind() 方法绑定后的函数通过了 new 创建了一个实例,那么此时
        // this 则为 bound 函数的实例,executeBound 方法内部就是通过这个来判断是否是 new 调用
        return executeBound(func, bound, context, this, args.concat(callArgs));
    });
    // 返回 bound 函数
    return bound;
});


// 返回一个把函数跟参数绑定后的新函数
_.partial = restArguments(function (func, boundArgs) {
    var placeholder = _.partial.placeholder;
    var bound = function () {
        // 保存 _.partial 调用时传入的参数个数(不包含第一个参数)
        var position = 0, length = boundArgs.length;
        // 创建一个长度为 length 的数组
        var args = Array(length);
        // 遍历预置的参数
        for (var i = 0; i < length; i++) {
            // 如果 bondArgs === placeholder 则返回 arguments 中对应位置的参数
            // 这里可以让你更灵活的调整参数位置
            args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
        }
        // 遍历调用新函数时传入的参数 arguments
        // 把预置参数和调用函数时传入的参数(剩余的参数)组成一个数组 args
        while (position < arguments.length) args.push(arguments[position++]);
        // 返回 executeBound
        // _.partial 这个方法没有绑定上下文的作用
        return executeBound(func, bound, this, this, args);
    };
    return bound;
});


// 把 _ 挂载到 _.partial.placeholder 上
_.partial.placeholder = _;


// 批量上下文绑定
_.bindAll = restArguments(function (obj, keys) {
    keys = flatten(keys, false, false);
    var index = keys.length;
    if (index < 1) throw new Error('bindAll must be passed function names');
    while (index--) {
        var key = keys[index];
        obj[key] = _.bind(obj[key], obj);
    }
});


// 缓存计算结果函数
_.memoize = function (func, hasher) {
    var memoize = function (key) {
        // 缓存 cache
        var cache = memoize.cache;
        // 计算出 cache 中的 key 值(如果有传 hasher 则取 hasher 执行结果 否则取 key)
        var address = '' + (hasher ? hasher.apply(this, arguments) : key);
        if (!has(cache, address)) cache[address] = func.apply(this, arguments);
        return cache[address];
    };
    memoize.cache = {};
    return memoize;
};


// 延迟执行函数跟(只是对 setTimeout 进行了封装)
_.delay = restArguments(function (func, wait, args) {
    return setTimeout(function () {
        return func.apply(null, args);
    }, wait);
});


// 下个事件循环再执行函数,底层还是利用了 setTimeout 实现
_.defer = _.partial(_.delay, _, 1);


// 节流
_.throttle = function (func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};


    var later = function () {
        previous = options.leading === false ? 0 : _.now();
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
    };


    var throttled = function () {
        // 记录时间戳
        var now = _.now();
        // 如果 previous 为 0,并且 options.leading 为 false
        if (!previous && options.leading === false) previous = now;
        // 算出剩余时间
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        // 如果 remaining 小于等于零,说明到了执行时间
        // 或者 remaining 大于 wait,说明系统时间被修改了
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            // 保存当前时间作为上一次时间
            previous = now;
            // 执行函数
            result = func.apply(context, args);
            // 如果 timeout 存在,重置 timeout,context,args 为 null
            if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {  // 如果 timeout 不存在,并且 options.trailing 不为 false 时
            timeout = setTimeout(later, remaining);
        }
        return result;
    };
    // 取消节流执行函数
    throttled.cancel = function () {
        clearTimeout(timeout);
        previous = 0;
        timeout = context = args = null;
    };


    return throttled;
};


// 防抖
_.debounce = function (func, wait, immediate) {
    var timeout, result;
    var later = function (context, args) {
        timeout = null;
        // 如果有传 args 说明是调用了 _.delay()
        if (args) result = func.apply(context, args);
    };
    var debounced = restArguments(function (args) {
        // 清空定时器
        if (timeout) clearTimeout(timeout);
        // 是否马上执行一次
        if (immediate) {
            var callNow = !timeout;
            // wait 毫秒之后执行
            timeout = setTimeout(later, wait);
            // 如果 timeout 为 null,说明为首次
            if (callNow) result = func.apply(this, args);
        } else {
            // wait 毫秒之后执行
            timeout = _.delay(later, wait, this, args);
        }
        return result;
    });
    // 取消防抖执行函数
    debounced.cancel = function () {
        clearTimeout(timeout);
        timeout = null;
    };


    return debounced;
};


// 把函数 func 作为 wrapper 函数的第一个参数
_.wrap = function (func, wrapper) {
    return _.partial(wrapper, func);
};


// 返回一个 predicate 取反的函数.
_.negate = function (predicate) {
    return function () {
        return !predicate.apply(this, arguments);
    };
};


// 返回复合函数,上一个函数结果作为下一个函数的参数(注意:从函数数组中的最后一个开始遍历)
_.compose = function () {
    var args = arguments;
    var start = args.length - 1;
    return function () {
        var i = start;
        var result = args[start].apply(this, arguments);
        while (i--) result = args[i].call(this, result);
        return result;
    };
};


// 运行指定次数(times)后才执行 func
_.after = function (times, func) {
    return function () {
        if (--times < 1) {
            return func.apply(this, arguments);
        }
    };
};


// 返回一个有限制调用次数(times - 1 次)的函数
_.before = function (times, func) {
    var memo;
    return function () {
        if (--times > 0) {
            memo = func.apply(this, arguments);
        }
        if (times <= 1) func = null;
        return memo;
    };
};


// 返回一个只会执行一次的函数
_.once = _.partial(_.before, 2);


// 对外暴露 restArguments 方法
_.restArguments = restArguments;
声明

1.原创文章,不经本站同意,不得以任何形式转载,如有不便,请多多包涵!

2.本文永久链接:http://yunkus.com/post/5d9c280a7d5f17e6

3.如果觉得本文对你有帮助,或者解决了你的问题,不妨扫一扫右边的二维码打赏支持,你的一分一毫,可能会让世界变得更美好。

微信
扫一扫,赏我
支付宝
扫一扫,赏我