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

2015-02-27 21:16:39

标签:javascript js bind call apply
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://rangercyh.blog.51cto.com/1444712/1615809

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

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

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

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

关于这三个函数的详细介绍,可以参看 MDN 的文档:bindcallapply

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

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

1
2
3
4
5
6
7
var a = {    
    v : 0,    
    f : function(xx) {                
        this.v = xx;    
    }
}
a.f(5);

这个时候,上面函数里的 this 就绑定的是这个对象 a。所以 this.v 可以取到对象 a 的属性 v

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

1
2
3
4
function f(xx) {        
    this.x = xx;
}
f(5);

这个时候,函数 f 里的 this 绑定的是全局对象,如果是在浏览器运行的解释器中,一般来说是 window 对象。所以这里 this.x 访问的其实是 window.x ,当然,如果 window 没有 x 属性,那么你这么一写,按照 js 的坑爹语法,就是给 window 对象添加了一个 x 属性,同时赋值。

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

1
2
3
4
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 的值,第二个是一个参数数组。看代码

1
2
3
4
5
6
7
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 的值,但是后面接受的是不定参数,而不再是一个数组,也就是说你可以像平时给函数传参那样把这些参数一个一个传递。所以如果一定要说有什么区别的话,看起来是这样的

1
2
3
4
5
6
7
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() 生成的新函数返回后,你想什么时候调就什么时候调,看下代码就明白了

1
2
3
4
5
6
7
8
9
10
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 里想定义一个函数,于是你会这么写:

1
function jam() {};

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

1
var jam = function() {};

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

1
function jam() {}();

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

1
(function jam() {}());

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

本文出自 “菜鸟浮出水” 博客,请务必保留此出处http://rangercyh.blog.51cto.com/1444712/1615809

bind,call,apply区别的更多相关文章

  1. JavaScript函数 bind call apply区别

    1. apply calll 在JavaScript中 call 和 apply 都是为了改变某个函数运行时上下文而存在的, 换句话说就是为了改变函数内部的this的指向. 这里我们有一个新的对象 b ...

  2. 数组去重,call、apply、bind之间的区别,this用法总结

    一.数组去重,直接写到Array原型链上. //该方法只能去除相同的数字 不会去判断24和'24'是不同的 所有数字和字符串数字是相同是重复的 Array.prototype.redup=functi ...

  3. Bind、Apply、Call三者的区别

    1)bind与apply.call 的最大区别就是:bind不会立即调用,其他两个会立即调用 var fn = { _int: function(){return 3}, fun: function( ...

  4. this指向和apply,call,bind三者的区别

    一.前言 this指向,apply,call,bind的区别是一个经典的面试问题,同时在项目中会经常使用到的原生的js方法.同时也是ES5中的众多坑的一个.ES6中可能会极大的避免了this产生的错误 ...

  5. apply、call、bind有什么区别?

    使用 apply var a = { name : "Cherry", func1: function () { console.log(this.name) }, func2: ...

  6. JavaScript中的call、apply、bind方法的区别

    在JavaScript 中,this的指向是动态变化的,很可能在写程序的过程中,无意中破坏掉this的指向,所以我们需要一种可以把this的含义固定的技术,于是就有了call,apply 和bind这 ...

  7. bind call apply 的区别和使用

    bind call apply 的区别和使用:https://www.jianshu.com/p/015f9f15d6b3 在讲这个之前要理解一些概念,这些概念很重要,有人说过学会了javascrip ...

  8. js中的bind、apply、call、callee、caller的区别

    1.bind.apply与call的区别与使用 相同点:2者是函数原型的一个方法,因此调用者都必须是函数,第1个参数都是对象.作用是,用另一个对象替换当前对象,另一对象也即是你传的第一个参数.通常用于 ...

  9. bind、apply与call

    bind.apply与call 先说观点:不论是bind.apply还是call,最大的好处就是代码复用. bind 在开发中,我们只有复用代码时,才会出现this指向需要改动的情况. 纵观bind的 ...

随机推荐

  1. linux笔记:RPM软件包管理-rpm命令管理

    rpm包命名原则: rpm包的依赖性: 包名和包全名: rpm软件包安装.升级和卸载: rpm软件包查询: 从rpm包中提取指定文件:

  2. 字符串(string)转json

    第一种方式: 使用js函数eval(); testJson=eval(testJson);是错误的转换方式. 正确的转换方式需要加(): testJson = eval("(" + ...

  3. laravel 自定义命令

    1.自定义命令:将自定义命令保存在app/Console/Commands,也可以在composer.json文件配置自动加载,自由选择想要放置的地方 php artisan make:console ...

  4. robot API笔记3

    robot.htmldata package 包编写HTML格式的输出文件. 这个包被认为是稳定的但不是公共API的一部分. Submodules robot.htmldata.htmlfilewri ...

  5. grouping sets,cube,rollup,grouping__id,group by

    例1: hive -e" select type ,status ,count(1) from usr_info where pt='2015-09-14' group by type,st ...

  6. Ubuntu配置LAMP+MediaWiki及常见问题

    /*在实验室觉得文档传来传去太麻烦了,干脆在实验室内部搞个wiki算了,于是网上搜集搜集资料,配了一个,由于时间仓促,mediaWiki比较高级的东西没来的及细看,等以后用的时候再完善吧*/ 环境:U ...

  7. Xcode 8 打包上线 iTunes Connect 找不到构建版本

    Xcode 8 打包上线 iTunes Connect 找不到构建版本 最近苹果推出新的mac操作系统(macOS Sierra 10.12),大家可能都已经升级了,作为一个开发者,小编肯定是第一时间 ...

  8. hdu5882 Balanced Game

    题目链接:hdu5882 Balanced Game 题解:每种手势的攻防数一样,不难想到n为奇数时游戏平衡. #include<cstdio> #include<cstring&g ...

  9. 在Linux下安装aws命令行操作

    使用安装包安装 环境: Linux, OS X, or Unix Python 2 version 2.6.5+ or Python 3 version 3.3+ 检查Python版本 $ pytho ...

  10. Objective-C:模拟按钮点击事件理解代理模式

    OC中的协议(Protocol)和和.NET中的接口(Interface)类似,简单来讲,就是一系列方法的列表,其中声明的方法可以被任何类实现.不同的是,在.NET中,如果某个类实现了一个接口,就必须 ...