这里推荐一本设计模式的javascript书. 《JavaScript设计模式与开发实践》。作者是腾讯大牛曾探。
我每天都会在里面抽出我受到的理解,作为我的读书笔记。今天就昨天讲的this里面涉及到的call和apply进行书中的讲解。
Function.prototype.call 和 Function.prototype.apply 都是非常常用的方法。它们的作用一模 一样,区别仅在于传入参数形式的不同。
1.apply
apply 接受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下 标的集合,这个集合可以为数组,也可以为类数组,apply 方法把这个集合中的 元素作为参数传 递给被调用的函数。
For Example:
var func = function( a, b, c ){
alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]
};
func.apply( null, [ 1, 2, 3 ] );
//参数123放进数组里面作为参数传入func,对应的参数分别 a b c
2.call
call 传入的参数数量不固定,跟 apply 相同的是,第一个参数也是代表函数体内的 this 指向, 从第二个参数开始往后,每个参数被依次传入函数:
For Example:
var func = function( a, b, c ){
alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]
};
func.call( null, 1, 2, 3 );
当调用一个函数时,JavaScript 的解释器并不会计较形参和实参在数量、类型以及顺序上的 区别,JavaScript的参数在内部就是用一个数组来表示的。从这个意义上说,apply 比 call 的使用 率更高,我们不必关心具体有多少参数被传入函数,只要用 apply 一股脑地推过去就可以了。
call 是包装在 apply 上面的一颗语法糖,如果我们明确地知道函数接受多少个参数,而且想 一目了然地表达形参和实参的对应关系,那么也可以用 call 来传送参数。
当使用 call 或者 apply 的时候,如果我们传入的第一个参数为 null,函数体内的 this 会指 向默认的宿主对象,在浏览器中则是 window:
For Example:
var func = function( a, b, c ){
alert ( this === window ); // 输出 true
};
func.apply( null, [ 1, 2, 3 ] );
但如果是在严格模式下,函数体内的 this 还是为 null:
For Example:
var func = function( a, b, c ){
"use strict"; alert ( this === null ); // 输出 true
}
func.apply( null, [ 1, 2, 3 ] );
有时候我们使用 call 或者 apply 的目的不在于指定 this 指向,而是另有用途,比如借用其 他对象的方法。那么我们可以传入 null 来代替某个具体的对象:
For Example:
Math.max.apply( null, [ 1, 2, 5, 3, 4 ] ) // 输出:5
那call和apply具体怎么怎么用的呢?
1.改变this的指向
call 这个函数呢,可以理解它本没有任何资产(属性方法),就借助别人的方法。别人好好的调用自己的方法,它横空一脚,自己在人家后面加上 .call,然后传入自己想要的处理的对象。这样,这个对象就用了别人的方法。省事不少,据说,杜鹃鸟就是这样干的,它不孵化小鸟,把蛋放在别的鸟窝里,让别人代为孵化。
For Example:
var obj1 = {
name: "adou1",
getName: function(){
console.log(this.name);//adou2
}
}
var obj2 = {
name: "adou2"
}
window.name = "window_adou";
var GetName = function(){
console.log(this.name);//
}
obj1.getName.call(obj2);
GetName();//window_adou;
GetName.call(obj1);//adou1;
GetName.call(obj2);//adou2
在举一个用call修正this的例子:
document.getElementById("blog").onclick=function(){
console.log(this.id);//blog
}//这我们都知道
假如在一个事件函数中有一个内部函数,在事件内部调用这个函数的时候,this就指向了 window,如下
document.getElementById("blog").onclick=function(){
console.log(this.id);//blog
var func = function (){
console.log(this.id);//undefined
//为甚指向的是undefined ,上一篇介绍的this的指向的时候,其中一个普通函数的调用,指向的就是全局 ,ES5中定义,这样的指向为undefined
}
func();
}
修正过来如下:就是把func()换成 func.call(this);//因为func还是在 onclick函数里调用的,this还是指向的是当前的点击对象
For Example:
document.getElementById = (function( func ){
return function(){
return func.apply( document, arguments );
}
})( document.getElementById );
var getId = document.getElementById;
var div = getId( 'div1' );
alert ( div.id ); // 输出: div1
2. Function.prototype.bind
For Example:
Function.prototype.bind = function(){
var self = this, // 保存原函数
context = [].shift.call( arguments ), // 需要绑定的 this 上下文
args = [].slice.call( arguments ); // 剩余的参数转成数组
return function(){ // 返回一个新的函数
return self.apply( context, [].concat.call( args, [].slice.call( arguments ) ) ); // 执行新的函数的时候,会把之前传入的 context 当作新函数体内的 this // 并且组合两次分别传入的参数,作为新函数的参数 } };
var obj = { name: 'sven' };
var func = function( a, b, c, d ){
alert ( this.name ); // 输出:sven
alert ( [ a, b, c, d ] )}
.bind( obj, 1, 2 );
func( 3, 4 );// 输出:[ 1, 2, 3, 4 ]
3.借用其他对象的方法 (用的更多点)
这个具体的案例前面已经有讲:再讲一个通过原型添加对象的方法,然后调用的例子
var A = function (name){
this.name = name;
};
var B = function(){
A.apply(this,arguments);
//调用A的时候返回了 name
};
B.prototype.getName = function(){
return this.name;
};
var b = new B("adou");
console.log(b.getName());//adou