跟我学习javascript的call(),apply(),bind()与回调

一、call(),apply(),bind()方法

JavaScript 中通过call或者apply用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。简单的说就是改变函数执行的上下文,这是最基本的用法。两个方法基本区别在于传参不同。

call(obj,arg1,arg2,arg3); call第一个参数传对象,可以是null。参数以逗号分开进行传值,参数可以是任何类型。
apply(obj,[arg1,arg2,arg3]); apply第一个参数传对象,参数可以是数组或者arguments 对象。
1、语法
先来看看JS手册中对call的解释:

call 方法
调用一个对象的一个方法,以另一个对象替换当前对象。

call([thisObj[,arg1[, arg2[,   [,.argN]]]]])
参数

thisObj可选项。将被用作当前对象的对象。
 arg1, arg2,  , arg可选项。将被传递方法参数序列。
说明
call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。

如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
说明白一点其实就是更改对象的内部指针,即改变对象的this指向的内容。这在面向对象的js编程过程中有时是很有用的。

2、用法

因为function也是对象,所以每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this 对象的值。首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array 的实例,也可以是arguments 对象。例如:

function sum(num1, num2){
 return num1 + num2;
}
function callSum1(num1, num2){
 return sum.apply(this, arguments); // 传入arguments 对象
}
function callSum2(num1, num2){
 return sum.apply(this, [num1, num2]); // 传入数组
}
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20

在上面这个例子中,callSum1()在执行sum()函数时传入了this 作为this 值(因为是在全局作用域中调用的,所以传入的就是window 对象)和arguments 对象。而callSum2 同样也调用了sum()函数,但它传入的则是this 和一个参数数组。这两个函数都会正常执行并返回正确的结果。

在严格模式下,未指定环境对象而调用函数,则this 值不会转型为window。除非明确把函数添加到某个对象或者调用apply()或call(),否则this 值将是undefined

3、不同点

call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this 值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call()方法时,传递给函数的参数必须逐个列举出来,如下面的例子所示。

function sum(num1, num2){
 return num1 + num2;
}
function callSum(num1, num2){
 return sum.call(this, num1, num2);
}
alert(callSum(10,10)); //20

在使用call()方法的情况下,callSum()必须明确地传入每一个参数。结果与使用apply()没有什么不同。至于是使用apply()还是call(),完全取决于你采取哪种给函数传递参数的方式最方便。如果你打算直接传入arguments 对象,或者包含函数中先接收到的也是一个数组,那么使用apply()肯定更方便;否则,选择call()可能更合适。(在不给函数传递参数的情况下,使用哪个方法都无所谓) 。

4、扩充函数运行的作用域

事实上,传递参数并非apply()和call()真正的用武之地;它们真正强大的地方是能够扩充函数
赖以运行的作用域。下面来看一个例子。

window.color = "red";
var o = { color: "blue" };
function sayColor(){
 alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue

这个例子是在前面说明this 对象的示例基础上修改而成的。这一次,sayColor()也是作为全局函数定义的,而且当在全局作用域中调用它时,它确实会显示”red”——因为对this.color 的求值会转换成window.color 的求值。而sayColor.call(this)和sayColor.call(window),则是两种显式地在全局作用域中调用函数的方式,结果当然都会显示”red”。但是,当运行sayColor.call(o)时,函数的执行环境就不一样了,因为此时函数体内的this 对象指向了o,于是结果显示的是”blue”。使用call()(或apply())来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。

在前面例子的第一个版本中,我们是先将sayColor()函数放到了对象o 中,然后再通过o 来调用它的;而在这里重写的例子中,就不需要先前那个多余的步骤了。

5、bind()方法

最后再来说 bind() 函数,上面讲的无论是 call() 也好, apply() 也好,都是立马就调用了对应的函数,而 bind() 不会, bind() 会生成一个新的函数,bind() 函数的参数跟 call() 一致,第一个参数也是绑定 this 的值,后面接受传递给函数的不定参数。 bind() 生成的新函数返回后,你想什么时候调就什么时候调,

window.color = "red";
var o = { color: "blue" };
function sayColor(){
 alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue

在这里,sayColor()调用bind()并传入对象o,创建了objectSayColor()函数。object-SayColor()函数的this 值等于o,因此即使是在全局作用域中调用这个函数,也会看到”blue”。

支持bind()方法的浏览器有IE9+、Firefox 4+、Safari 5.1+、Opera 12+和Chrome。

二、call(),apply()的继承和回调

类的继承

先来看这个例子:

function Person(name,age){
 this.name = name;
 this.age=age;
 this.alertName = function(){
 alert(this.name);

 }
 this.alertAge = function(){
 alert(this.age);
 }
}

function webDever(name,age,sex){
 Person.call(this,name,age);
 this.sex=sex;
 this.alertSex = function(){
 alert(this.sex);
 }
}

var test= new webDever(“愚人码头”,28,”男”);

test.alertName();//愚人码头

test.alertAge();//28

test.alertSex();//男

这样 webDever类就继承Person类,Person.call(this,name,age) 的 意思就是使用 Person构造函数(也是函数)在this对象下执行,那么 webDever就有了Person的所有属性和方法,test对象就能够直接调用Person的方法以及属性了

用于回调
call 和 apply在回调行数中也非常有用,很多时候我们在开发过程中需要对改变回调函数的执行上下文,最常用的比如ajax或者定时什么的,一般情况下,Ajax都是全局的,也就是window对象下的,来看这个例子:

function Album(id, title, owner_id) {

 this.id = id;

 this.name = title;

 this.owner_id = owner_id;

};

Album.prototype.get_owner = function (callback) {

 var self = this;

 $.get(‘/owners/' + this.owner_id, function (data) {

 callback && callback.call(self, data.name);

 });

};

var album = new Album(1, ‘生活', 2);

album.get_owner(function (owner) {

 alert(‘The album' + this.name + ‘ belongs to ‘ + owner);

});

这里

album.get_owner(function (owner) {

 alert(‘The album' + this.name + ‘ belongs to ‘ + owner);

});

中的 this.name就能直接取到album对象中的name属性了。

三 、回调函数

说起回调函数,好多人虽然知道意思,但是还是一知半解。至于怎么用,还是有点糊涂。网上的一些相关的也没有详细的说一下是怎么回事,说的比较片面。下面我只是说说个人的一点理解,大牛勿喷。

定义
回调是什么?
看维基的 Callback_(computer_programming) 条目:

In computer programming, a callback is a reference to a piece of executable code that is passed as an argument to other code.

在JavaScript中,回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。

举个例子:

你有事去隔壁寝室找同学,发现人不在,你怎么办呢?
    方法1,每隔几分钟再去趟隔壁寝室,看人在不
    方法2,拜托与他同寝室的人,看到他回来时叫一下你
前者是轮询,后者是回调。
那你说,我直接在隔壁寝室等到同学回来可以吗?
可以啊,只不过这样原本你可以省下时间做其他事,现在必须浪费在等待上了。
把原来的非阻塞的异步调用变成了阻塞的同步调用。
JavaScript的回调是在异步调用场景下使用的,使用回调性能好于轮询。
因此callback 不一定用于异步,一般同步(阻塞)的场景下也经常用到回调,比如要求执行某些操作后执行回调函数。

一个同步(阻塞)中使用回调的例子,目的是在func1代码执行完成后执行func2。

var func1=function(callback){
 //do something.
 (callback && typeof(callback) === "function") && callback();
}

func1(func2);
 var func2=function(){
}

异步回调的例子:

$(document).ready(callback);

$.ajax({
 url: "test.html",
 context: document.body
}).done(function() {
 $(this).addClass("done");
}).fail(function() { alert("error");
}).always(function() { alert("complete");
});

回调什么时候执行

回调函数,一般在同步情境下是最后执行的,而在异步情境下有可能不执行,因为事件没有被触发或者条件不满足。另外,最好保证回调存在且必须是函数引用或者函数表达式:

(callback && typeof(callback) === "function") && callback();
我们来看一下一个粗略的一个定义“函数a有一个参数,这个参数是个函数b,当函数a执行完以后执行函数b。那么这个过程就叫回调。”,这句话的意思是函数b以一个参数的形式传入函数a并执行,顺序是先执行a ,然后执行参数b,b就是所谓的回调函数。我们先来看下面的例子。

 function a(callback){
 alert('a');
 callback.call(this);//或者是 callback(), callback.apply(this),看个人喜好
 }
 function b(){
 alert('b');
 }
 //调用
 a(b);

这样的结果是先弹出 ‘a',再弹出‘b'。这样估计会有人问了“写这样的代码有什么意思呢?好像没太大的作用呢!”

是的,其实我也觉得这样写没啥意思,“如果调用一个函数就直接在函数里面调用它不就行了”。我这只是给大家写个小例子,做初步的理解。真正写代码的过程中很少用这样无参数的,因为在大部分场景中,我们要传递参数。来个带参数的:

function c(callback){
 alert('c');
 callback.call(this,'d');
 }
//调用
c(function(e){
 alert(e);
});

这个调用看起来是不是似曾相识,这里e参数被赋值为'd',我们只是简单的赋值为字符窜,其实也可以赋值为对象。Jquery里面是不是也有个e参数?

回调函数的使用场合

  • 资源加载:动态加载js文件后执行回调,加载iframe后执行回调,ajax操作回调,图片加载完成执行回调,AJAX等等。
  • DOM事件及Node.js事件基于回调机制(Node.js回调可能会出现多层回调嵌套的问题)。
  • setTimeout的延迟时间为0,这个hack经常被用到,settimeout调用的函数其实就是一个callback的体现
  • 链式调用:链式调用的时候,在赋值器(setter)方法中(或者本身没有返回值的方法中)很容易实现链式调用,而取值器(getter)相对来说不好实现链式调用,因为你需要取值器返回你需要的数据而不是this指针,如果要实现链式方法,可以用回调函数来实现
  • setTimeout、setInterval的函数调用得到其返回值。由于两个函数都是异步的,即:他们的调用时序和程序的主流程是相对独立的,所以没有办法在主体里面等待它们的返回值,它们被打开的时候程序也不会停下来等待,否则也就失去了setTimeout及setInterval的意义了,所以用return已经没有意义,只能使用callback。callback的意义在于将timer执行的结果通知给代理函数进行及时处理。

当函数的实现过程非常漫长,你是选择等待函数完成处理,还是使用回调函数进行异步处理呢?这种情况下,使用回调函数变得至关重要,例如:AJAX请求。若是使用回调函数进行处理,代码就可以继续进行其他任务,而无需空等。实际开发中,经常在javascript中使用异步调用,甚至在这里强烈推荐使用!

下面有个更加全面的使用AJAX加载XML文件的示例,并且使用了call()函数,在请求对象(requested object)上下文中调用回调函数。

function fn(url, callback){
 var httpRequest;    //创建XHR
 httpRequest = window.XMLHttpRequest ? new XMLHttpRequest() :   
     window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : undefined;
 //针对IE进行功能性检测

 httpRequest.onreadystatechange = function(){

 if(httpRequest.readystate === 4 && httpRequest.status === 200){  //状态判断
  callback.call(httpRequest.responseXML); 

 }

 };

 httpRequest.open("GET", url);
 httpRequest.send();
}

fn("text.xml", function(){    //调用函数
 console.log(this);   //此语句后输出
});
console.log("this will run before the above callback.");  //此语句先输出

我们请求异步处理,意味着我们开始请求时,就告诉它们完成之时调用我们的函数。在实际情况中,onreadystatechange事件处理程序还得考虑请求失败的情况,这里我们是假设xml文件存在并且能被浏览器成功加载。这个例子中,异步函数分配给了onreadystatechange事件,因此不会立刻执行。

最终,第二个console.log语句先执行,因为回调函数直到请求完成才执行。

以上就是本文的全部内容,希望对大家的学习有所帮助。

详细介绍请查看: 《详解JavaScript的回调函数》

(0)

相关推荐

  • 浅谈JavaScript中的apply/call/bind和this的使用

    fun.apply(context,[argsArray]) 立即调用fun,同时将fun函数原来的this指向传入的新context对象,实现同一个方法在不同对象上重复使用. context:传入的对象,替代fun函数原来的this: argsArray:一个数组或者类数组对象,其中的数组参数会被展开作为单独的实参传给 fun 函数,需要注意参数的顺序. fun.call(context,[arg1],[arg2],[-]) 同apply,只是参数列表不同,call的参数需要分开一个一个传入.

  • JS中改变this指向的方法(call和apply、bind)

    this是javascript的一个关键字,随着函数使用场合不同,this的值会发生变化.但是总有一个原则,那就是this指的是调用函数的那个对象. this一般指向的是当前被调用者,但也可以通过其它方式来改变它的指向,下面将介绍三种方式: 1.call用作继承时: function Parent(age){ this.name=['mike','jack','smith']; this.age=age; } function Child(age){ Parent.call(this,age);

  • 浅谈javascript的call()、apply()、bind()的用法

    JavaScript中的函数不仅是一种类似于Java中方法的语言功能,它还可以作为对象而存在. 本文将要探讨JavaScript中函数的一些特殊用法,包括call.apply.bind三个原型方法. 一.函数基础 JavaScript中的函数是一种类似于Java中方法的语言功能,不过它可以独立于类进行定义. 函数式编程:由于JavaScript支持匿名函数,因此可以将函数作为对象来使用, 所以JavaScript不仅支持过程式编程(面向对象也是过程式编程的一种),还支持函数式编程. 上下文 函数

  • 浅谈javascript中call()、apply()、bind()的用法

    call(thisObj,arg1,arg2...).apply(thisObj,[obj1,obj2...])这二个方法是每个函数都包含的非继承的方法 call(thisobj[, args])和apply(thisobj[, args]) 作用都是一样的,简单来说就是改变当前使用该方法的对象中的this指向,指向调用方法中的thisObj对象二者的区别(第一个参数是相同的)就是call方法中传入的参数是是一个个列举出来的,而apply方法中的参数二是一个数组 还是举例说明比较直观: wind

  • js apply/call/caller/callee/bind使用方法与区别分析

    一.call 方法 调用一个对象的一个方法,以另一个对象替换当前对象(其实就是更改对象的内部指针,即改变对象的this指向的内容). Js代码 call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 参数 thisObj 可选项.将被用作当前对象的对象. arg1, arg2, , argN 可选项.将被传递方法参数序列. 说明 call 方法可以用来代替另一个对象调用一个方法.call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对

  • javascript中call apply 与 bind方法详解

    在JavaScript中,call.apply和bind是Function对象自带的三个方法,本文将通过几个场景的应用,来详细理解三个方法. call() call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法. 当调用一个函数时,可以赋值一个不同的 this 对象.this 引用当前对象,即 call 方法的第一个参数. 通过 call 方法,你可以在一个对象上借用另一个对象上的方法,比如Object.prototype.toString.call([]),

  • javascript中call,apply,bind的用法对比分析

    关于call,apply,bind这三个函数的用法,是学习javascript这门语言无法越过的知识点.下边我就来好好总结一下它们三者各自的用法,及常见的应用场景. 首先看call这个函数,可以理解成"借用","请求".想像一下如下的情景:你孤单一人漂泊在外,有急事想往家里打电话,可是很不巧,手机欠费了,或者没电了,或者掉坑里了,总之你的手机就是用不成.可是你非打这个电话不可,于是你可以去借一下朋友的手机,或者借用一下邻居的手机,或者公用电话,这样呢,你就可以在自己

  • 跟我学习javascript的call(),apply(),bind()与回调

    一.call(),apply(),bind()方法 JavaScript 中通过call或者apply用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象.简单的说就是改变函数执行的上下文,这是最基本的用法.两个方法基本区别在于传参不同. call(obj,arg1,arg2,arg3); call第一个参数传对象,可以是null.参数以逗号分开进行传值,参数可以是任何类型. apply(obj,[arg1,arg2,arg3]); appl

  • javascript中call,apply,bind函数用法示例

    本文实例讲述了javascript中call,apply,bind函数用法.分享给大家供大家参考,具体如下: 一.call函数 a.call(b); 简单的理解:把a对象的方法应用到b对象上(a里如果有this,会指向b) call()的用法:用在函数上面 var Dog=function(){ this.name="汪星人"; this.shout=function(){ alert(this.name); } }; var Cat=function(){ this.name=&qu

  • javascript中call,apply,bind的区别详解

    在JS中,这三者都是用来改变函数的this对象的指向的,他们有什么样的区别呢. 在说区别之前还是先总结一下三者的相似之处: 1.都是用来改变函数的this对象的指向的. 2.第一个参数都是this要指向的对象. 3.都可以利用后续参数传参. 那么他们的区别在哪里的,先看一个例子. var xw = { name : "小王", gender : "男", age : 24, say : function() { alert(this.name + " ,

  • JavaScript中的this/call/apply/bind的使用及区别

    一.this 1.什么是this this 关键字在大部分语言中都是一个重要的存在,JS中自然不例外,其表达的意义丰富多样甚至有些复杂,深刻理解this是学习JS.面向对象编程非常重要的一环. 2.this 代表什么 this代表函数(方法)执行的上下文环境(上下文,类似与你要了解一篇文章,了解文章的上下文你才能清晰的了解各种关系). 但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变. 1.在方法中,this 表示该方法所属的对象. 2.如果单独使用,thi

  • JavaScript必知必会(十) call apply bind的用法说明

    call 每个func 都会继承call apply等方法. function print(mesage) { console.log(mesage); return mesage; } print.call(this, "cnblogs");//cnblogs call(thisAgr,agr1,agr2...) ,call方法第一个传递一个context上下文.后面是参数的个数. apply apply(thisAgr,[agr1,agr2]),apply方法和call的用法一样,

  • 如何使用50行javaScript代码实现简单版的call,apply,bind

    在实现自己的call,apply,bind前,需要复习一下this. 所谓的this其实可以理解成一根指针: 其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象,这就是精髓.最关键所在 this的四种指向: 当this所在的函数被普通调用时,指向window,如果当前是严格模式,则指向undefined function test() { console.log(this); }; test(); 指向window 输出下面的代码: // Window {speec

  • JavaScript手写call,apply,bind方法

    目录 前言 改写this实现思路 前期准备 手写call方法 手写apply方法 手写bind方法 前言 改变this指向在书写业务的时候经常遇到,我们经常采用以下方法进行改写 使用作用声明变量存储this 使用jJavaScript的原生方法call,apply,以及bind进行改写 第一种方法就不说了,就是一个变量存储的问题,主要说第二种如何实现的 call,bind,apply方法都是JavaScript原生的方法,挂载在Function原型上,使得所有函数都可以调用,今天我们来实现一下c

  • Javascript中的this,bind和that使用实例

    这篇文章主要介绍了Javascript中的this,bind和that使用实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Javascript中必须通过this来访问类成员,可是this的特点就是函数绑在哪个对象上,它就指向那个对象.这个可能困扰过很多的程序员,特别是从C#,Java等语言过来的程序员. function Foo(){ this.message = 'This is message from Foo'; } Foo.prot

随机推荐