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 相关文章推荐
基于jquery的3d效果实现代码
Mar 23 Javascript
jQuery选择器中含有空格的使用示例及注意事项
Aug 25 Javascript
js确认删除对话框适用于a标签及submit
Jul 10 Javascript
JS和JQ的event对象区别分析
Nov 24 Javascript
Bootstrap Navbar Component实现响应式导航
Oct 08 Javascript
实例讲解javascript实现异步图片上传方法
Dec 05 Javascript
浅谈webpack打包之后的文件过大的解决方法
Mar 07 Javascript
浅谈Webpack核心模块tapable解析
Sep 11 Javascript
用Object.prototype.toString.call(obj)检测对象类型原因分析
Oct 11 Javascript
layui动态绑定事件的方法
Sep 20 Javascript
详解JavaScript中的Object.is()与&quot;===&quot;运算符总结
Jun 17 Javascript
Vue详细的入门笔记
May 10 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
浅析is_writable的php实现
2013/06/18 PHP
php实例分享之mysql数据备份
2014/05/19 PHP
php+ajax实现文章自动保存的方法
2014/12/30 PHP
PHP测试成功的邮件发送案例
2015/10/26 PHP
PHP实现基本留言板功能原理与步骤详解
2020/03/26 PHP
JavaScript URL参数读取改进版
2009/01/16 Javascript
js 匿名调用实现代码
2009/06/19 Javascript
JavaScript 动态添加表格行 使用模板、标记
2009/10/24 Javascript
js变量以及其作用域详解
2020/07/18 Javascript
javascript学习笔记(五) Array 数组类型介绍
2012/06/19 Javascript
document.execCommand()的用法小结
2014/01/08 Javascript
jQuery DOM删除节点操作指南
2015/03/03 Javascript
jQuery实现简单滚动动画效果
2016/04/07 Javascript
JQuery 动态生成Table表格实例代码
2016/12/02 Javascript
JavaScript 巧学巧用
2017/05/23 Javascript
vue使用技巧及vue项目中遇到的问题
2018/06/04 Javascript
Python的Urllib库的基本使用教程
2015/04/30 Python
自动化Nginx服务器的反向代理的配置方法
2015/06/28 Python
Python简单检测文本类型的2种方法【基于文件头及cchardet库】
2016/09/18 Python
python爬取足球直播吧五大联赛积分榜
2018/06/13 Python
Sanic框架路由用法实例分析
2018/07/16 Python
python 实现在一张图中绘制一个小的子图方法
2019/07/07 Python
详解Python文件修改的两种方式
2019/08/22 Python
详解Python self 参数
2019/08/30 Python
Python内置方法和属性应用:反射和单例(推荐)
2020/06/19 Python
在Tensorflow中实现leakyRelu操作详解(高效)
2020/06/30 Python
python实现MySQL指定表增量同步数据到clickhouse的脚本
2021/02/26 Python
通过Canvas及File API缩放并上传图片完整示例
2013/08/08 HTML / CSS
iframe在移动端的缩放的示例代码
2018/10/12 HTML / CSS
某公司Java工程师面试题笔试题
2016/03/27 面试题
企业授权委托书范本
2014/04/02 职场文书
小学生安全演讲稿
2014/04/25 职场文书
户籍证明模板
2014/09/28 职场文书
钢琴师观后感
2015/06/12 职场文书
分享一个vue实现的记事本功能案例
2022/04/11 Vue.js
Redis全局ID生成器的实现
2022/06/05 Redis