如何理解和熟练运用js中的call及call apply 区别

1376人阅读
JavaScript(219)
在js中call和apply它们的作用都是将函数绑定到另外一个对象上去运行,两者仅在定义参数方式有所区别,下面我来给大家介绍一下call和apply用法。
在web前端开发过程中,我们经常需要改变this指向,通常我们想到的就是用call方法,但是对于call的理解很多人不是很清晰,那么下面小编就给大家详细说一说call和apply的详细知识
一、call方法的定义
大家在百度里面可以搜索call,关于call的定义都很拗口。在我的理解,a.call(b,arg1,arg2..)就是a对象的方法应用到b对象上。例如如下例子:
function add(a,b)
alert(a+b);
function reduce(a,b)
alert(a-b);
add.call(reduce,1,3) //将add方法运用到reduce,结果为4
二、call可以改变this指向
function b()
alert(this)
b(); //window
b.call(); //window
b.call(“a”,2,3); //a
再看一个复杂的例子:
function Animal()
this.name=”animal”;
this.showName=function()
alert(this.name)
function Cat()
this.name=”cat”;
var animal = new Animal();
var cat = new Cat();
animal.showName(); //结果为animal
animal.showName.call(cat); //原本cat没有showName方法,但是通过call方法将animal的showName方法应用到cat上,因此结果为cat
三、实现继承
如下例子:
function Animal(name)
this.name=
this.showName=function()
alert(this.name)
function Cat(name)
Animal.call(this,name); //将Animal应用到Cat上,因此Cat拥有了Animal的所有属性和方法
var cat = new Cat(“Black Cat”);
cat.showName(); //弹出Black Cat
四、apply用法
apply和call的用法只有一个地方不一样,除此之外,其他地方基本一模一样
a.call(b,arg1,arg2…)
apply(b,[arg1,arg2]) //apply只有2个参数,它将call的参数(arg1,arg2…)放在一个数组中作为apply的第二参数
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:519797次
积分:7080
积分:7080
排名:第1657名
原创:130篇
转载:650篇
评论:48条
(6)(20)(39)(8)(7)(7)(17)(3)(42)(48)(18)(11)(3)(25)(10)(7)(1)(5)(15)(28)(38)(37)(41)(82)(15)(17)(6)(16)(14)(1)(2)(6)(12)(7)(10)(2)(7)(6)(18)(15)(13)(6)(5)(9)(23)(18)(1)(1)(2)(6)(4)(4)(5)(2)(5)(1)(2)(1)Javascript 中 apply 、call、argument的使用方法
- 狂奔de蜗牛 - ITeye技术网站
最近压力很大啊!
apply 与 call 的功能基本相同先看看 apply的用途
// 第一个类function clazz1(){
this.name = "刘德华";}
clazz1.prototype.func = function(){
alert(this.name);}
//第二个类function clazz2(){
this.name = "周杰伦";}
var clz1 = new clazz1();//调用自己的方法clz1.func();//定义一个clazz2的对象并且调用clazz1的func方法var clz2 = new clazz2();clz1.func.apply(clz2);*******************************这里clazz2的对象调用了clazz1的func方法
可以理解为-----》将clz1的func方法应用到claz2上。
下面再举一例
//apply 与 call 的区别
function func1(a,b){ alert(a + b);}
function func2(a,b){ func1.apply(this,[a,b]);
func1.apply(this,arguments); //arguments内置对象,代表着正在执行的和调用他的函数的变量
func1.call(this,a,b); }
func2('a','b');
重点来了,apply 与 call 的主要区别就是,call的参数可以有很多个,而apply的方法的参数只有两个,第二个是一个数组,或者javascript的内置对象arguments
evilwicker
浏览: 36360 次
来自: 杭州
第一次接触这个东西,才知道文件文档类型还那么多收藏,4.4k 浏览
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
在一些jQuery插件中经常看到类似 callback.call(xxx,xxx)
虽然看到书上有介绍 说call和apply函数可以改变作用域,但还是无法非常透彻的理解改变作用域主要是为了解决什么问题,有没有替代方案,或者 这2个函数主要为了解决什么问题,应用场景,何时使用最合适,每次读到这样的代码就晕了,一下子从线性阅读中跳出去了,感觉有点绕
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
call和apply的作用很简单,就是改变上下文,适用场景太多了,虽然有时候只是为了“美观”,下面几个是我常用的。
Object.prototype.toString.call(Obj)
用来判断 Obj 的类型
arguments 虽然和Array 很像,但是他没有Array的push之类的方法,怎么办?
Array.prototype.push.call(arguments)
3.Javascript 没有私有方法的概念,想用闭包实现
(function () {
var Person = function () {
this.doSomeThing = function () {
_privateFunction.call(this);
var _privateFunction = function () {
window.Person = P
}).call(window);
差不多就是这个意思,callback的时候,当你希望你的callback中的上下文是当前上下文的时候,也可以用call或者apply,有什么好处呢?
这个时候你的callback 里面的this 就是指代当前上下文。例如一个类Person,然后他的方法 say 有一个callback的参数,如果这个callback是通过普通的括号来执行的话,那在这个callback里面执行person的其它方法还需要用person.other 来实现,但是切换上下文之后,就是this.other搞定~代码对比如下:
var Person = function(){
Person.prototype.say = function(callback){
callback();
Person.prototype.other = function(){
var vincent = new Person();
vincent.say(function(){
vincent.other();
用了call的:
var Person = function(){
Person.prototype.say = function(callback){
callback.call(this);
Person.prototype.other = function(){
var vincent = new Person();
vincent.say(function(){
this.other();
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
也用来使函数调用的多个参数变为数组参数,比如求一个数组内最大数值。
arr = [3,23,4,88,2.5,1,5,7,89];
alert(Math.max.apply(Math,arr));
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
给你个提示
你去做做事件绑定
肯定会用到call
同步到新浪微博
分享到微博?
与我们一起探索更多的未知
专业的开发者技术社区,为用户提供多样化的线上知识交流,丰富的线下活动及给力的工作机会
加入只需一步
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要举报该,理由是:
扫扫下载 App
SegmentFault
一起探索更多未知4)&&& this、new、call和apply的相关问题
  讲解this指针的原理是个很复杂的问题,如果我们从javascript里this的实现机制来说明this,很多朋友可能会越来越糊涂,因此本篇打算换一个思路从应用的角度来讲解this指针,从这个角度理解this指针更加有现实意义。
  下面我们看看在java语言里是如何使用this指针的,代码如下:
public class Person {
private int
public Person(String name, String sex, int age, String job) {
this.name =
this.sex =
this.age =
this.job =
private void showPerson(){
System.out.println("姓名:" + this.name);
System.out.println("性别:" + this.sex);
System.out.println("年龄:" + this.age);
System.out.println("工作:" + this.job);
public void printInfo(){
this.showPerson();
public static void main(String[] args) {
Person person = new Person("马云", "男", 46, "董事长");
person.printInfo();
//姓名:马云
//性别:男
//年龄:46
//工作:董事长
  上面的代码执行后没有任何问题,下面我修改下这个代码,加一个静态的方法,静态方法里使用this指针调用类里的属性,如下图所示:
  我们发现IDE会报出语法错误&Cannot use this in a static context&,this指针在java语言里是不能使用在静态的上下文里的。
  在面向对象编程里有两个重要的概念:一个是类,一个是实例化的对象,类是一个抽象的概念,用个形象的比喻表述的话,类就像一个模具,而实例化对象就是通过这个模具制造出来的产品,实例化对象才是我们需要的实实在在的东西,类和实例化对象有着很密切的关系,但是在使用上类的功能是绝对不能取代实例化对象,就像模具和模具制造的产品的关系,二者的用途是不相同的。
  有上面代码我们可以看到,this指针在java语言里只能在实例化对象里使用,this指针等于这个被实例化好的对象,而this后面加上点操作符,点操作符后面的东西就是this所拥有的东西,例如:姓名,工作,手,脚等等。
  其实javascript里的this指针逻辑上的概念也是实例化对象,这一点和java语言里的this指针是一致的,但是javascript里的this指针却比java里的this难以理解的多,究其根本原因我个人觉得有三个原因:
  原因一:javascript是一个函数编程语言,怪就怪在它也有this指针,说明这个函数编程语言也是面向对象的语言,说的具体点,javascript里的函数是一个高阶函数,编程语言里的高阶函数是可以作为对象传递的,同时javascript里的函数还有可以作为构造函数,这个构造函数可以创建实例化对象,结果导致方法执行时候this指针的指向会不断发生变化,很难控制。
  原因二:javascript里的全局作用域对this指针有很大的影响,由上面java的例子我们看到,this指针只有在使用new操作符后才会生效,但是javascript里的this在没有进行new操作也会生效,这时候this往往会指向全局对象window。
  原因三:javascript里call和apply操作符可以随意改变this指向,这看起来很灵活,但是这种不合常理的做法破坏了我们理解this指针的本意,同时也让写代码时候很难理解this的真正指向
  上面的三个原因都违反了传统this指针使用的方法,它们都拥有有别于传统this原理的理解思路,而在实际开发里三个原因又往往会交织在一起,这就更加让人迷惑不解了,今天我要为大家理清这个思路,其实javascript里的this指针有一套固有的逻辑,我们理解好这套逻辑就能准确的掌握好this指针的使用。
  我们先看看下面的代码:
&script type="text/javascript"&
this.a = "aaa";
console.log(a);//aaa
console.log(this.a);//aaa
console.log(window.a);//aaa
console.log(this);// window
console.log(window);// window
console.log(this == window);// true
console.log(this === window);// true
  在script标签里我们可以直接使用this指针,this指针就是window对象,我们看到即使使用三等号它们也是相等的。全局作用域常常会干扰我们很好的理解javascript语言的特性,这种干扰的本质就是:
  在javascript语言里全局作用域可以理解为window对象,记住window是对象而不是类,也就是说window是被实例化的对象,这个实例化的过程是在页面加载时候由javascript引擎完成的,整个页面里的要素都被浓缩到这个window对象,因为程序员无法通过编程语言来控制和操作这个实例化过程,所以开发时候我们就没有构建这个this指针的感觉,常常会忽视它,这就是干扰我们在代码里理解this指针指向window的情形。
  干扰的本质还和function的使用有关,我们看看下面的代码:
&script type="text/javascript"&
function ftn01(){
console.log("I am ftn01!");
var ftn02 = function(){
console.log("I am ftn02!");
  上面是我们经常使用的两种定义函数的方式,第一种定义函数的方式在javascript语言称作声明函数,第二种定义函数的方式叫做函数表达式,这两种方式我们通常认为是等价的,但是它们其实是有区别的,而这个区别常常会让我们混淆this指针的使用,我们再看看下面的代码:
&script type="text/javascript"&
console.log(ftn01);//ftn01()
注意:在firebug下这个打印结果是可以点击,点击后会显示函数的定义
console.log(ftn02);// undefined
function ftn01(){
console.log("I am ftn01!");
var ftn02 = function(){
console.log("I am ftn02!");
  这又是一段没有按顺序执行的代码,先看看ftn02,打印结果是undefined,undefined我在前文里讲到了,在内存的栈区已经有了变量的名称,但是没有栈区的变量值,同时堆区是没有具体的对象,这是javascript引擎在预处理(群里东方说预处理比预加载更准确,我同意他的说法,以后文章里我都写为预处理)扫描变量定义所致,但是ftn01的打印结果很令人意外,既然打印出完成的函数定义了,而且代码并没有按顺序执行,这只能说明一个问题:
  在javascript语言通过声明函数方式定义函数,javascript引擎在预处理过程里就把函数定义和赋值操作都完成了,在这里我补充下javascript里预处理的特性,其实预处理是和执行环境相关,在上篇文章里我讲到执行环境有两大类:全局执行环境和局部执行环境,执行环境是通过上下文变量体现的,其实这个过程都是在函数执行前完成,预处理就是构造执行环境的另一个说法,总而言之预处理和构造执行环境的主要目的就是明确变量定义,分清变量的边界,但是在全局作用域构造或者说全局变量预处理时候对于声明函数有些不同,声明函数会将变量定义和赋值操作同时完成,因此我们看到上面代码的运行结果。由于声明函数都会在全局作用域构造时候完成,因此声明函数都是window对象的属性,这就说明为什么我们不管在哪里声明函数,声明函数最终都是属于window对象的原因了。
  关于函数表达式的写法还有秘密可以探寻,我们看下面的代码:
&script type="text/javascript"&
function ftn03(){
var ftn04 = function(){
console.log(this);// window
  运行结果我们发现ftn04虽然在ftn03作用域下,但是执行它里面的this指针也是指向window,其实函数表达式的写法我们大多数更喜欢在函数内部写,因为声明函数里的this指向window这已经不是秘密,但是函数表达式的this指针指向window却是常常被我们所忽视,特别是当它被写在另一个函数内部时候更加如此。
  其实在javascript语言里任何匿名函数都是属于window对象,它们也都是在全局作用域构造时候完成定义和赋值,但是匿名函数是没有名字的函数变量,但是在定义匿名函数时候它会返回自己的内存地址,如果此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了,因为匿名函数也是在全局执行环境构造时候定义和赋值,所以匿名函数的this指向也是window对象,所以上面代码执行时候ftn04的this也是指向window,因为javascript变量名称不管在那个作用域有效,堆区的存储的函数都是在全局执行环境时候就被固定下来了,变量的名字只是一个指代而已。
  这下子坏了,this都指向window,那我们到底怎么才能改变它了?
  在本文开头我说出了this的秘密,this都是指向实例化对象,前面讲到那么多情况this都指向window,就是因为这些时候只做了一次实例化操作,而这个实例化都是在实例化window对象,所以this都是指向window。我们要把this从window变成别的对象,就得要让function被实例化,那如何让javascript的function实例化呢?答案就是使用new操作符。我们看看下面的代码:
&script type="text/javascript"&
var obj = {
name:"sharpxiajun",
job:"Software",
show:function(){
console.log("Name:" + this.name + ";Job:" + this.job);
console.log(this);// Object { name="sharpxiajun", job="Software", show=function()}
var otherObj = new Object();
otherObj.name = "xtq";
otherObj.job = "good";
otherObj.show = function(){
console.log("Name:" + this.name + ";Job:" + this.job);
console.log(this);// Object { name="xtq", job="good", show=function()}
obj.show();//Name:Job:Software
otherObj.show();//Name:Job:good
&  这是我上篇讲到的关于this使用的一个例子,写法一是我们大伙都爱写的一种写法,里面的this指针不是指向window的,而是指向Object的实例,firebug的显示让很多人疑惑,其实Object就是面向对象的类,大括号里就是实例对象了,即obj和otherObj。Javascript里通过字面量方式定义对象的方式是new Object的简写,二者是等价的,目的是为了减少代码的书写量,可见即使不用new操作字面量定义法本质也是new操作符,所以通过new改变this指针的确是不过攻破的真理。
  下面我使用javascript来重写本篇开头用java定义的类,代码如下:
&script type="text/javascript"&
function Person(name,sex,age,job){
this.name =
this.sex =
this.age =
this.job =
this.showPerson = function(){
console.log("姓名:" + this.name);
console.log("性别:" + this.sex);
console.log("年龄:" + this.age);
console.log("工作:" + this.job);
console.log(this);// Person { name="马云", sex="男", age=46, 更多...}
var person = new Person("马云", "男", 46, "董事长");
person.showPerson();
  看this指针的打印,类变成了Person,这表明function Person就是相当于在定义一个类,在javascript里function的意义实在太多,function既是函数又可以表示对象,function是函数时候还能当做构造函数,javascript的构造函数我常认为是把类和构造函数合二为一,当然在javascript语言规范里是没有类的概念,但是我这种理解可以作为构造函数和普通函数的一个区别,这样理解起来会更加容易些。
  下面我贴出在《javascript高级编程》里对new操作符的解释:
  new操作符会让构造函数产生如下变化:
  1.&&&&&& 创建一个新对象;
  2.&&&&&& 将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
  3.&&&&&& 执行构造函数中的代码(为这个新对象添加属性);
  4.&&&&&& 返回新对象
  关于第二点其实很容易让人迷惑,例如前面例子里的obj和otherObj,obj.show(),里面this指向obj,我以前文章讲到一个简单识别this方式就是看方法调用前的对象是哪个this就指向哪个,其实这个过程还可以这么理解,在全局执行环境里window就是上下文对象,那么在obj里局部作用域通过obj来代表了,这个window的理解是一致的。
  第四点也要着重讲下,记住构造函数被new操作,要让new正常作用最好不能在构造函数里写return,没有return的构造函数都是按上面四点执行,有了return情况就复杂了,这个知识我会在讲prototype时候讲到。
  Javascript还有一种方式可以改变this指针,这就是call方法和apply方法,call和apply方法的作用相同,就是参数不同,call和apply的第一个参数都是一样的,但是后面参数不同,apply第二个参数是个数组,call从第二个参数开始后面有许多参数。Call和apply的作用是什么,这个很重要,重点描述如下:
  Call和apply是改变函数的作用域(有些书里叫做改变函数的上下文)
  这个说明我们参见上面new操作符第二条:
  将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
  Call和apply是将this指针指向方法的第一个参数。
  我们看看下面的代码:
&script type="text/javascript"&
var name = "sharpxiajun";
function ftn(name){
console.log(name);
console.log(this.name);
console.log(this);
ftn("101");
var obj = {
name:"xtq"
ftn.call(obj,"102");
* 结果如下所示:
T002.html (第 73 行)
sharpxiajun
T002.html (第 74 行)
Window T002.html
T002.html (第 75 行)
T002.html (第 73 行)
T002.html (第 74 行)
Object { name="xtq"}
  我们看到apply和call改变的是this的指向,这点在开发里很重要,开发里我们常常被this所迷惑,迷惑的根本原因我在上文讲到了,这里我讲讲表面的原因:
  表面原因就是我们定义对象使用对象的字面表示法,字面表示法在简单的表示里我们很容易知道this指向对象本身,但是这个对象会有方法,方法的参数可能会是函数,而这个函数的定义里也可能会使用this指针,如果传入的函数没有被实例化过和被实例化过,this的指向是不同,有时我们还想在传入函数里通过this指向外部函数或者指向被定义对象本身,这些乱七八糟的情况使用交织在一起导致this变得很复杂,结果就变得糊里糊涂。
  其实理清上面情况也是有迹可循的,就以定义对象里的方法里传入函数为例:
  情形一:传入的参数是函数的别名,那么函数的this就是指向window;
  情形二:传入的参数是被new过的构造函数,那么this就是指向实例化的对象本身;
  情形三:如果我们想把被传入的函数对象里this的指针指向外部字面量定义的对象,那么我们就是用apply和call
  我们可以通过代码看出我的结论,代码如下:
&script type="text/javascript"&
var name = "I am window";
var obj = {
name:"sharpxiajun",
job:"Software",
ftn01:function(obj){
obj.show();
ftn02:function(ftn){
ftn03:function(ftn){
ftn.call(this);
function Person(name){
this.name =
this.show = function(){
console.log("姓名:" + this.name);
console.log(this);
var p = new Person("Person");
obj.ftn01(p);
obj.ftn02(function(){
console.log(this.name);
console.log(this);
obj.ftn03(function(){
console.log(this.name);
console.log(this);
  结果如下:
  最后再总结一下:
  如果在javascript语言里没有通过new(包括对象字面量定义)、call和apply改变函数的this指针,函数的this指针都是指向window的。
阅读(...) 评论()

我要回帖

更多关于 jquery call apply 的文章

 

随机推荐