underscore源码解析之一: 实现简易版underscore框架
前言
每次询问他人,如何更好更快的深入学习js呢?大部分的回答都是看源码呀~~~
源码的魅力在哪呢?
作为一个已经从事前端好几年的萌新来说,实在惭愧。还没领教过。。。
今天就从易入手且仍不失精华的underscore源码开始吧~~~~
本次是基于1.8.3版本来研究的。
初始版本实现
underscore的通常使用方式都是_.funcName(), 其实就是把 _ 对象当成一个全局对象使用,所有的方法都挂载到 _ 对象上。
基于此,我们的初始版本可以如下实现:
(function() {
var root = this;
var _ = {};
root._ = _;
_.init = function(obj) {
return obj
}
})()
var obj = {name:'hi'};
console.log(_.init(obj));
然而,具体实现肯定不会如此简单,我们往下具体分析。
this 判别
首先,underscore 是一个工具函数库,是可以在多端环境下运行的,所以 var root = this 需要重新判断属于那个环境。
通常使用 underscore库的环境如下:
- 浏览器
- node
- Web Worker
- 微信小程序
-
浏览器环境中提供window全局对象 this默认指向window;
-
node中的全局对象是global this默认指向空对象;
-
Web Worker 所在的全局对象与主线程不同,是无法访问 Window 对象和 Document 对象。但能通过 self 访问到 Worker 环境中的全局对象,并且window === self;
-
node的vm模块(沙盒模块),runInContext方法中,是不存在window 和 global变量的,但可通过this访问到全局对象;
-
在微信小程序中,window 和 global 都是 undefined,加上又强制使用严格模式,this 为 undefined,挂载就会发生错误,可以默认为空对象{};
所以代码更改如下:
(function() {
var root = (typeof self === 'object' && self.self === self && self) ||
(typeof global === 'object' && global.global === global && global) ||
this ||
{};
var _ = {};
root._ = _;
_.init = function(obj) {
return obj
}
})()
var obj = {name:'hi'};
console.log(_.init(obj));
_ 对象定义
underscore 其实支持两种调用方式:
- 函数式风格调用;
- 面向对象风格调用;
上述原始版本的实现就是基于面向对象方式的,那如何实现函式调用呢?
既然要实现函数调用,首先它就得是个函数。
在javascript中,函数就是对象,也可以给函数增加属性的。所以,对 _ 对象定义如下:
var _ = function(obj) {
if(obj instanceof _) return obj;
if(!(this instanceof _)) return new _(obj);
this._wrapped = obj;
}
接下来就是给 _ 对象添加属性(方法)
//添加版本号
_.VERSION = '0.1';
mixin
好的,目前我们基本已经实现了以函数的形式调用,但是以对象的形式调用方法时,就会发现会发生报错。
这个时候就需要我们的原型登场啦~~
把所有需要用到的方法都绑定到原型对象上,也就是 _.prototype上,然后再调用不就可以了嘛~
_.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 func.apply(_, args);
}
})
return _;
};
_.mixin(_);
至于上述用到的each() functions()方法如下:
(注解即是说明)
var ArrayProto = Array.prototype;
var push = ArrayProto.push;
var MAX_ARRAY_INDEX= Math.pow(2,53) -1;
//判断是否是数组(包含伪数组)
function isArrayLike(collection) {
var length = collection.length;
return typeof length === 'number' && length >=0 && length <= MAX_ARRAY_INDEX;
}
_.each = function(obj, callback) {
var length,i = 0;
if(isArrayLike(obj)){
length = obj.length;
for(;i<length;i++) {
// 当回调函数返回false时,就中止循环
if(callback.call(obj[i], obj[i], i) === false){
break;
}
}
}else{
// 对象
for(i in obj) {
if(callback.call(obj[i], obj[i], i) === false){
break;
}
}
}
return obj;
}
_.isFunction = function(obj){
return typeof obj === 'function' || false;
}
// 用于获取方法标识符数组
_.functions = function(obj) {
var names = [];
for(var key in obj){
if(_.isFunction(obj[key])){
names.push(key);
}
}
return names.sort();
}
导出
最后一步就是导出啦~
if(typeof exports !== 'undefined' && !exports.nodeType){
if(typeof module !== 'undefined' && !module.nodeType && module.exports ) {
exports = module.exports = _;
} else {
exports._ = _;
}
} else {
root._ = _;
}
总结
那么,最终我们的代码整合就是这样啦(来个全貌)~
(function(){
// this判别
var root = (typeof self === 'object' && self.self === self && self) ||
(typeof global === 'object' && global.global === global && global) ||
this ||
{};
// _定义
var _ = function(obj) {
if(obj instanceof _) return obj;
if(!(this instanceof _)) return new _(obj);
this._wrapped = obj;
}
// this中添加_
if(typeof exports !== 'undefined' && !exports.nodeType){
if(typeof module !== 'undefined' && !module.nodeType && module.exports ) {
exports = module.exports = _;
}else{
exports._ = _;
}
} else {
root._ = _;
}
_.VERSION = '0.1';
// 调用mixin 将_上的方法绑定到_.prototype上
// 会用到 _.each()
// _.functions()
// isArrayLike()
// _.isFunction()
var ArrayProto = Array.prototype;
var push = ArrayProto.push;
var MAX_ARRAY_INDEX= Math.pow(2,53) -1;
function isArrayLike(collection) {
var length = collection.length;
return typeof length === 'number' && length >=0 && length <= MAX_ARRAY_INDEX;
}
_.each = function(obj, callback) {
var length,i = 0;
// 数组和类数组
if(isArrayLike(obj)){
length = obj.length;
for(;i<length;i++) {
// 当回调函数返回false时,就中止循环
if(callback.call(obj[i], obj[i], i) === false){
break;
}
}
}else{
// 对象
for(i in obj) {
if(callback.call(obj[i], obj[i], i) === false){
break;
}
}
}
return obj;
}
_.isFunction = function(obj){
return typeof obj === 'function' || false;
}
_.functions = function(obj) {
var names = [];
for(var key in obj){
if(_.isFunction(obj[key])){
names.push(key);
}
}
return names.sort();
}
// 自定义方法,添加自己所需方法
_.log = function(obj){
return obj;
}
_.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 func.apply(_, args);
}
})
return _;
};
_.mixin(_);
})()