js中apply,call的用法
最近一直在用 js 写游戏服务器,我也接触 js 时间不长,大学的时候用 js 做过一个 H3C 的 web的项目,然后在腾讯实习的时候用 js 写过一些奇怪的程序,自己也用 js 写过几个的网站。但真正大规模的使用 js 这还是第一次。我也是初生牛犊不怕虎,这次服务器居然抛弃 C++ 和 lua 的正统搭配,而尝试用 nodejs 来写游戏服务器,折腾的自己要死要活的我也是醉了。
在给我们项目组的其他程序介绍 js 的时候,我准备了很多的内容,但看起来效果不大,果然光讲还是不行的,必须动手。前几天有人问我关于代码里 call() 函数的用法,我让他去看书,这里推荐用js 写服务器的程序猿看 《javascript编程精粹》 这本书,crockford大神果然不是盖的。之后我在 segmentfault 上又看到了类似的问题,那边解答之后干脆这里记一笔。
首先,关于 js 定义类或对象的方法,请参看 w3school 的这里,写的非常详细和清晰,我不再赘言了。
为了介绍 bind、call、apply 这三个函数的用法,不得不介绍 js 里函数的一些设定。关于这部分推荐通读 《javascript编程精粹》 的第四章,这里我所说的在书里都能找到。
关于这三个函数的详细介绍,可以参看 MDN 的文档:bind、call、apply。
下面开始搬砖,修改自我之前在 segmentfault 上的答案:
js 里函数调用有 4 种模式:方法调用、正常函数调用、构造器函数调用、apply/call 调用。
同时,无论哪种函数调用除了你声明时定义的形参外,还会自动添加 2 个形参,分别是 this 和arguments。
arguments 不涉及到上述 3 个函数,所以这里只谈 this。this 的值,在上面 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,而这个原型自带有好几个属性和方法,其中就有这里困惑的 bind、call、apply 方法。先说 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
js中apply,call的用法的更多相关文章
- 原生JS中apply()方法的一个值得注意的用法
今天在学习vue.js的render时,遇到需要重复构造多个同类型对象的问题,在这里发现原生JS中apply()方法的一个特殊的用法: var ary = Array.apply(null, { &q ...
- JS里设定延时:js中SetInterval与setTimeout用法
js中SetInterval与setTimeout用法 JS里设定延时: 使用SetInterval和设定延时函数setTimeout 很类似.setTimeout 运用在延迟一段时间,再进行某项操 ...
- js中apply方法的使用
js中apply方法的使用 1.对象的继承,一般的做法是复制:Object.extend prototype.js的实现方式是: Object.extend = function(destinat ...
- js 中关键字 this的用法
<1> js中this 的用法? (key:函数是由调用的,四种情况标红可知) (http://www.ruanyifeng.com/blog/2010/04/using_this_k ...
- [转]JS中apply和call的联系和区别
JS中有时常用到 apply 和 call 两个方法,搜索网上很多,整理如下,简单看看这两个联系和区别, 联系: 网上查到关于apply和call的定义:这两个方法都能劫持另外一个对象的方法,继承另外 ...
- js中setTimeout/setInterval定时器用法示例
js中setTimeout(定时执行一次)和setInterval(间隔循环执行)用法介绍. setTimeout:在指定的毫秒数后调用指定的代码段或函数:setTimeout示例代码 functio ...
- js中typeof与instanceof用法
今天写JS代码,遇到动态生成多个名称相同的input复选按钮 需要判断其是否是数组,用到了if (typeof(document.MapCheckMgr.checkid)!="undefin ...
- js中的escape的用法汇总
js对文字进行编码涉及3个函数:escape,encodeURI,encodeURIComponent,相应3个解码函数:unescape,decodeURI,decodeURIComponent 1 ...
- js中的||和&&的用法
与其他语言不同,在JS中,a&&b或者a||b返回的是要么是a,要么是b:而其他语言中返回的是true or false 对于js中的或与运算,需要隐式的转换为boolean类型再来运 ...
随机推荐
- Apache CXF初级介绍(一)
Web Service做项目必不可少 一.CXF安装 二.用Ant来创建项目 三.用Maven来创建项目 四.用Eclipse来创建项目 五
- 【转载】Unity 优雅地管理资源,减少占用内存,优化游戏
转自:星辰的<Unity3D占用内存太大的解决方法> 最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大. 这里写下关于Unity3D对于内存的管理与优化. Unity3D ...
- GridView控件隐藏列
GridView隐藏列visible="false" 后你就无法取得这列的值了 下面是迄今为止最简洁的解决方法了. protected void GVList_RowDataBou ...
- putchar和puts
#include<stdio.h> int main() { char a = 'h'; char b[] = "hello"; putchar(a); //putch ...
- poj.1988.Cube Stacking(并查集)
Cube Stacking Time Limit:2000MS Memory Limit:30000KB 64bit IO Format:%I64d & %I64u Submi ...
- java系列-安装MySql(三)
第一大步:MySQL安装文件分为两种,一种是msi格式的,一种是zip格式的.如果是msi格式的可以直接点击安装,按照它给出的安装提示进行安装(相信大家的英文可以看懂英文提示),一般MySQL将会安装 ...
- PHP Socket实现websocket(四)Select函数
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); /* ...
- javaweb框架构想-自己的对象存储池-遁地龙卷风
设计初衷: 网站在提供服务的过程中,会创建很多对象,bean,dao层的对象尤为频繁,然而这些对象是可以重复利用的.设计思路: 对象连接池ObjectPool才用单态的设计模式,自带线程,每隔一段时间 ...
- Java Io(数据输入输出流)
Java Io 字节流中的DataInputStream 和 DataOutputStream,使用流更加方便,是流的一个扩展,更方便读取int, long,字符等类型数据. 事例代码如下: pack ...
- leetcode 215. Kth Largest Element in an Array
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...