不同类型函数调用之间的主要区别在于:最终作为函数上下文(可以通过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. 51nod 1594 Gcd and Phi

    Link 题解: $ans = \sum_{i = 1}^{n}\sum_{j = 1}^{n}phi(gcd(phi(i), phi(j)))$ $=\sum_{d = 1}^{n}phi(d)\s ...

  2. Luckysheet文档 设置表格内容和背景色

    Luckysheet的使用参考  https://mengshukeji.gitee.io/LuckysheetDocs/zh/guide/api.html#%E5%8D%95%E5%85%83%E6 ...

  3. Redis设置开机自启动

    0.前提条件 redis_version:7.0.5 Linux Alibaba Cloud Linux release 3 (soaring Falcon) 查询版本有如下两种方式: 1.通过red ...

  4. JQUERY动态的修改<div>样式

    首先需要引入JQUERY <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"> & ...

  5. pycharm 连接服务器进行操作比 Xshell 更简单!

    一.SSH 连接 pycharm 中通过 SSH 连接服务器非常的简单,只需要简单的几步操作就能完成,如下图 1.第一步:点击开启 SSH 会话 2.第二步:填入对应的连接信息,点击 OK 3.这个时 ...

  6. NXOpen获取UFUN的tag

    #include <NXOpen/NXObject.hxx>#include <NXOpen/NXObjectManager.hxx> 1 NXObject* nXObject ...

  7. 【GROMACS】分子动力学模拟①——环境搭建

    系统环境 Win11 22H2 企业版 开启虚拟化.window subsystem for liunx等虚拟机相关的功能 应用商店中安装WSL2 安装步骤 打开Ubuntu,输入sudo apt f ...

  8. 模拟ATM系统 —— 用户存款、取款、转账、修改密码和销户功能

    接着上一篇: 5.用户存款功能 分析: *存款就是拿到当前账户对象 *然后让用户输入存款的金额 *调用账户对象的setMoney方法将账户余额修改成存钱后的余额 *存款后需要查询当前账户信息,确认是否 ...

  9. Word运行 没有注册类

    启动Word时出现Microsoft Visual Baisc窗口报错,提示"运行时错误 没有注册类""运行时错误 424 要求对象" 删除路径为 C:\用户\ ...

  10. 使用JFinal实现简单的学生管理系统

    JFinal简介 Controller是JFinal核按心类美之一,该类作为MVC模式中的控制器.基于JFinal的Web应用的控制器需要继承该类.Controller是定义Action方法的地点,是 ...