文章目录
  1. 1. bind
  2. 2. bindAll
  3. 3. flatten
  4. 4. 总结

在学习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的使用以及内部实现。并且夯实基础。

日积月累,相信自己一定会有很大进步。

文章目录
  1. 1. bind
  2. 2. bindAll
  3. 3. flatten
  4. 4. 总结