不同类型函数调用之间的主要区别在于:最终作为函数上下文(可以通过this参数隐式引用到)传递给执行函数对象不同。对于方法而言,即为所在的对象;对于函数而言是window或是undefined(取决于是否处于严格模式下);

对于构造函数而言是一个新创建的对象实例。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>为函数绑定特定的上下文</title>
<script src="../unitl/test.js"></script>
<style>
#results li.pass {color:green;}
#results li.fail {color:red;}
</style>
</head>
<body>
<button id="test">Click Me</button>
<ul id="results"></ul>
</body>
<script> function Button() {
this.clicked = false;
//单击事件处理器的声明函数。由于该函数是对象的方法,所以在函数中使用this来获取对象的引用。
this.click = function name(params) {
this.click = true;
//在该方法中,我们测试了按钮是否在单击后正确改变了状态
assert(button.clicked,"The button hs been clicked");
} } //创建一个用于跟踪是否被单击的实例
var button = new Button(); //在按钮上添加单击处理器
var elem = document.getElementById("test");
elem.addEventListener("click",button.click); </script>
</html>

在这个例子中,我们定义了一个按钮Click Me,并且想知道他是否曾被单击过。为了保存单击状态的状态信息,我们使用构造函数创建一个名为button的实例化对象,通过该对象我们可以存储被

单击的状态:

      function Button() {
this.clicked = false;
//单击事件处理器的声明函数。由于该函数是对象的方法,所以在函数中使用this来获取对象的引用。
this.click = function name(params) {
this.click = true;
//在该方法中,我们测试了按钮是否在单击后正确改变了状态
assert(button.clicked,"The button hs been clicked");
} }
var button = new Button();

在该对象中,我们还得意了click方法作为单击按钮式触发的事件处理函数,该方法将clicked属性设置为true,然后测试实例化对象的状态是否正确(我们有意使用button标识符而非this关键字)----它们应该具有相同的指向,

(但事实果真如此吗?)。最后,我们创建了button.click方法作为按钮的单击处理函数。

      var elem = document.getElelemtById('test');
elem.addEventListener("click",button.click);

当我们在浏览器中加载示例代码并单击按钮时,显示结果如下图所示,红色部分的文件表示测试失败了,在本例中的代码之所以测试失败,是由于click函数的上下文对象并非像我们预期的一样指向button对象。



我们的测试为何失败了?我们对按钮单击状态的改变去哪儿了?通常情况下,事件回调函数的上下文是粗发事件的对象(在本例中是HTML的按钮,而非button对象)。回想本章前面部分的内容,如果通过button.click()调用函数,上下文将是按钮,因为函数将作为button对象的方法被调用。但在这个例子中,浏览器的事件处理系统将把调用的上下文定义为事件触发的目标

元素,因此上下文将是<button>元素,而非button对象。所以我们将单击状态设置到了错误的对象上。

这是一个令人惊讶的常见问题,本章的后面将会介绍如何 避免出现这种情况。 现在探讨一个如何解决它:可以使用apply和call方法显式得设置函数上下文。

使用apply和call方法

javascript为我们提供了一种调用函数的方式,从而可以显式地指定任何对象作为函数的上下文。我们可以使用每个函数上都存在的这两种方法来完成:apply和call。

我们所指的正是函数的方法。作为第一类对象(顺便说一下,函数是由内置的Function构造函数所构建),我们可以像其他对象类型一样拥有属性,也包括方法。

若想使用apply方法调用函数,需要为其传递两个参数:作为函数上下文的对象和一个数组作为函数调用的参数。call方法的使用方式类似,不同点在于是直接以参数列表的形式,而不再是作为数组传递。

下面例子演示了这两种方法的实际使用方式

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>使用apply和call方法来设置函数上下文</title>
<script src="../unitl/test.js"></script>
<style>
#results li.pass {color:green;}
#results li.fail {color:red;}
</style>
</head>
<body>
<ul id="results"></ul>
</body>
<script> //函数"处理"了参数,并将结果result变量放在一个作为该函数上下文的对象上。
function juggle() {
var result = 0;
for(var n=0; n<arguments.length; n++) { result += arguments[n]; }
this.result = result;
}
//这些对象的初始值为空,它们会作为测试对象
var ninja1 = {};
var ninja2 = {}; //使用apply方法向ninja1传递一个参数数组
juggle.apply(ninja1,[1,2,3,4]);
//使用call方法向ninja2传递一个参数列表
juggle.call(ninja2,5,6,7,8); //测试展现了传入juggle方法中的对象拥有了结果值。
assert(ninja1.result ===10,"juggled via apply");
assert(ninja2.result === 26, "juggled via call"); </script>
</html>

在这个例子中,我们定义了一个名为juggle的函数,函数的作用是将所有的参数加载一起并存储在函数上下文的result属性中(通过this关键字引用)。看起来似乎是一个不太实用的函数,但它能够帮助我们验证函数的传参是否正确,以及哪个对象最终作为函数上下文。

然后设置两个对象:ninja1和ninja2,我们将使用这两个对象作为函数上下文,第一个对象连同一个参数数组一起传递给函数的apply方法,将第二个对象连同参数列表传递给函数的call方法。

      juggle.apply(ninja1,[1,2,3,4]);
juggle.call(ninja2,5,6,7,8);

值得注意的是,apply和call之间唯一的不同之处在于如何传递参数。在使用apply的情况下,我们使用参数数组;在使用call的情况下,我们则在函数上下文之后依次列出调用参数。



上图传入的call和apply方法的第一个参数都会被作为函数上下文,不同处在于后续的参数。apply方法只需要额外的参数,也就是一个包含参数的数组;call方法则需要传入任意数量的参数值,

这些参数将用作函数的实参。

现在已经提供了行数上下文和参数,接下来继续测试!首先,检查传递给apply方法的ninja1对象,他应该拥有一个result属性,并存储了所有参数(1,2,3,4) 的和,同样,传递给call方法的ninja2对象的result属性值应该等于参数5、6、7、8的和。

    assert(ninja1.result ===10,"juggled via apply");
assert(ninja2.result === 26, "juggled via call");

call和apply这两个方法对于我们要特殊指定一个函数的上下文对象时特别有用,在执行回调函数时可能会经常用到。

function juggle() {
var result = 0;
for(var n=0; n<arguments.length; n++) { result += arguments[n]; }
this.result = result;
}
var ninja1 = {}
var ninja2 = {}



使用call和apply方法手动设置函数上下文,产生函数上下文(this)与arguments。

函数调用_通过apply和call方法调用的更多相关文章

  1. JavaScript中bind、call、apply函数使用方法具体解释

    在给我们项目组的其它程序介绍 js 的时候,我准备了非常多的内容,但看起来效果不大,果然光讲还是不行的,必须动手. 前几天有人问我关于代码里 call() 函数的使用方法.我让他去看书,这里推荐用js ...

  2. [Effective JavaScript 笔记]第18条:理解函数调用、方法调用及构造函数调用之间的不同

    面向对象编程中,函数.方法.类的构造函数是三种不同的概念. JS中,它们只是单个构造对象的三种不同的使用模式. 三种不同的使用模式 函数调用 function hello(username){ ret ...

  3. S1:动态方法调用:call & apply

    js中函数执行的两种方式:一是通过调用运算符’()’,二是通过调用call或apply来动态执行. 一.动态方法调用中指定this对象 开发中我们往往需要在对象B中调用对象A的方法,这个时候就用到了a ...

  4. JavaScript 方法调用模式和函数调用模式

    这两天在读<JavaScript语言精粹>关于第4章函数调用的几种模式琢磨了半天. 这里就说一下方法调用模式跟函数调用模式. 方法调用模式: 当一个函数被保存为对象的一个属性时,我们称它为 ...

  5. python中方法调用和函数调用的区别

    函数调用: 传几个参数,就会有几个实参方法调用: 默认传递一个参数self,至少要定义一个形参

  6. Chapter3_操作符_方法调用中的别名问题

    接下来展示方法调用中的别名问题,方法调用中的别名问题指的是,将一个对对象的引用传递给某一个方法时,方法操作的是这一个特定的引用而不是这个引用的拷贝. class Person{ float heigh ...

  7. Javascript中call,apply,bind方法的详解与总结

    在 javascript之 this 关键字详解 文章中,谈及了如下内容,做一个简单的回顾: 1.this对象的涵义就是指向当前对象中的属性和方法. 2.this指向的可变性.当在全局作用域时,thi ...

  8. JS中的call、apply、bind方法详解

    bind 是返回对应函数,便于稍后调用:apply .call 则是立即调用 . apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(co ...

  9. 第164天:js方法调用的四种模式

    js方法调用的四种模式 1.方法调用模式 function Persion() { var name1 = "itcast", age1 = 19, show1 = functio ...

  10. js中apply()和call()方法的使用

    1.apply()方法 apply方法能劫持另外一个对象的方法,继承另外一个对象的属性.  Function.apply(obj,args)方法能接收两个参数     obj:这个对象将代替Funct ...

随机推荐

  1. MAC怎么快速截图

    ​ 1.截取全屏 按住[command][shift][3]这三个键即可截取全屏. ​ 编辑 2.截取某区域 按住[command][shift][4]. ​ 编辑 3.截图完成 在电脑桌面能找到截图 ...

  2. shell脚本定时任务转移项目日志

    1.之前同时项目部署在根目录,根目录磁盘空间40G,运行一年后日志占了18G的磁盘空间, 根目录只有几个G的磁盘空间,现在写shell脚本定时转移日志文件到挂载的磁盘目录下 2.编写shell脚本 # ...

  3. tomcat不生成日志文件

    1.不生成 catalina.out 将 /bin/catalina.sh 文件中的: if [ -z "$CATALINA_OUT" ] ; then CATALINA_OUT= ...

  4. 使用yum快速安装mysql-5.7(用于测试)

    1)CentOS 7 下安装 MySQL 5.7 下载并安装MySQL官方的 Yum Repository [wget -i -c http://dev.mysql.com/get/mysql57-c ...

  5. 为 windows 10 右键菜单加打开DOS窗口

    创建一个批处理文件,输入以下行,保存执行即可. echo off reg add "HKCR\*\shell\ms-dos" /ve /d 打开DOS命令 /f reg add & ...

  6. jquery随手做的一个滚动库 我称之为 idkScroll

    /** * 仅做到底自动刷新功能 * options: { * page int 当前在第几页 * limit int 每页个数 * onBottom function 到底的回调 * whenEnd ...

  7. lua如何转化为exe

    在这里下载    https://wwn.lanzout.com/iIS9d07rpesh 然后用cmd到 下载的盘:\luapack\luapack\bin\Debug 然后glue.exe srl ...

  8. hdu 4283You Are the One

    The TV shows such as You Are the One has been very popular. In order to meet the need of boys who ar ...

  9. typora破解使用

    1.将example文件夹以及run.exe同时拷贝到typora安装目录下的\Typora\resources\ 里面. 2.然后运行run.exe,会出现一个cmd弹窗,然后会消失,此时,激活码k ...

  10. Java的JDK以及maven环境变量配置

    右键我的电脑->属性->高级->环境变量->系统变量 新建变量名(win7,win10系统变量)JAVA_HOME变量值填写D:\Java\jdk1.8.0_172为jdk的安 ...