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
1 | <script src="underscore.js"></script> |
打印结果:
展开console.info(_.prototype);
打印的结果
1 | + Object |
2.从_.each()
开始
2.1 整体结构:IIFE
1 | (function(){ |
2.2 初始化_
1 | //在浏览器上是window(self),服务器上是global |
2.3 两个类型isObject
,isArrayLike
判断
为了压缩我们把常用的方法/属性独立写成变量
1 | var ArrayProto = Array.prototype, |
1 | //判断是不是对象/函数 |
2.4 上下文绑定
1 | var optimizeCb = function(func, context, argCount) { |
2.5 each方法
一个简单的版本属性
1 | _.VERSION = '0.0.1'; |
_.each
需要_.keys
1 | _.each = _.forEach = function(obj, iteratee, context) { |
_.keys
1 | _.keys = function(obj) { |
_.has
1 | _.has = function(obj, key) { |
3. 将方法放入原型中
_.prototype
的打印结果是:console.logo(_.prototype);
不在其原型中(其实我们定义_.** 也并没有放到原型中)如果不放到原型中,第5
部分不能成功调用。
需要使用的工具类
1 | _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) { |
混入,并且执行混入
1 | _.mixin = function(obj) { |
将_.*
形式的方法放入到原型中
对于_(obj).*
的方法,已经将数据([this._wrapped])传入到函数中(var func = _[name] = obj[name])。当然包括原型中的方法。
_mixin
是支持用户自己扩展方法的。如:
1 | _.mixin({ |
4.链式
1 | _.chain = function(obj) { |
使用:
1 | _.chain(arr) |
1 | var chainResult = function(instance, obj) { |
5. 支持形如_(obj).each
方式逻辑
使用形式如下:
1 | _([1, 2, 3]).each(function(n) { |
我们看到第2.2
部分初始化的代码
1 | var _ = function(obj) { |
执行的逻辑会是
- 1.
if (!(this instanceof _)) return new _(obj);
再次调用构造函数,这个时候的this从window已经指向了_
; - 2.
this._wrapped = obj; //存放数据
初始化函数中console.log(this)
的结果是:
6.避免冲突
1 | var previousUnderscore = root._; |
使用:
1 | var $$ = _.noConflict();//previousUnderscore |
全部代码贴在这里,可以在我的github查看具体的所有代码
1 | /** |
参考阅读: