Underscore 是一个 JavaScript 工具库,它提供一整套函数编程的实用功能。他弥补了 jQuery 没有实现的功能,同时又是Backbone 必不可少的部分。

underscore.js源码加上注释也就1千多行,用underscore.js作为阅读源码的开始是一个不错的开始,当然阅读源码的同时,手也不能停下来。下面我会写几篇博客,一边分析源码,一边根据源码重新DIY一份(_underscore.js),基于版本:1.8.3

underscore.js分为集合(Collections)、数组(Arrays)、函数(Functions)、对象(Objects)、工具函数(Utility)五大部分。

所有代码挂在我的github上。

1.简单的应用Demo

    <script src="underscore.js"></script><!-- <script src="../DIY/1/_underscore.js"></script> --><script>console.info(_);console.info(_.prototype);/*** 数组处理*/_.each([1, 2, 3], function(ele,idx) {console.log(idx + " : " +ele);});_.each([1, 2, 3], console.log);_.each({one: 1, two: 2, three: 3}, console.log);</script>

打印结果:

展开console.info(_.prototype);打印的结果

+ Object//... ...- ()each: - ()escape: - ()every: - ()extend: //... ...
__proto__: Object

2.从_.each()开始

2.1 整体结构:IIFE

    (function(){}())

2.2 初始化_

    //在浏览器上是window(self),服务器上是globalvar root = typeof self == 'object' && self.self === self && self ||typeof global == 'object' && global.global === global && global ||this;//形式:_([1, 2, 3]).each(function(ele) {});会执行下面的var _ = function(obj) {if (obj instanceof _) return obj;if (!(this instanceof _)) return new _(obj);this._wrapped = obj;  //存放数据};//形式:_.each([1, 2, 3], function(ele, idx) { });不会执行上面的函数,而是直接通过全局的_,寻找定义在_或者其原型上的方法root._ = _;

2.3 两个类型isObject,isArrayLike判断

为了压缩我们把常用的方法/属性独立写成变量

    var ArrayProto = Array.prototype,ObjProto = Object.prototype;var push = ArrayProto.push,toString = ObjProto.toString,hasOwnProperty = ObjProto.hasOwnProperty;var nativeIsArray = Array.isArray,nativeKeys = Object.keys;
    //判断是不是对象/函数_.isObject = function(obj) {var type = typeof obj;return type === 'function' || type === 'object' && !!obj;};//属性中是否有keyvar property = function(key) {return function(obj) {return obj == null ? void 0 : obj[key];};};var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;var getLength = property('length');var isArrayLike = function(collection) {var length = getLength(collection);return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;};

2.4 上下文绑定

    var optimizeCb = function(func, context, argCount) {//void 0 === undefined 返回tureif (context === void 0) return func;return function() {return func.apply(context, arguments);};};

2.5 each方法

一个简单的版本属性

_.VERSION = '0.0.1';

_.each需要_.keys

     _.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); //(element, index, list)}} else {var keys = _.keys(obj);for (var i = 0, length = keys.length; i < length; i++) {iteratee(obj[keys[i]], keys[i], obj); //(value, key, list)}}return obj; //返回obj方便链式调用};

_.keys

    _.keys = function(obj) {//不是对象/函数,返回空数组if (!_.isObject(obj)) return [];//使用ES5中的方法,返回属性(数组)if (nativeKeys) return nativeKeys(obj);var keys = [];for (var key in obj)if (_.has(obj, key)) keys.push(key);//兼容IE< 9 暂时省略// if (hasEnumBug) collectNonEnumProps(obj, keys);return keys;};

_.has

    _.has = function(obj, key) {return obj != null && hasOwnProperty.call(obj, key);};

3. 将方法放入原型中

_.prototype的打印结果是:

console.logo(_.prototype);不在其原型中(其实我们定义_.** 也并没有放到原型中)如果不放到原型中,第5部分不能成功调用。
需要使用的工具类

    _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {_['is' + name] = function(obj) {return toString.call(obj) === '[object ' + name + ']';};});// IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).var nodelist = root.document && root.document.childNodes;if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {_.isFunction = function(obj) {return typeof obj == 'function' || false;};}_.functions = _.methods = function(obj) {var names = [];for (var key in obj) {if (_.isFunction(obj[key])) names.push(key);}return names.sort();};

混入,并且执行混入

     _.mixin = function(obj) {_.each(_.functions(obj), function(name) {var func = _[name] = obj[name];_.prototype[name] = function() {var args = [this._wrapped]; //_ 保存的数据objpush.apply(args, arguments);//原型方法中的数据和args合并到一个数组中return chainResult(this, func.apply(_, args));//见第`4`部分//将_.prototype[name]的this指向 _ 【func.apply(_,args)已经//将func的this指向了 _ ,并且传了参数】,返回带链式的obj,即是 _ };});return _;};_.mixin(_);

_.*形式的方法放入到原型中
对于_(obj).*的方法,已经将数据([this.wrapped])传入到函数中(var func = [name] = obj[name])。当然包括原型中的方法。

_mixin是支持用户自己扩展方法的。如:

_.mixin({capitalize: function(string) {return string.charAt(0).toUpperCase() +                   string.substring(1).toLowerCase();}
});
_("fabio").capitalize();
=> "Fabio"

4.链式

    _.chain = function(obj) {var instance = _(obj);instance._chain = true;return instance;};

使用:

    _.chain(arr).each(function(ele) {console.log(ele);})//可以继续链式
    var chainResult = function(instance, obj) {return instance._chain ? _(obj).chain() : obj;};

5. 支持形如_(obj).each方式逻辑

使用形式如下:

     _([1, 2, 3]).each(function(n) {console.log(n * 2);});

我们看到第2.2部分初始化的代码

    var _ = function(obj) {console.log(this);if (obj instanceof _) return obj;if (!(this instanceof _)) return new _(obj);this._wrapped = obj;  //存放数据};

执行的逻辑会是
- 1.if (!(this instanceof _)) return new _(obj);再次调用构造函数,这个时候的this从window已经指向了_;
- 2.this._wrapped = obj; //存放数据

初始化函数中console.log(this)的结果是:

6.避免冲突

    var previousUnderscore = root._;_.noConflict = function() {root._ = previousUnderscore;return this;};

使用:

    var $$ = _.noConflict();//previousUnderscore$$.each([1, 2, 3], function(ele, idx) {console.info(idx + " : " + ele);});

全部代码贴在这里,可以在我的github查看具体的所有代码

/*** DIY 一个underscore库1*/
(function() {//在浏览器上是window(self),服务器上是globalvar root = typeof self == 'object' && self.self === self && self ||typeof global == 'object' && global.global === global && global ||this;var previousUnderscore = root._;var _ = function(obj) {console.log(this);if (obj instanceof _) return obj;if (!(this instanceof _)) return new _(obj);this._wrapped = obj; //存放数据};root._ = _;var ArrayProto = Array.prototype,ObjProto = Object.prototype;var push = ArrayProto.push,toString = ObjProto.toString,hasOwnProperty = ObjProto.hasOwnProperty;var nativeIsArray = Array.isArray,nativeKeys = Object.keys;//判断是不是对象/函数_.isObject = function(obj) {var type = typeof obj;return type === 'function' || type === 'object' && !!obj;};var optimizeCb = function(func, context, argCount) {//void 0 === undefined 返回tureif (context === void 0) return func;return function() {return func.apply(context, arguments);};};_.VERSION = '0.0.1';var property = function(key) {return function(obj) {return obj == null ? void 0 : obj[key];};};var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;var getLength = property('length');var isArrayLike = function(collection) {var length = getLength(collection);return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;};_.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); //(element, index, list)}} else {var keys = _.keys(obj);for (i = 0, length = keys.length; i < length; i++) {iteratee(obj[keys[i]], keys[i], obj); //(value, key, list)}}return obj; //返回obj方便链式调用};_.keys = function(obj) {//不是对象/函数,返回空数组if (!_.isObject(obj)) return [];//使用ES5中的方法,返回属性(数组)if (nativeKeys) return nativeKeys(obj);var keys = [];for (var key in obj)if (_.has(obj, key)) keys.push(key);//兼容IE< 9};_.has = function(obj, key) {return obj != null && hasOwnProperty.call(obj, key);};/*链式*/_.chain = function(obj) {var instance = _(obj);instance._chain = true;return instance;};/*_.chain(arr).each(function(ele) {console.log(ele);})*/var chainResult = function(instance, obj) {return instance._chain ? _(obj).chain() : obj;};/*** 方法放入原型中*/_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {_['is' + name] = function(obj) {return toString.call(obj) === '[object ' + name + ']';};});// IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).var nodelist = root.document && root.document.childNodes;if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {_.isFunction = function(obj) {return typeof obj == 'function' || false;};}_.functions = _.methods = function(obj) {var names = [];for (var key in obj) {if (_.isFunction(obj[key])) names.push(key);}return names.sort();};_.mixin = function(obj) {_.each(_.functions(obj), function(name) {var func = _[name] = obj[name];_.prototype[name] = function() {var args = [this._wrapped]; //_ 保存的数据objpush.apply(args, arguments);//原型方法中的数据和args合并到一个数组中return chainResult(this, func.apply(_, args));//将_.prototype[name]的this指向 _ (func.apply(_,args)已经//将func的this指向了 _ ,并且传了参数),返回带链式的obj,即是 _ };});return _;};_.mixin(_);/*** 避免冲突*/_.noConflict = function() {root._ = previousUnderscore;return this;};
}());

参考阅读:
- http://underscorejs.org/
- underscorejs中文:http://www.bootcss.com/p/underscore/
- UnderscoreJS精巧而强大工具包
- JS高手进阶之路:underscore源码经典

动手DIY一个underscorejs库及underscorejs源码分析1相关推荐

  1. 深度学习库 caffe使用 源码分析 依赖库分析 caffe glog gflags openBlas prototxt yolo_darknet 转 caffe

    深度学习库 caffe使用 源码分析 依赖库分析 caffe glog gflags openBlas 本文github链接 yolo_darknet 转 caffe caffe 安装 Caffe代码 ...

  2. 一个简单的IPmsg程序源码分析(二)

    离上篇一个简单的IPmsg程序源码分析(一)已经基本半个月(上篇最初发布在点点上面,后边纠结了一下还是选择了博客园),利用空闲的时间终于把源码的构架和一些细节基本都搞清楚了,总的来说是很简单的一个客户 ...

  3. Android网络库之Okio源码分析

    今天来扒一扒Square公司的IO流的库Okio,现在越来越多Android项目都在使用Square公司的网络开源全家桶,即 Okio + OkHttp + Retrofit.这三个库的层级是从下网上 ...

  4. bfd库使用-nm源码分析

    bfd介绍 想深入了解elf等可执行文件的原理(包括结构.运行等细节),用bfd库作切入点是比较好的选择. BFD是Binary format descriptor的缩写, 即二进制文件格式描述,是很 ...

  5. java web开源项目源码_超赞!推荐一个专注于Java后端源码分析的Github项目!

    大家好,最近有小伙伴们建议我把源码分析文章及源码分析项目(带注释版)放到github上,这样小伙伴们就可以把带中文注释的源码项目下载到自己本地电脑,结合源码分析文章自己本地调试,总之对于学习开源项目源 ...

  6. dll 源码_【技术分享】 | 一个JAVA内存马的源码分析

    前言 偶然接触到了这样一个JAVA内存马,其作者也是冰蝎的作者,项目地址: https://github.com/rebeyond/memShell 正好最近在接触JAVA,借此机会学习下大佬的代码, ...

  7. java开启一个线程_【jdk源码分析】java多线程开启的三种方式

    1.继承Thread类,新建一个当前类对象,并且运行其start()方法 1 packagecom.xiaostudy.thread;2 3 /** 4 * @desc 第一种开启线程的方式5 *@a ...

  8. 自己动手写CSDN博客提取器源码分析之三:处理网页保存为pdf文件

    下面我讲下处理pdf文件的,这里我用了PD4ML来处理的,原因有几个:(1).它对CSS的支持做的很好:(2).可以处理图片(很爽吧)(3).可以处理中文,不过麻烦一些.基础的过程可以看我的另外一篇帖 ...

  9. Linux内核源码分析之内存管理

    本文站的角度更底层,基本都是从Linux内核出发,会更深入.所以当你都读完,然后再次审视这些功能的实现和设计时,我相信你会有种豁然开朗的感觉. 1.页 内核把物理页作为内存管理的基本单元. 尽管处理器 ...

  10. java 源码分析_Java 源代码编译成 Class 文件的过程分析

    原标题:Java 源代码编译成 Class 文件的过程分析 在上篇文章< Java三种编译方式:前端编译 JIT编译 AOT编译 >中了解到了它们各有什么优点和缺点,以及前端编译+JIT编 ...

最新文章

  1. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的连接恢复和命令拦截...
  2. OVS DPDK vhost-user详解(十一)
  3. 菜单与工具条的同步 APP_STANDARD.SYNCHRONIZE
  4. 【Qt】QWidget类详解(函数篇)
  5. java 反射api_Java的反射API
  6. LOJ#6282. 数列分块入门 6
  7. 聊聊 vue 生命周期
  8. leetcode讲解--169. Majority Element
  9. DIY(也即Build Your Own) vSAN时,选择SSD需要注意的事项
  10. Get!读懂数据科学和机器学习,看这文就够了!
  11. Android之AbsoluteLayout(绝对布局)
  12. C++ new 解析重载
  13. python type判断_python判断type与isinstance的区别
  14. matlb:图像的几何矩,中心矩,Hu不变矩(含代码)
  15. 安装各种工具/第三方库(随时更新)
  16. 软件工程 网络工程,职业方向是怎样的?选择之前一定要了解!
  17. CSS居中对齐的各种方式
  18. 洛龙区:加快布局大数据产业
  19. Access 一些内部函数(Access 帮助里) .
  20. Python环境下OpenCV视频流的多线程处理方式

热门文章

  1. oracle 数据字典画报,数据字典视图之:DBA_LOGSTDBY_LOG结构
  2. 谷歌面试题:男孩多还是女孩多?
  3. PHPOK 5.2.009 发布
  4. springboot农村饮用水海量数据存储平台毕业设计源码061205
  5. CUDA 编程简介(上)
  6. WiFiSpoof for Mac(MAC地址修改软件)
  7. 18-12-19 美国7-11连锁店
  8. 【每日一网】Day20:A MultiPath Network for Object Detection(MPN)简单理解
  9. Excel中的智能模糊查询下拉菜单,比下拉列表强20倍
  10. ios 11 屏幕适配问题!