js中bind、call、apply函数的用法

最近一直在用 js 写游戏服务器,我也接触 js 时间不长,大学的时候用 js 作过一个 H3C 的 web 的项目,而后在腾讯实习的时候用 js 写过一些奇怪的程序,本身也用 js 写过几个的网站。但真正大规模的使用 js 这仍是第一次。我也是初生牛犊不怕虎,此次服务器竟然抛弃 C++lua 的正统搭配,而尝试用 nodejs 来写游戏服务器,折腾的本身要死要活的我也是醉了。
javascript

在给咱们项目组的其余程序介绍 js 的时候,我准备了不少的内容,但看起来效果不大,果真光讲仍是不行的,必须动手。前几天有人问我关于代码里 call() 函数的用法,我让他去看书,这里推荐用 js 写服务器的程序猿看 《javascript编程精粹》 这本书,crockford大神果真不是盖的。以后我在 segmentfault 上又看到了相似的问题,那边解答以后干脆这里记一笔。java


首先,关于 js 定义类或对象的方法,请参看 w3school 的这里,写的很是详细和清晰,我再也不赘言了。node


为了介绍 bindcallapply 这三个函数的用法,不得不介绍 js 里函数的一些设定。关于这部分推荐通读 《javascript编程精粹》 的第四章,这里我所说的在书里都能找到。web

关于这三个函数的详细介绍,能够参看 MDN 的文档:bindcallapply编程


下面开始搬砖,修改自我以前在 segmentfault 上的答案:segmentfault

js 里函数调用有 4 种模式:方法调用正常函数调用构造器函数调用apply/call 调用
同时,不管哪一种函数调用除了你声明时定义的形参外,还会自动添加 2 个形参,分别是 thisarguments
arguments 不涉及到上述 3 个函数,因此这里只谈 thisthis 的值,在上面 4 中调用模式下,分别会绑定不一样的值。分别来讲一说:
方法调用
这个很好理解,函数是一个对象的属性,好比
数组

var a = {    
    v : 0,    
    f : function(xx) {                
        this.v = xx;    
    }
}
a.f(5);

这个时候,上面函数里的 this 就绑定的是这个对象 a。因此 this.v 能够取到对象 a 的属性 v浏览器

正常函数调用:
依然看代码
服务器

function f(xx) {        
    this.x = xx;
}
f(5);

这个时候,函数 f 里的 this 绑定的是全局对象,若是是在浏览器运行的解释器中,通常来讲是 window 对象。因此这里 this.x 访问的实际上是 window.x ,固然,若是 window 没有 x 属性,那么你这么一写,按照 js 的坑爹语法,就是给 window 对象添加了一个 x 属性,同时赋值。app

构造器函数调用
构造函数一直是我认为是 js 里最坑爹的部分,由于它和 js 最初设计的基于原型的面向对象实现方式格格不入,就好像是特地为了迎合你们已经被其余基于类的面相对象实现给惯坏了的习惯。
若是你在一个函数前面带上 new 关键字来调用,那么 js 会建立一个 prototype 属性是此函数的一个新对象,同时在调用这个函数的时候,把 this 绑定到这个新对象上。固然 new 关键字也会改变 return 语句的行为,不过这里就不谈了。看代码

function a(xx) {        
    this.m = xx;
}
var b = new a(5);

上面这个函数和正常调用的函数写法上没什么区别,只不过在调用的时候函数名前面加了关键字 new 罢了,这么一来,this 绑定的就再也不是前面讲到的全局对象了,而是这里说的建立的新对象,因此说这种方式其实很危险,由于光看函数,你不会知道这个函数究竟是准备拿来当构造函数用的,仍是通常函数用的。因此咱们能够看到,在 jslint 里,它会要求你写的全部构造函数,也就是一旦它发现你用了 new 关键字,那么后面那个函数的首字母必须大写,这样经过函数首字母大写的方式来区分,我我的只有一个见解:坑爹:)

apply/call 调用:
咱们知道,在 js 里,函数其实也是一个对象,那么函数天然也能够拥有它本身的方法,有点绕,在 js 里,每一个函数都有一个公共的 prototype —— Function,而这个原型自带有好几个属性和方法,其中就有这里困惑的 bindcallapply 方法。先说 apply 方法,它让咱们构造一个参数数组传递给函数,同时能够本身来设置 this 的值,这就是它最强大的地方,上面的 3 种函数调用方式,你能够看到,this 都是自动绑定的,没办法由你来设,当你想设的时候,就能够用 apply() 了。apply 函数接收 2 个参数,第一个是传递给这个函数用来绑定 this 的值,第二个是一个参数数组。看代码

function a(xx) {        
    this.b = xx;
}
var o = {};
a.apply(o, [5]);
alert(a.b);    // undefined
alert(o.b);    // 5

是否是很神奇,函数 a 竟然能够给 o 加属性值。固然,若是你 apply 的第一个参数传递 null,那么在函数 a 里面 this 指针依然会绑定全局对象。

call() 方法和 apply() 方法很相似,它们的存在都是为了改变 this 的绑定,那 call()apply() 有什么区别呢?就我我的看来,没啥鸟区别。。。开玩笑!刚刚说了,上面 apply() 接收两个参数,第一个是绑定 this 的值,第二个是一个参数数组,注意它是一个数组,你想传递给这个函数的全部参数都放在数组里,而后 apply() 函数会在调用函数时自动帮你把数组展开。而 call() 呢,它的第一个参数也是绑定给 this 的值,可是后面接受的是不定参数,而再也不是一个数组,也就是说你能够像平时给函数传参那样把这些参数一个一个传递。因此若是必定要说有什么区别的话,看起来是这样的

function a(xx, yy) {    
    alert(xx, yy);    
    alert(this);    
    alert(arguments);
}
a.apply(null, [5, 55]);
a.call(null, 5, 55);

仅此而已。

最后再来讲 bind() 函数,上面讲的不管是 call() 也好, apply() 也好,都是立马就调用了对应的函数,而 bind() 不会, bind() 会生成一个新的函数,bind() 函数的参数跟 call() 一致,第一个参数也是绑定 this 的值,后面接受传递给函数的不定参数。 bind() 生成的新函数返回后,你想何时调就何时调,看下代码就明白了

var m = {	
    "x" : 1
};
function foo(y) {	
    alert(this.x + y);
}
foo.apply(m, [5]);
foo.call(m, 5);
var foo1 = foo.bind(m, 5);
foo1();

末了来个吐槽,你在 js 里想定义一个函数,因而你会这么写:

function jam() {};

其实这是 js 里的一种语法糖,它等价于:

var jam = function() {};

而后你想执行这个函数,脑洞大开的你会这么写:

function jam() {}();

可是这么写就报错了,其实这种写法也不算错,由于它确实是 js 支持的函数表达式,可是同时 js 又规定以 function 开头的语句被认为是函数语句,而函数语句后面是确定不会带 () 的,因此才报错,因而聪明的人想出来,加上一对括号就能够了。因而就变成了这样:

(function jam() {}());

这样就定义了一个函数同时也执行它,详情参见 ECMAScript 的 Expression Statement 章节

相关文章
相关标签/搜索