1. 作用域
与其他第三方库一样,Underscore也通过立即执行函数来包裹自己的业务逻辑。
目的:
- 避免全局污染:所有库中的逻辑,库所定义和使用的变量全部被封装到了该函数的作用域中。
- 隐私保护:但凡在立即执行函数中声明的函数、变量等,除非是自己想暴露,否则绝无可能在外部获得。
(function(root){
var _ = function(){
}
root._ = _
})(this)
2. _ 对象
underscore有下划线的意思,所以underscore通过一个下划线变量 _ 来标识自身。
注: _ 是一个函数对象,之后所有的api都会被挂载到这个对象上,例如_.map()
(funtion(root){
var _ = function(obj){
}
_.map = function(){
//balalala
}
// ......
root._ = _
})(this)
3. _()
虽然Underscore推崇函数式编程,但也支持面向对象风格的函数调用,仅需要通过_()来包裹对象即可。
当我们进行如下调用时:_([1,2,3])
会创建一个新的underscore对象实例(从而能够调用underscore提供的方法),并在this._wrapped中存储传入的数据。
var _ = function(obj){
if(obj instanceof _){
return obj
}
if(!(this instanceof _)){
return new _(obj)
}
this._wrapped = obj
}
当使用面向对象的方式调用时,例如传入[1,2,3],函数首先会判断是不是_的实例,如果是直接返回这个实例对象,如果不是则创建一个实例对象new _(obj),然后将参数[1,2,3]赋值给_wrapped,以备后用。
4. mixin
- mixin(混入)模式是增加代码复用度的一个广泛的设计模式。
- _.mixin(obj):为underscore对象混入obj具有的功能
- mixin主要实现了 _ 的原型对象的方法调用
源码如下:
_.mixin = function(obj) {
_.each(_.functions(obj), function(name) {
var func = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result(this, func.apply( _ , args));
}
});
}
_.mixin(_)
最后一句是执行这个mixin函数,执行完后会将 _ 上挂载的方法都挂载到原型对象prototype上,以方便面向对象时的调用
5. 链式调用 _.chain()
- _.chain(obj):为underscore对象的方法增加链式调用能力
- _.chain源码如下:
_.chain = function(obj){
var instance = _(obj)
instance._chain = true
return instance
}
比较简单,就是拿到这个实例对象,赋了一个属性 _chain为true,再返回这个实例对象
- underscore还提供了一个帮助函数result,该函数将会判断方法调用结果,如果该方法的调用者被标识了需要链化,则链化当前的方法执行结果。
- 源码如下:
var chainResult = function(instance , obj){
return instance._chain ? _(obj).chain() : obj ;
}
============这是分割线============
接下来我们就看看,当调用_.unique([1,2,3,1])的时候怎么执行的
先附上源码:
(function(root){
var _ = function(){}
_.unique = function(arr,callback){
var ret = [];
var target, i = 0;
for (; i < arr.length; i++) {
var target = callback ? callback(arr[i]) : arr[i];
if (ret.indexOf(target) === -1) {
ret.push(target);
}
}
return ret;
}
root._ = _
})(this)
代码就不解释了,比较简单,就是在 _ 对象上新建一个unique方法直接调用就行。
如果使用面向对象的调用方式呢?就需要这样写了 _([1,2,3,1]).chain().unique()
先附上源码:
(function(root){
var push = Array.prototype.push;
//面向对象调用时返回_的实例,然后将参数存储在_wrapped中
var _ = function(obj) {
if (obj instanceof _) {
return obj;
}
if (!(this instanceof _)) {
return new _(obj);
}
this._wrapped = obj;
}
//给_定义一个去重的函数
_.unique = function(arr, callback) {
var ret = [];
var target, i = 0;
for (; i < arr.length; i++) {
var target = callback ? callback(arr[i]) : arr[i];
if (ret.indexOf(target) === -1) {
ret.push(target);
}
}
return ret;
}
//定义一个遍历函数
_.each = function(target, callback) {
console.log(target)
var key, i = 0;
if (_.isArray(target)) {
var length = target.length;
for (; i < length; i++) {
callback.call(target, target[i], i);
}
} else {
for (key in target) {
callback.call(target, key, target[key]);
}
}
}
//开启链接式的调用
_.chain = function(obj) {
var instance = _(obj);
instance._chain = true;
return instance;
}
//辅助函数 obj 数据结果
var result = function(instance, obj) {
//_(obj).chain() 会对传入的obj创建一个新的实例对象,返回出去
return instance._chain ? _(obj).chain() : obj;
}
//获取_上所有绑定的函数
_.functions = function(obj) {
var result = [];
var key;
for (key in obj) {
result.push(key);
}
return result;
}
//类型检测
_.isArray = function(array) {
return toString.call(array) === "[object Array]";
}
//将函数加载到
_.mixin = function(obj) {
_.each(_.functions(obj), function(name) {
var func = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result(this, func.apply(this, args));
}
});
}
root._ = _
})(this)
以上基本就是underscore的源码框架了。
- 先解释
_.mixin(_)
- 函数一开始就会调用这个函数,传入的参数就是我们自定义的参数,例如[1,2,3],然后调用each方法,each方法传入两个参数,第一个参数_.functions(obj),就是获取绑定在 _ 上面的所有函数,返回的是一个数组。
- 继续往里走,第二个参数回去调用数组中各个函数名,拿到这个函数。
- 继续,之后会给 _ 的prototype绑定函数,
var args = [this._wrapped]
就是获取我们传进来的参数[1,2,3,1]
push.apply(args,arguments)
如果后面调用方法时,还有参数,则合并push进args里。(如果不了解apply的可以看看我之前写的call apply bind的理解这篇文章) - 最后调用result这个函数,第一个参数this就是当前的实例对象,第二个参数就是调用unique的方法。
- 最后返回的结果,是调用chain后的实例对象
_([1,2,3,1]).chain().unique()
- 首先_([1,2,3,1])会生成一个实例对象,instance
- 调用chain方法,给instance创建一个属性chain并且设置为true,表示可以链式调用
- 调用unique方法,此处的unique方法不是 _ 的方法,而是_.prototype的方法,也就是mixin中的函数。
- 最后返回result的结果,也就是执行unique方法后的实例对象。