概述

很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以就了结研究underscore源码这一心愿吧。

underscore.js源码研究(1)

underscore.js源码研究(2)

underscore.js源码研究(3)

underscore.js源码研究(4)

underscore.js源码研究(5)

underscore.js源码研究(6)

underscore.js源码研究(7)

underscore.js源码研究(8)

参考资料:underscore.js官方注释undersercore 源码分析undersercore 源码分析 segmentfault

函数式编程

之前一直没搞懂为什么要用map而不用for循环,原来是因为map是函数式编程中的迭代式思维

对于一个迭代来说,他至少由两个部分构成:被迭代集合当前迭代过程。在underscore中,当前迭代过程就是一个函数,叫做iteratee,它会对被迭代集合进行处理。示例如下:

_.map = _.collect = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
results = Array(length);
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};

可以看到,iteratee函数会先经过cb方法进行处理,而cb方法是通过optimizeCb来处理一个函数的,原代码如下:

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);
};

optimizeCb的代码很长:

var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
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);
};
};

虽然代码很长,但是optimizeCb方法只起了1个作用:把context这个上下文绑定在func函数上面。那些长长的case只是原样返回func不同参数的情形:func只带一个参数,比如times的迭代函数;func带三个参数,比如map,some等的迭代函数;func带四个参数,比如reduce等的迭代函数。

剩余参数的实现

我们常常有这样一个需求,就是想要使一个函数接受多个参数,但是在调用的时候可以不必输入全部参数,我们可以这样实现:

function add(x, y, z) {
z = z == null ? 0 : z;
return x + y + z;
} add(1,2,0); //输出3
add(1,2); //输出3

但是如果我们想要使add能够接受无限多个参数呢?(这在函数式编程里面是非常正常的)我们可以把剩余的参数装在一个rest参数里面:

function add(x, rest) {
return _.reduce(rest,function(accum, current){
return accum+current;
},x);
} add(1, [2, 3]); //输出6
add(1, [2, 3, 4]); //输出10
add(1, [2, 3, 4, 5]); //输出15

所以问题变成了怎么把add进行封装使得add(1,2,3,4)会变成add(1, [2,3,4])的形式。

对此underscore利用了restArguments方法,源码和注释如下:

var restArguments = function(func, startIndex) {
//不输入startIndex则自动取最后一个为rest
startIndex = startIndex == null ? func.length - 1 : +startIndex;
//接受一个函数为参数,返回一个包装后的函数,参数用arguments获取
return function() {
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length),
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
switch (startIndex) {
//原函数只接受一个rest参数
case 0: return func.call(this, rest);
//原函数接受1个参数 + rest参数
case 1: return func.call(this, arguments[0], rest);
//原函数接受2个参数 + rest参数
case 2: return func.call(this, arguments[0], arguments[1], rest);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
//原函数接受2个以上参数 + rest参数
return func.apply(this, args);
};
};

使用起来是这样的:

var addWithRest = _.restArguments(add, 1);
addWithRest(1,2,3,4); //10

值得一提的是,es6发明了一个语法,叫做Rest parameters。这种语法比上面的方法更加人性化,示例如下:

//...theArgs这种写法就是Rest parameters的写法
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
} console.log(sum(1, 2, 3));
// expected output: 6 console.log(sum(1, 2, 3, 4));
// expected output: 10

注意:restArguments方法是最新加入的,cdn上的1.8.3 版本的underscore里面没有restArguments方法。

underscore.js源码研究(3)的更多相关文章

  1. underscore.js源码研究(8)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  2. underscore.js源码研究(7)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  3. underscore.js源码研究(6)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  4. underscore.js源码研究(5)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  5. underscore.js源码研究(4)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  6. underscore.js源码研究(2)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  7. underscore.js源码研究(1)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  8. underscore.js源码解析(五)—— 完结篇

    最近公司各种上线,所以回家略感疲惫就懒得写了,这次我准备把剩下的所有方法全部分析完,可能篇幅过长...那么废话不多说让我们进入正题. 没看过前几篇的可以猛戳这里: underscore.js源码解析( ...

  9. underscore.js 源码

    underscore.js 源码 underscore]JavaScript 中如何判断两个元素是否 "相同" Why underscore 最近开始看 underscore.js ...

随机推荐

  1. 20155312 2016-2017-2 《Java程序设计》第九周学习总结

    20155312 2016-2017-2 <Java程序设计>第九周学习总结 课堂内容总结 两个类有公用的东西放在父类里. 面向对象的三要素 封装 继承 多态:用父类声明引用,子类生成对象 ...

  2. 为什么要使用日志管理?syslog和Windows事件日志

    为什么要使用日志管理?syslog和Windows事件日志 日志管理 - 确保网络安全的先决条件 日志给予您有关网络活动的第一手信息.日志管理确保日志中隐藏的网络活动数据转换为有意义的可操作的安全信息 ...

  3. Le Chapitre X

    Il se trouvait dans la région des astéroïdes 325, 326, 327, 328, 329 et 330. Il commença donc par le ...

  4. java socket之多人聊天室Demo

    一.功能介绍 该功能实现了一个类似QQ的最简单多人聊天室,如下图所示. 二.目录结构 三.服务端 1)SocketServer类,该类是服务端的主类,主要负责创建聊天窗口,创建监听客户端的线程: pa ...

  5. VSFTPD+MYSQL+PAM

    一           需要的软件包: 1. vsftpd: 2.MySQL 3.pam_mysql 4.pam-devel 5.openssl 6.tcp-wrappers 二            ...

  6. 构造函数的prototype和constructor属性

    Car.prototype = { name:'BMW', height:1400, long:4900 } function Car(color,owner){ this.color = color ...

  7. 如何使用阿里巴巴iconfont矢量图片

    ①打开此网站http://www.iconfont.cn/ , 选择需要的几个图形 → 加入购物车 → 添加至项目 → 给项目随便命名 → 点击电线连接并点击代码 → 复制代码到css ②在body里 ...

  8. 安卓4.2用adb 获取屏幕分辨率等信息

    在终端输入adb shell dumpsys: 终端会打印出一些service list 用adb shell dumpsys+service名即可查询相应的信息. 屏幕分辨率用adb shell d ...

  9. Swift3 页面顶部实现拉伸效果代码

    //懒加载 //顶部需要拉伸自定义视图 lazy var headView:MyHeaderView = { //let hframe = CGRect(x: 0, y: 0, width: swid ...

  10. CentOS7安装BugFree

    1. 安装apache yum install httpd 2. 安装mysql yum install mysql yum install mysql-server 注意: 1)已安装mysql的跳 ...