一步一步学习underscore的封装和扩展方式
前言
underscore虽然有点过时,这些年要慢慢被Lodash给淘汰或合并。
但通过看它的源码,还是能学到一个库的封装和扩展方式。
第一步,不污染全局环境。
ES5中的JS作用域是函数作用域。
函数内部可以直接读取全局变量,当然函数外部无法读取函数内的局部变量。
所以,我们在匿名函数里啪啪啪写代码,妈妈再也不会担心修改到全局变量。
(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方法实例。
(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);
};
});
})();
一步一步学习underscore的封装和扩展方式的更多相关文章
- underscore的封装和扩展
// 1. 不污染全局环境 (function() { // 2. 保留之前同名变量 var previousUnderscore = window._; var _ = function(obj) ...
- jQuery的封装和扩展方式
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper
前言:之前学习过很多的Bootstrap组件,博主就在脑海里构思:是否可以封装一套自己Bootstrap组件库呢.再加上看到MVC的Razor语法里面直接通过后台方法输出前端控件的方式,于是打算仿照H ...
- 一步一步学习SignalR进行实时通信_5_Hub
原文:一步一步学习SignalR进行实时通信_5_Hub 一步一步学习SignalR进行实时通信\_5_Hub SignalR 一步一步学习SignalR进行实时通信_5_Hub 前言 Hub命名规则 ...
- 一步一步学习Vue(十一)
本篇继续学习vuex,还是以实例为主:我们以一步一步学Vue(四)中讲述的例子为基础,对其改造,基于vuex重构一遍,这是原始的代码: todolist.js ; (function () { var ...
- C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)
前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...
- 12.Linux软件安装 (一步一步学习大数据系列之 Linux)
1.如何上传安装包到服务器 有三种方式: 1.1使用图形化工具,如: filezilla 如何使用FileZilla上传和下载文件 1.2使用 sftp 工具: 在 windows下使用CRT 软件 ...
- (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性
转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...
- (转) 一步一步学习ASP.NET 5 (二)- 通过命令行和sublime创建项目
转发:微软MVP 卢建晖 的文章,希望对大家有帮助. 注:昨天转发之后很多朋友指出了vNext的命名问题,原文作者已经做出了修改,后面的标题都适用 asp.net 5这个名称. 编者语 : 昨天发了第 ...
随机推荐
- javascript:算法之斐波那契数列
一 //1,1,2,3,5,8,13,21这个数列 斐波那契 数列(肥波哪弃) //得到第9项是几? /*******************************111111111递归的思想*** ...
- UFS
● UFS vs eMMC 1. UFS有分离的读写通道,可以同时进行读写操作(双向),但是eMMC在同一时刻只能读或写. 2. UFS有一个命令队列,将命令进行排序.因此,多个命令可以同时处理,从而 ...
- GDC2016 Epic Games【Bullet Train】 新风格的VR-FPS的制作方法
追求“舒适”和“快感”的VR游戏设计方法 http://game.watch.impress.co.jp/docs/news/20160318_749016.html [Bullet Tr ...
- Day1(2016/1/21)——Beginning
今日进度: helloworld 了解android项目的基本结构,框架与资源分离 四大组件:活动:服务:广播接收器:内容提供器 活动: 通常每个项目有一到多个主活动,也可没有 所有组件必须在Andr ...
- Nodejs开发(1.Sublime Text 3配置)
本例使用Sublime Text 3开发 原因: 1. 有开发提示: 2. 非常easy的调试运行: 下载Sublime Text 3,官网地址:http://www.sublimetext.com/ ...
- 【译】使用UIKit进行面向对象的编程
在WWDC 2015上,Apple谈了Swift中面向协议编程的话题,令人深思.在那之后,好像每个人都在讨论关于协议扩展的话题,这个新的语言特性使每个人都有所困惑. 我阅读了许多关于Swift中协议的 ...
- Python开发【杂货铺】:五个知识点搞定作用域
1.块级作用域 想想此时运行下面的程序会有输出吗?执行会成功吗? #块级作用域 if 1 == 1: name = "lzl" print(name) for i in range ...
- iOS中文API之NSLayoutconstraint
AutoLayout为开发者提供了一种不同于传统对于UI元素位置指定的布局方法.以前,不论是在IB里拖放,还是在代码中写,每个UIView都会有自己的frame属性,来定义其在当前视图中的位置和尺寸. ...
- 面向对象php基本格式、构造、析构、访问修饰符
<?php//面向对象//1.类//由众多的对象抽象出来的 //2.对象//一切皆对象//由类实例化出来的 //基本形式class 类名{ 成员变量 成员方法 } $对象名 =new 类名 $对 ...
- 让Placeholder在IE中燥起来
网上有好多关于这方面解决兼容性问题的文章,很多招式,学会这一招,让你轻松搞定Placeholder的兼容性,叫我好人,拿走,不谢.... placeholder属性是HTML5 中为input添加的. ...