粗鄙之语 2017-12-17
// 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,所以,不会有特别大的问题。
...未完待续