underscore中的function类函数解析
underscore是一个非常不错的基础javascript库,他提供了很多实用的方法,弥补了javascript原生 API调用的一些不足,读他的文档的时候,读到array,object,uitlity的时候已经非常兴奋了。觉得足够用了。但是我看到function这部分的时候,发现这些函数真的非常的有意义,结合源代码来看看这部分function相关的功能。
_.bind方法
最常见的方法。作用是改变默认的function中的this指向。需要说明的是在ECMA 5这个版本中function已经自带了一个bind方法,参见这里(该文章具体介绍了bind的集中使用场景)。bind的使用方法是:
_.bind(function, object, [*arguments])
下面是一个使用demo:
var func = function(greeting){
//this指向的是bind的第二个参数
// greeting 是bind的第三个参数
return greeting + ': ' + this.name
};
// bind返回的是一个新的function对象
var newfunc = _.bind(func, {name : 'moe'}, 'hi');
func();
原本以为这个bind的源码会很简单,无非就是用apply返回新的function,但是看源码发现挺讲究:
_.bind = function bind(func, context) {
var bound, args;
//如果function存在原生的bind方法使用原生的bind
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
//不是function,抛异常
if (!_.isFunction(func)) throw new TypeError;
//将后面的参数转化成数组
args = slice.call(arguments, 2);
return bound = function() {
//如果当前的this已经指向的一个function的实例,就不需要再改变this的指向,因为此时的function已经作为一个构造函数在使用
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
//否则function视为构造函数使用,要保证this为构造函数的实例
ctor.prototype = func.prototype;
var self = new ctor;
//将function强制转换成一个类的构造函数
var result = func.apply(self, args.concat(slice.call(arguments)));
//Object(result) === result 只有当result是Object时才会成立,基本的数据类型如number,string则不成立
if (Object(result) === result) return result;
return self;
};
};
代码实现还是考虑了普通函数调用,构造函数调用,通过成员函数调用的情况,逻辑实现的很全面。
_.bindAll
bindAll方法可以将一个对象中所有的成员函数的this都指向这个对象,什么情况下对象的成员函数的this不指向对象呢?比如:
var buttonView = {
label : 'underscore',
onClick : function(){ alert('clicked: ' + this.label); },
onHover : function(){ console.log('hovering: ' + this.label); }
};
_.bindAll(buttonView);
//当成员函数作为事件监听的时候,因为默认的事件监听,this都会指向当前事件源
//bindAll之后可以保证onClick中的this仍指向buttonView
jQuery('#underscore_button').bind('click', buttonView.onClick);
_.memoize(function, [hashFunction])
该方法可以缓存函数返回结果,如果一个函数计算需要很长的时间,多次反复计算可以只计算一次缓存结果,默认的缓存key是函数调用时的第一个参数,也可以自己定义function(第二个参数)来计算key
_.memoize = function(func, hasher) {
var memo = {};//缓存存放位置
//_.indentity默认取数组第一个元素
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
delay : _.delay(function, wait, [*arguments])
delay方法在指定的wait后面执行函数与setTimeout功能一致
defer: _.defer(function, [*arguments])
defer也是延迟执行方法,不同的是他能保证在当前堆栈中的所有的代码跑完之后再执行function。其实就是setTimeout(fn,1);
throttle:_.throttle(function, wait)
throttle这个单词的意思是使减速,用于控制频繁触发的 function的的频率,比如,拖动页面滚动条时scroll方易做图以很高的频率触发,如果在scroll的处理事件中做了很费时的操作,会导致浏览器假死,如果使用了throttle后,function被触发的频率可以降低。
document.body.onscroll = _.throttle(function(){
console.log("scrolling:"+(document.body.scrollTop|| document.body.scrollTop);
},100);
scroll事件默认50ms触发一次,但是使用throttle之后事件触发频率为100ms一次
debounce: _.debounce(function, wait, [immediate])
debounce 本意是“使反跳”,这个翻译是在让人看不明白。同样用于处理频繁触发的事件,处理方法时,对于频繁处理的时间,只在第一次触发(是否触发取决于immdiate 参数),和事件频繁触发最后一次触发(有最多wait的延时)。拿滚动事件为例,滚动事件50ms触发一次,如果设置wait为100ms。则在最后一次触发scroll事件时,也就是停止滚动时,在100ms后触发function。如果immediate参数为true,开始滚动时也会触发function
document.body.onscroll = _.debounce(function(){
//一次滚动过程触发两次该函数
console.log("scrolling:"+(document.body.scrollTop|| document.body.scrollTop);
},100,true);
在整个滚动过程中触发function,对于只关注整个滚动前后变化的处理非常有用。
下面是_.debounce和_.throttle的源码:
_.debounce = function(func, wait, immediate) {
var timeout, result;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;//最后一次调用时清除延时
if (!immediate) result = func.apply(context, args);
};
var callNow = immediate && !timeout;
//每次func被调用,都是先清除延时再重新设置延时,这样只有最后一次触发func再经过wait延时后才会调用func
clearTimeout(timeout);//
timeout = setTimeout(later, wait);
//如果第一次func被调用 && immediate ->立即执行func
if (callNow) result = func.apply(context, args);
return result;
};
};
_.throttle = function(func, wait) {
var context, args, timeout, throttling, more, result;
//延时wait后将more throttling 设置为false
var whenDone = _.debounce(function(){
more = throttling = false;
}, wait);
return function() {
context = this; args = arguments;
var later = function() {
timeout = null;
&nb
补充:综合编程 , 其他综合 ,