underscore 是最适合初级人士阅读的源码,在阅读源码时,有一些有趣的实现,记录如下。
基于 underscore1.8.3。
- // Establish the root object, `window` (`self`) in the browser, `global`
- // on the server, or `this` in some virtual machines. We use `self`
- // instead of `window` for `webWorker` support.
- var root = typeof self == 'object' && self.self === self && self ||
- typeof global == 'object' && global.global === global && global ||
- this ||
- {};
- // Save the previous value of the `_` variable.
- var previousUnderscore = root._;
- // .......
- _.noConflict = function() {
- root._ = previousUnderscore;
- return this;
- };
在浏览器情况下,self 是 window 自身的引用。上面的语法主要是为了保证在 sever 端和服务端都能正常获得根对象。
将 root._ 存起来,是为了防止命名冲突。调用 noConflict 方法,就能把原来的 _ 恢复,然后重新赋值到不冲突的变量上即可。
在 underscore 源码常看到会将一些常用的方法保留起来。
- // Save bytes in the minified (but not gzipped) version:
- var ArrayProto = Array.prototype, ObjProto = Object.prototype;
- var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
- // Create quick reference variables for speed access to core prototypes.
- var push = ArrayProto.push,
- slice = ArrayProto.slice,
- toString = ObjProto.toString,
- hasOwnProperty = ObjProto.hasOwnProperty;
这样做的好处有两个:
题外话:实际上,为了更好得提高性能,通常将变量保存到局部作用域,检索将会加快。
- var chainResult = function(instance, obj) {
- // 如果_chain为true,则return一个加了链式属性的underscore对象。
- return instance._chain ? _(obj).chain() : obj;
- };
- // Add your own custom functions to the Underscore object.
- // 可以把自己写的扩展方法通过mixin加入到underscore (_) 上。
- _.mixin = function(obj) {
- _.each(_.functions(obj),
- function(name) {
- var func = _[name] = obj[name];
- _.prototype[name] = function() {
- var args = [this._wrapped];
- push.apply(args, arguments);
- return chainResult(this, func.apply(_, args));
- };
- });
- return _;
- };
- // Add all of the Underscore functions to the wrapper object.
- // 对underscore使用mixin,可以将全部实例方法挂载到原型上。
- _.mixin(_);
- // 链式调用方法,不过是加了一个Boolean型开关,来对返回值做判断
- _.chain = function(obj) {
- var instance = _(obj);
- instance._chain = true;
- return instance;
- };
.mixin 方法用来把 obj 上的方法,都内置到下划线 上,相当于 jquery 的 extends 方法。
此处调用 _ mixin(_);实际上,是将 _ 上的方法,都挂载到 _ .prototype 上,以便于之后的链式调用。
再来关注一下 .chain 这个方法,调用之后会返回一个 underscore 对象,并且把该对象的 chain 属性赋为 true。在 chainResult 这个方法里,会对当前的这个实例的 _ chain 属性进行判断,如果调用了 chain 方法,就认为接下来会进行链式调用,就会将这个实例包裹之后,继续返回。
链式调用的关键就在于,函数 return 原对象。
- var _ = function(obj) {
- // 如果是underscore的实例,就直接返回obj
- if (obj instanceof _) return obj;
- // 如果this不是underscore的实例,就new一个新的underscore实例并返回
- if (! (this instanceof _)) return new _(obj);
- // 将this._wrapped属性置为obj
- this._wrapped = obj;
- };
需要注意第二步,this 的指向,因为如果直接调用 _ 函数,则 this 指向为 window,使用 new 构造函数,this 指向为新创建的对象。
接下来对一些函数做分析。
这个方法是一个优化方法,根剧不同的参数个数,返回不同的调用方式。
好处有三:
- _.isArray = nativeIsArray || function(obj) {
- return toString.call(obj) === '[object Array]';
- };
目前判断数组的方法,较为公认的做法就是通过 toString,查看是否是 [object Array]。在 ES5 之后,原生带有 isArray 方法,兼容性不是很完善,IE9 之后支持。可以把这个改一下,作为 polyfill。
在 zepto 中的 isArray 实现稍有不同:
- isArray
- =
- Array
- .
- isArray
- ||
- function
- (object)
- {
- return
- object
- instanceof
- Array
- }
这两种方法有所区别,zepto 的实现在 iframe 的情况下会有 bug,具体参见 这篇博客 。
不过由于移动端通常不会使用 iframe,所以,不会有特别大的问题。
... 未完待续
来源: http://www.cnblogs.com/liuyongjia/p/8053489.html