关于underscore.js源码的一些理解与笔记

Feb 13, 2015

623

在公司接触到的一些项目中,用到了underscore.js,但一直没时间去研究underscore.js的源码。临近过年,闲散时间多了一些,便看了underscore.js的源码,顺便把自己的一些理解记录下来。

1、_采用强制new模式,自调用构造函数

我们先来看一个例子:

function Person()
{
  this.name = "wen";
}

Person.prototype.age = 25;

var p1 = new Person(); //object
var p2 = Person(); //undefined
console.log(p1.age); //25
console.log(window.name); //wen

p2在调用构造函数时没有使用new操作符,会产生不同的结果,并且name会被添加到全局变量中,同时p2也没有原型链中的属性。(注:ES5严格模式下this并不会指向全局对象)。

我们看一下underscore中的处理,首先检测obj是否是_的实例,如果是直接返回obj,否则继续检查this对象是否是_的实例,如果不是,用new操作符实例_并返回。

代码如下:

var _ = function(obj) {
  if (obj instanceof _) return obj;
  if (!(this instanceof _)) return new _(obj);
  this._wrapped = obj;
};

这样做的好处就是能够保证我们调用的_对象不会丢失掉原型的链接,并且在没有使用new操作符时也能正常工作。

2、obj.length === +obj.length

在underscore中,多次用到了obj.length === +obj.length。首先我们来看一下ES5中对一元操作符+的描述。

这段话大概意思如下:

  1. 计算一元操作符后边的表达式,得到一个值;

  2. 将1中得到的值进行数值转换,并返回。

也就是+obj.length返回一个数值,obj.length === +obj.length这个表达式的意是就是说检查obj.length是不是数值,并且不为NaN,它可以改写成typeof obj.length === 'number' && (!isNaN(obj.length))。String、Function、Array类型的对象都具有length属性,而Object类型的对象,如果没有length属性,obj.length就会返回undefined。

在这里,说到一元操作符+,顺便提一下bootstrap.js中插件的+号。

+function ($) {

//the code

}(jQuery);

根据ES5中的描述,必定会执行+号后面的匿名函数。其效果跟如下代码一致:

(function($){

//the Code

}(jQuery));

3、为啥void 0

在underscore中,多处使用void 0判断对象是否存在。在ES5中,void操作符描述如下:

这段话的意思大概如下:

  1. 执行UnaryExpression,得到结果expr;
  2. 调用GetValue(expr);(GetValue(V):获取V的值);
  3. 返回undefined。

注:GetValue必须被调用,即使它的值是无用的,但是可能会有可见其他的效果。

也就是说,void 0 === undefined。在underscore中,之所以要用void 0,主要是因为ES3中(比如IE9以下),undefined可以被覆写。

在html中,我们也经常采用javascript:void(0)表示一个无效的链接。

4、优化isFunction

在underscore中,作者对isFunction进行了优化。我们先来看一下underscore中的代码

_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
  _['is' + name] = function(obj) {
    return toString.call(obj) === '[object ' + name + ']';
  };
});

if (typeof /./ !== 'function') {
  _.isFunction = function(obj) {
    return typeof obj == 'function' || false;
  };
}

使用Object.prototype.toString.call(obj)来定义isFunction,紧接着,判断/./类型是否为function,如果不是则覆写isFunction,采用typeof来判断对象对象是否为function。

在这里,为啥需要添加typeof /./ !== 'function'呢?

/./是一个正则表达式,在chrome9以上浏览器中返回类型为object,而在chrome9及以下浏览器中会返回function,这个判断主要是为了兼容低版本的chrome浏览器。

在这里顺便对 || 操作符和 && 操作符做一下说明。

我们看一下ES5中 || 操作符的描述:

  1. 执行操作符左边表达式,得到结果lref;
  2. 调用GetValue(lref),得到lref的值为lval;
  3. 将lval转化为布尔值,若为真,则返回lval,否则继续执行;
  4. 执行操作符右边表达式,得到结果rref;
  5. 调用GetValue(rref),并将得到的值返回。

再来看一下&&操作符的描述

  1. 执行操作符左边表达式,得到结果lref;
  2. 调用GetValue(lref),得到lref的值为lval;
  3. 将lval转化为布尔值,若为假,则返回lval,否则继续执行;
  4. 执行操作符右边表达式,得到结果rref;
  5. 调用GetValue(rref),并将值返回。

5、+0 与 -0

Javascript中,+0 === -0 会返回true。但在实际应用中,+0和-0还是有一些区别,0前面的符号可能会影响到计算结果, 1 / 0 = Infinity, 1 / -0 = -Infinity。在underscore中,为了能够区别+0和-0,做了如下判断:

if (a === b) return a !== 0 || 1 / a === 1 / b;

检测a是否严格等于b,若是,检测a是否严格不等于0,如果为true,直接返回true,否则继续判断1 / a 是否严格等于1 / b,若是返回true,否则返回false。这样子,便能够区别出+0和-0。

JavaScript 」相关文章

Wen's Blog

文章归档 » 文章标签 » 博主:吴文伟,Web开发爱好者,专注于前端开发,该博客用于记录和分享平时遇到的一些问题以及知识。

订阅

联系方式

链接