JavaScript里call,apply,bind方法简介

页面导航:首页 > 网络编程 > JavaScript > JavaScript里call,apply,bind方法简介

JavaScript里call,apply,bind方法简介

来源: 作者: 时间:2016-01-23 10:23 【

JavaScript里call,apply,bind方法不太容易理解,其实背后的思想并不算非常复杂,希望本文能帮你更好地了解这3个很像,而且看很神秘的方法。非要用一个关键字来点明它们的背后思想的

Script里call,apply,bind方法不太容易理解,其实背后的思想并不算非常复杂,希望本文能帮你更好地了解这3个很像,而且看似很神秘的方法。

非要用一个关键字来点明它们的背后思想的精髓的话,关键字就是:this

 

因为通常程序员对C++比较熟,先借用C++,简单说一下this。

类的成员函数里,都可以用this来访问当前类的成员,但问题是成员函数的参数并没有this这个参数,比如:

 

Animal a;
a.eat(); 
a.eat("meat"); 

Animal的对象调用无参和单参数成员函数eat。在eat方法里可以用this访问其他成员。可问题是eat是个"无参"或“单形参”的方法,并没有一个叫this的形参。

 

原因在于编译器会对类的成员函数进行扩展。

 

a.eat();        //编译器会重写成:Animal::eat(&a);
a.eat("meat");  //编译器会重写成:Animal::eat(&a, "meat");
类的每个成员函数(除static成员函数。因为static是属于类而非对象的,static和this天生语义不同)都有一个额外的,隐含的形参this。调用成员函数时,形参this初始化为调用函数的对象的地址。

 


扯远了,拉回主题。。。先看中的Function.prototype中的call方法

就像C++一样,函数或方法的接收者是绑定到this的,但有时需要使用自定义接收者来调用函数或方法。call方法就是用于改变this。

 

function Class1() { 
    this.name = "class1"; 
    this.showName = function() { alert(this.name); } 
} 
function Class2() { 
    this.name = "class2"; 
} 
var c1 = new Class1(); 
var c2 = new Class2(); 

如果你直接在Class2的对象上调用showName一定报错:

 

c2.showName();    //Error

编译器会重写成:Class2::showName(&c2),显然Class2里没有showName方法将报错,这一点不奇怪所有人都能理解。


但showName方法太诱人,无论如何都想用呢?有两个办法,一个自然是在Class2中自己定义一个showName方法,这没什么不好,但有时显得比较麻烦,且重复代码维护起来也是个问题。另一种解决方法就是用call:

 

c1.showName.call(c2);	//class2

 

编译器会重写成:Class1::showName(&c2),隐藏的形参this本来是默认指向Class1的对象的,现在call方法将this改变为指向Class2的对象。

这样就将Class1中定义的showName方法实体应用到Class2的对象上,即用c2的上下文替换c1的上下文

 

理解了call之后,apply就迎刃而解,因为两者本质是一样的,区别在于外在表现:

object.call(this, arg1,arg2,arg3) == object.apply(this, arguments)

从第二个参数起,call方法参数将依次传递给借用的方法作参数,而apply直接将这些参数放到一个数组中再传递,最后借用方法的参数列表是一样的。

因为上面这个例子的showName方法没有参数,因此用call和apply是一样的:

 

c1.showName.call(c2);	//class2
c1.showName.apply(c2);	//class2

最后是bind。call和apply是改变this,bind同样不例外,但改变this后还将方法返回,因此bind准确的语义是:获得具有确定接收者的方法。

 

它本质上是得到一个方法,方法可以直接调用,也可以被当做参数传递给高阶函数。

如果直接调用那上面这个例子用bind可以改成:

 

c1.showName.call(c2);	//class2
c1.showName.apply(c2);	//class2
c1.showName.bind(c2)();	//class2

 

看到bind后面的一对小括号了吗?如果没有那对小括号,c1.showName.bind(c2); 仅仅表示一个方法,然后就没有然后了。加上那对小括号就是直接调用"通过bind为方法绑定接收者后返回的方法"

如果只能直接调用,那用call和apply即可,bind就没有存在的价值了,因此另一个作用就是当做参数传递给高阶函数,一个常用的例子就是用bind实现函数柯里化(将函数与其参数的一个子集绑定的技术称为函数柯里化):

 

function simpleURL(protocol, domain, path) {
    return protocol + "://" + domain + "/" + path;
}

上面是一个非常普通的生成URL地址的函数。但你觉得其实前两个参数通常是固定不变的,无非是第三个参数要变来变去,你可以这样:

 

var urls = paths.map(function(path) {
    return simpleURL("http", "csdn", path);	//前两个参数都固定,只有第3个参数会变
});
用bind可以简化上述代码:

 

 

var urls = paths.map(simpleURL.bind(null, "http", "csdn"));
bind的第一个参数为方法的接收者,由于simpleURL没有this,因此设为null即可,表示忽略接收者。这样bind将单个参数path调用simpleURL函数委托到simpleURL("http", "baidu", path)函数。

 

Tags:

文章评论

最 近 更 新
热 点 排 行
Js与CSS工具
代码转换工具

<