underscore源码学习2
更新日期:
在学习underscore源码过程中,发现了自己很多基础知识点薄弱环节,而且源码也很大程度比较精简细致。
目前我学习的方法是先看api文档,确定功能需求,以及参数,然后自己实现具体的代码,每个工具方法一般也就10来行代码不到。然后对比自己的代码和源码的差异。
今天学习了三个比较重要的工具方法。分别是bind,bindAll,flatten。
以下代码是我自己编写和结合源码实现的。源码考虑的更深入。如果照搬源码,我担心没有自己的思想。
bind
给函数显式指定上下文,即this指向。
用法:
var func = function(greeting){
return greeting + ': ' + this.name;
};
func = _.bind(func, {name: 'moe'}, 'hi');
console.log(func()); // hi: moe
bind 方法有三个参数。
第一个目标函数,第二个指定的上下文对象,第三个传递给目标函数的参数(可选)
ES5新增Function.bind方法。我们可以使用此方法实现。不支持的浏览器使用apply方法。
var nativeBind = Function.bind;
var slice = Array.prototype.slice;
if (nativeBind && func.bind === nativeBind)
return nativeBind.apply(func, slice.call(arguments, 1));
//不支持bind方法兼容方案
var args = slice.call(arguments, 2);
return function() {
return func.apply(context,args);
}
在我编写代码过程中,出现了一个有意思的事情,把arguments的第二个以上参数转成数组过程中。
我的代码是:
var args = slice.call(arguments).slice(2);
不知道把2直接放在call方法第二个参数,此处没有优化好。
bindAll
_.bindAll(object, *methodNames)
*methodNames string类型。
给一堆函数指定上线文,必选。
用法:
var buttonView = {
label : 'underscore',
onClick: function(){ alert('clicked: ' + this.label); },
onHover: function(){ console.log('hovering: ' + this.label); }
};
_.bindAll(buttonView, 'onClick', 'onHover');
$('#btn').bind('click', buttonView.onClick); //'clicked: underscore'
一开始我还有点看不懂。.bindAll方法是让onClick,onHover两个函数指定上下文为buttonView,以后不管在那里调用他,该函数上下文都是buttonView对象。也就是说.bindAll方法直接重写了buttonView对象里面的指定函数。
代码实现:
_.bindAll = function(object) {
var args = slice.call(arguments, 1);
_.each(args, function(key) {
object[key] = _.bind(object[key], object);
});
return object;
};
先获取要指定函数的方法名数组args,然后通过each方法遍历该数组,重写object对象。
flatten
功能:对数组扁平化操作。将一个嵌套多层的数组 array(数组) (嵌套可以是任何层数)转换为只有一层的数组。 如果你传递 shallow参数为true,数组将只减少一维的嵌套。
例子:
_.flatten([1, [2], [3, [[4]]]]);
=> [1, 2, 3, 4];
_.flatten([1, [2], [3, [[4]]]], true);
=> [1, 2, 3, [[4]]];
看到这个题目想起了三月份做的阿里实习笔试题,当时的要求是就是把多维数组转成一维数组。后来我自己使用递归的方法实现了。
但后来发到网上,有网友给出了一个比较简单的方法
[1,[[4,5,6],2,[[[7,8,9]]],3]].toString().split(",");
今天回过头来发现这个方法不可行,因为这样返回的数组的元素成string类型了,而原数组是number类型,所以还是需要递归的方法。
以下是我重写的代码:
_.flatten = function(array, shallow) {
var newArr = [];
(function(arr) {
for (var i = 0, len = arr.length; i < len; i++) {
if(arr[i].length){ //判断是不是嵌套数组
if (shallow) {
for (var j = 0, lenj = array[i].length; j<lenj; j++) {
newArr.push(array[i][j]);
}
} else {
arguments.callee(arr[i]); //递归调用
}
}else{
newArr.push(arr[i]);
}
}
})(array);
return newArr;
};
内部使用了一个自执行的函数,遍历array,如果子元素有length属性,那么表示是嵌套数组,并且继续判断shallow的值,如果为true,那么直接把遍历这个子项,把元素添加到newArr上去,如果shollow为false,则递归这个嵌套数组。
最后返回新数组。
总结
通过这种自己编写代码的方法,熟悉了各个api的使用以及内部实现。并且夯实基础。
日积月累,相信自己一定会有很大进步。