underscore虽然有点过期,这些年要慢慢被Lodash给淘汰或合并。
但经过看它的源码,仍是能学到一个库的封装和扩展方式。javascript
ES5中的JS做用域是函数做用域。
函数内部能够直接读取全局变量,固然函数外部没法读取函数内的局部变量。
因此,咱们在匿名函数里啪啪啪写代码,妈妈不再会担忧修改到全局变量。java
(funtion(){ var _ = function(obj) { return new wrapper(obj); }; var wrapper = function(obj) { this._wrapped = obj; }; window._ = _; })()
首先,咱们要知道,
声明在_
.prototype的方法是专门给_
实例用。
声明在wrapper.prototype的方法是给wrapper方法实例用。
underscore的_
方法是一个工厂方法,_
方法返回的是私有wrapper方法实例。
那么如何把_
的静态方法赋予给wrapper方法实例?且看如下代码。数组
(function(){ var _ = function(obj) { return new wrapper(obj); }; var wrapper = function(obj) { this._wrapped = obj; }; var result = function(obj) { return obj; }; var ArrayProto = Array.prototype, forEach = ArrayProto.forEach, push = ArrayProto.push; _.each = forEach; _.type = function(obj){ return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase(); } _.isFunction = function(fn){ return (_.type(fn) == "function"); } _.functions = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); } _.mixin = function(obj) { forEach.call(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); return result( func.apply(_, args)); }; }); }; // _.prototype 指向 wrapper.prototype _.prototype = wrapper.prototype; // 修复_实例的原型链 _.prototype.constructor = _; // 这里经过mixin方法把_的静态方法,赋值给wrapper实例 _.mixin(_); window._ = _; })();
测试代码缓存
var str = _("str"); str.type(); //"String" str instanceof _; //true
链式调用使得操做同一个对象时很是方便。
实现的思路是,从新包装调用的函数,缓存函数调用结果,使其返回的值是wrapper方法实例。app
(function(){ var _ = function(obj) { return new wrapper(obj); }; var wrapper = function(obj) { this._wrapped = obj; }; // 链式包装函数 var result = function(obj, chain) { return chain ? _(obj).chain() : obj; }; // 触发可链式函数 wrapper.prototype.chain = function() { // this._chain用来标示当前对象是否使用链式操做 this._chain = true; return this; }; // 当触发可链式后,用这个来取结果值 wrapper.prototype.value = function() { return this._wrapped; }; var ArrayProto = Array.prototype, forEach = ArrayProto.forEach; // 这些数组方法须要包装如下才能够链式调用 forEach.call(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; wrapper.prototype[name] = function() { var wrapped = this._wrapped; // 调用Array对应的方法并返回结果 method.apply(wrapped, arguments); var length = wrapped.length; if ((name == 'shift' || name == 'splice') && length === 0) { delete wrapped[0]; } return result(wrapped, this._chain); }; }); // 这些数组方法自己可链式调用 forEach.call(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; wrapper.prototype[name] = function() { return result(method.apply(this._wrapped, arguments), this._chain); }; }); window._ = _; })();
测试代码模块化
var a =_([1, 2]).chain().push(3).push(4).push(5); a.value(); // [1, 2, 3, 4, 5] [1,2].push(3).push(4).push(5); // Uncaught TypeError: [1,2].push(...).push is not a function(…)
ES6 Modules以前,UMD很盛行,咱们要支持。函数
(function(){ var _ = function(obj) { return new wrapper(obj); }; var wrapper = function(obj) { this._wrapped = obj; }; if (typeof define === 'function' && define.amd) { define('underscore', [], function() { return _; }); } else if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { window['_'] = _; } )();
(function() { var _ = function(obj) { return new wrapper(obj); }; if (typeof define === 'function' && define.amd) { define('underscore', [], function() { return _; }); } else if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { window['_'] = _; } var wrapper = function(obj) { this._wrapped = obj; }; var result = function(obj, chain) { return chain ? _(obj).chain() : obj; }; wrapper.prototype.chain = function() { this._chain = true; return this; }; wrapper.prototype.value = function() { return this._wrapped; }; var ArrayProto = Array.prototype, forEach = ArrayProto.forEach, push = ArrayProto.push; _.each = forEach; _.type = function(obj){ return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase(); } _.isFunction = function(fn){ return (_.type(fn) == "function"); } _.functions = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); } _.mixin = function(obj) { forEach.call(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); return result( func.apply(_, args),this._chain); }; }); }; _.prototype = wrapper.prototype; _.prototype.constructor = _; _.mixin(_); forEach.call(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; wrapper.prototype[name] = function() { var wrapped = this._wrapped; method.apply(wrapped, arguments); var length = wrapped.length; if ((name == 'shift' || name == 'splice') && length === 0) { delete wrapped[0]; } return result(wrapped, this._chain); }; }); forEach.call(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; wrapper.prototype[name] = function() { return result(method.apply(this._wrapped, arguments), this._chain); }; }); })();