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 相关文章推荐
js字符串转成JSON
Nov 07 Javascript
js实现身份证号码验证的简单实例
Feb 19 Javascript
require.js配合插件text.js实现最简单的单页应用程序
Jul 12 Javascript
用JS动态设置CSS样式常见方法小结(推荐)
Nov 10 Javascript
原生JS实现九宫格抽奖效果
Apr 01 Javascript
使用JS实现气泡跟随鼠标移动的动画效果
Sep 16 Javascript
vue-lazyload图片延迟加载插件的实例讲解
Feb 09 Javascript
JS实现调用本地摄像头功能示例
May 18 Javascript
微信小程序实现人脸检测功能
May 25 Javascript
BootstrapValidator实现表单验证功能
Nov 08 Javascript
ES5新增数组的实现方法
May 12 Javascript
vue mvvm数据响应实现
Nov 11 Javascript
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异步执行的详解
2013/06/03 PHP
php函数与传递参数实例分析
2014/11/15 PHP
PHP 中提示undefined index如何解决(多种方法)
2016/03/16 PHP
JS计算网页停留时间代码
2014/04/28 Javascript
js QQ客服悬浮效果实现代码
2014/12/12 Javascript
JS实现的图片预览插件与用法示例【不上传图片】
2016/11/25 Javascript
angular.js + require.js构建模块化单页面应用的方法步骤
2017/07/19 Javascript
Node.js 使用递归实现遍历文件夹中所有文件
2017/09/18 Javascript
Angular将填入表单的数据渲染到表格的方法
2017/09/22 Javascript
js实现Tab选项卡切换效果
2020/07/17 Javascript
Vue2.0设置全局样式(less/sass和css)
2017/11/18 Javascript
react学习笔记之state以及setState的使用
2017/12/07 Javascript
JavaScript引用类型Function实例详解
2018/08/09 Javascript
JavaScript面向对象程序设计创建对象的方法分析
2018/08/13 Javascript
小程序识别身份证,银行卡,营业执照,驾照的实现
2019/11/05 Javascript
React实现轮播效果
2020/08/25 Javascript
详解Vue的七种传值方式
2021/02/08 Vue.js
python基础教程之Hello World!
2014/08/29 Python
python编码最佳实践之总结
2016/02/14 Python
python生成式的send()方法(详解)
2017/05/08 Python
Python之自动获取公网IP的实例讲解
2017/10/01 Python
python游戏地图最短路径求解
2019/01/16 Python
django框架中ajax的使用及避开CSRF 验证的方式详解
2019/12/11 Python
python psutil监控进程实例
2019/12/17 Python
PyCharm取消波浪线、下划线和中划线的实现
2020/03/03 Python
Python中lru_cache的使用和实现详解
2021/01/25 Python
html5使用canvas绘制太阳系效果
2014/12/15 HTML / CSS
SIDESTEP荷兰:在线购买鞋子
2019/11/18 全球购物
《商鞅南门立木》教学反思
2014/02/16 职场文书
遗嘱公证书标准样本
2014/04/08 职场文书
文明寝室申报材料
2014/05/12 职场文书
暖通工程师岗位职责
2014/06/12 职场文书
2016年社区“我们的节日·中秋节”活动总结
2016/04/05 职场文书
MySQL 亿级数据导入导出及迁移笔记
2021/06/18 MySQL
前端JavaScript大管家 package.json
2021/11/02 Javascript
MySQL优化及索引解析
2022/03/17 MySQL