Underscore源码分析


Posted in Javascript onDecember 30, 2015

几年前就有人说javascript是最被低估一种编程语言,自从nodejs出来后,全端(All Stack/Full Stack)概念日渐兴起,现在恐怕没人再敢低估它了。javascrip是一种类C的语言,有C语言基础就能大体理解javascript的代码,但是作为一种脚本语言,javascript的灵活性是C所远远不及的,这也会造成学习上的一些困难。

一、集合

 1.首先是几个迭代的方法。

_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
 for (i = 0, length = obj.length; i < length; i++) {
  iteratee(obj[i], i, obj);
 }
} else {
 var keys = _.keys(obj);
 for (i = 0, length = keys.length; i < length; i++) {
  iteratee(obj[keys[i]], keys[i], obj);
 }
}
// 链式调用
return obj;
 };

ES为数组同样添加了原生的forEach()方法。不同的是这里的each(forEach)方法可以对所有集合使用,函数接受三个参数(集合、迭代函数、执行环境)。

optimizeCb函数根据迭代函数参数个数的不同为不同的迭代方法绑定了相应的执行环境,forEach迭代函数同样接受三个参数(值,索引,集合)。

接下来就是for循环调用迭代函数了。 

_.map中一种更优雅的判断isArrayLike的实现方式:(只用一个for循环)

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;
  // 合理使用&&、||、?:可以大大减少代码量

还有两个特别的地方:

•将集合分成了类数组集合和对象集合。使用了isArrayLike函数:

// js的最大精确整数
 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
 var isArrayLike = function(collection) {
var length = collection != null && collection.length;
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
 }; // 如果集合有Length属性且为数字并且大于0小于最大的精确整数,则判定是类数组

 •使用了_.keys函数,Object同样有原生的keys函数,用于返回一个集合obj可被枚举的属性数组。实现比较简单,for in加上hasOwnProperty()方法。

--------------------------------------------------------------------------------

_.map,_.reduce方法原理类似.

 _.find函数和Array.some()类似,不同的是返回的是第一个使迭代结果为真的那个元素,而不是Array.some()那样返回布尔值。

_.find = _.detect = function(obj, predicate, context) {
  var key;
  if (isArrayLike(obj)) {
   key = _.findIndex(obj, predicate, context);
  } else {
   key = _.findKey(obj, predicate, context);
  }
  if (key !== void 0 && key !== -1) return obj[key];
 };
function createIndexFinder(dir) {
  return function(array, predicate, context) {
   predicate = cb(predicate, context);
   var length = array != null && array.length;
   // 如果dir为1,index为0,index+=1,index正序循环
   // 如果dir 为-1,index为length-1,index += -1反序循环
   // 判断循环条件则用了index >= 0 && index < length方法兼顾两种循环方式
   var index = dir > 0 ? 0 : length - 1;
   for (; index >= 0 && index < length; index += dir) {
    if (predicate(array[index], index, array)) return index;
   }
   return -1;
  };
 }
 _.findIndex = createIndexFinder(1);
 _.findLastIndex = createIndexFinder(-1);

值得借鉴的地方是这里的一个for循环能够根据传入的参数不同配置不同的循环顺序。

 1.集合中的其他方法基本都是基于迭代方法来实现的。

_.max = function(obj, iteratee, context) {
var result = -Infinity, lastComputed = -Infinity,
  value, computed;
if (iteratee == null && obj != null) {
 obj = isArrayLike(obj) ? obj : _.values(obj);
 for (var i = 0, length = obj.length; i < length; i++) {
  value = obj[i];
  if (value > result) {
   result = value;
  }
 }
} else {
 iteratee = cb(iteratee, context);
 _.each(obj, function(value, index, list) {
  computed = iteratee(value, index, list);
  if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
   result = value;
   lastComputed = computed;
  }
 });
}
return result;
 };

  max方法用于寻找集合中的最大值,通过循环list中的所有项,然后比较当前项和结果项,如果当前项大于结果,则将其赋给结果项,最后返回结果项。

 2.集合转换为数组

_.toArray = function(obj) {
    if (!obj) return [];
    // 如果是数组,采用了Array.prototype.slice.call(this,obj)这种方法
    if (_.isArray(obj)) return slice.call(obj);
    // 类数组对象,这里没有采用Slice方法,而是利用.map对集合进行迭代,从而返回一个数组。 _.identity该方法传入的值和返回的值相等。(主要用于迭代)
    if (isArrayLike(obj)) return _.map(obj, _.identity);
    // 普通对象,则返回由属性值组成的数组。
    return _.values(obj);
   };

数据类型

STL需要对vector、list等进行区分是因为不同的数据结构需要或者可以进行不同的实现,但underscore里面Collections和Arrays分开是什么道理呢?这也要从javascript的数据类型说起,看下图。

Underscore源码分析

Javascript 相关文章推荐
javascript 类方法定义还是有点区别
Apr 15 Javascript
javascript验证上传文件的类型限制必须为某些格式
Nov 14 Javascript
jQuery异步上传文件插件ajaxFileUpload详细介绍
May 19 Javascript
JQuery ztree 异步加载实例讲解
Feb 25 Javascript
详解js实现线段交点的三种算法
Aug 09 Javascript
在Vuex使用dispatch和commit来调用mutations的区别详解
Sep 18 Javascript
基于vue-cli、elementUI的Vue超简单入门小例子(推荐)
Apr 17 Javascript
微信小程序人脸识别功能代码实例
May 07 Javascript
vue使用微信扫一扫功能的实现代码
Apr 11 Javascript
简单了解前端渐进式框架VUE
Jul 20 Javascript
vue-drawer-layout实现手势滑出菜单栏
Nov 19 Vue.js
vue基于Teleport实现Modal组件
May 31 Vue.js
Jsonp 关键字详解及json和jsonp的区别,ajax和jsonp的区别
Dec 30 #Javascript
javascript实现禁止复制网页内容汇总
Dec 30 #Javascript
jquery实现树形菜单完整代码
Dec 29 #Javascript
javascript设置页面背景色及背景图片的方法
Dec 29 #Javascript
js获取及修改网页背景色和字体色的方法
Dec 29 #Javascript
基于JavaScript实现根据手机定位获取当前具体位置(X省X市X县X街道X号)
Dec 29 #Javascript
jQuery中attr()与prop()函数用法实例详解(附用法区别)
Dec 29 #Javascript
You might like
php 5.3.5安装memcache注意事项小结
2011/04/12 PHP
PHP处理大量表单字段的便捷方法
2015/02/07 PHP
为JavaScript提供睡眠功能(sleep) 自编译JS引擎
2010/08/16 Javascript
js bind 函数 使用闭包保存执行上下文
2011/12/26 Javascript
JavaScript中的变量声明早于赋值分析
2012/03/01 Javascript
js前台分页显示后端JAVA数据响应
2013/03/18 Javascript
node.js中的fs.writeFile方法使用说明
2014/12/14 Javascript
全面解析Bootstrap表单使用方法(表单控件状态)
2015/11/24 Javascript
理解javascript中的Function.prototype.bind的方法
2017/02/03 Javascript
vue中过滤器filter的讲解
2019/01/21 Javascript
详解JavaScript 的执行机制
2020/09/18 Javascript
如何搜索查找并解决Django相关的问题
2014/06/30 Python
Django中传递参数到URLconf的视图函数中的方法
2015/07/18 Python
Python的Flask框架及Nginx实现静态文件访问限制功能
2016/06/27 Python
Python 绘图和可视化详细介绍
2017/02/11 Python
python3中dict(字典)的使用方法示例
2017/03/22 Python
Python图片裁剪实例代码(如头像裁剪)
2017/06/21 Python
Python批处理更改文件名os.rename的方法
2018/10/26 Python
对python 调用类属性的方法详解
2019/07/02 Python
Python虚拟环境的创建和使用详解
2020/09/07 Python
HTML5新控件之日期和时间选择输入的实现代码
2018/09/13 HTML / CSS
The Beach People美国:澳洲海滨奢华品牌
2018/07/05 全球购物
美国健康和保健平台:healtop
2020/07/02 全球购物
区域总监的岗位职责
2013/11/21 职场文书
法律专业推荐信范文
2013/11/29 职场文书
季度思想汇报
2014/01/01 职场文书
校运会广播稿100字
2014/01/27 职场文书
促销活动总结
2014/04/28 职场文书
毕业大学生自荐信
2014/06/17 职场文书
学生检讨书怎么写
2014/10/09 职场文书
怎样写离婚协议书
2015/01/26 职场文书
城管年度个人总结
2015/02/28 职场文书
法定代表人免职证明
2015/06/24 职场文书
申请吧主发表的感言
2015/08/03 职场文书
原生JS中应该禁止出现的写法
2021/05/05 Javascript
Mysql如何实现不存在则插入,存在则更新
2022/03/25 MySQL