理解JavaScript函数调用和“this”

深入理解 JavaScript 中的 this 关键字 JavaScript 中的 this 是一个非常重要的概念,但它也是许多开发者容易混淆的地方。this 的值取决于函数的调用方式,而不是函数的定义位置。在这篇博客中,我们将深入探讨 this 的各种使用场景,帮助你更好地理解它的行为。 阅读详情

多年来,我看到很多关于JavaScript函数调用的混淆。特别是,很多人抱怨this函数调用的语义令人困惑。

在我看来,通过理解核心函数调用原语,然后查看在该原语之上调用函数作为糖的所有其他方法,可以清除很多这种混淆。事实上,这正是ECMAScript规范对此的看法。在某些领域,这篇文章是规范的简化,但基本思路是一样的。

核心原语

首先,让我们看一下核心函数调用原语,一个函数的call方法[1]。呼叫方法相对简单。

  • argList从参数1到结尾创建参数列表()
  • 第一个参数是 thisValue
  • 使用thisset to thisValue和argList作为其参数列表调用该函数

例如:

function hello(thing) {
  console.log(this + " says hello " + thing);
}

hello.call("Yehuda", "world") //=> Yehuda says hello world

如您所见,我们hello使用thisset to "Yehuda"和单个参数调用该方法"world"。这是JavaScript函数调用的核心原语。您可以将所有其他函数调用视为对该原语的贬低。(对“desugar”来说是采用方便的语法并用更基本的核心原语来描述它)。

[1]在ES5规范中,该call方法是根据另一个更低级的原语来描述的,但它在该原语之上是一个非常薄的包装器,所以我在这里简化了一下。有关更多信息,请参阅本文末尾。

简单的函数调用

显然,一直调用函数call会非常烦人。JavaScript允许我们使用parens语法直接调用函数(hello("world")。当我们这样做时,调用desugars:

function hello(thing) {
  console.log("Hello " + thing);
}

// this:
hello("world")

// desugars to:
hello.call(window, "world");

仅当使用严格模式 [2] 时,ECMAScript 5中的此行为已更改:

// this:
hello("world")

// desugars to:
hello.call(undefined, "world");

简短版本是:函数调用就像fn(...args)是一样fn.call(window [ES5-strict: undefined], ...args)。

请注意,对于内联声明的函数也是如此:(function() {})()与...相同(function() {}).call(window [ES5-strict: undefined)。

[2]实际上,我撒谎了一下。ECMAScript 5规范说undefined(几乎)总是传递,但被调用的函数应该thisValue在不处于严格模式时将其更改为全局对象。这允许严格模式调用者避免破坏现有的非严格模式库。

会员职能

调用方法的下一个非常常见的方法是作为object(person.hello())的成员。在这种情况下,调用desugars:

var person = {
  name: "Brendan Eich",
  hello: function(thing) {
    console.log(this + " says hello " + thing);
  }
}

// this:
person.hello("world")

// desugars to this:
person.hello.call(person, "world");

请注意,hello方法如何附加到此表单中的对象并不重要。请记住,我们之前定义hello为独立功能。让我们看看如果我们动态地附加到对象会发生什么:

function hello(thing) {
  console.log(this + " says hello " + thing);
}

person = { name: "Brendan Eich" }
person.hello = hello;

person.hello("world") // still desugars to person.hello.call(person, "world")

hello("world") // "[object DOMWindow]world"

请注意,该函数没有“this”的持久概念。它始终根据呼叫者调用的方式设置在呼叫时间。

运用 Function.prototype.bind

因为有时可以方便地引用具有持久this值的函数,所以人们历来使用简单的闭包技巧将函数转换为具有不变的函数this:

var person = {
  name: "Brendan Eich",
  hello: function(thing) {
    console.log(this.name + " says hello " + thing);
  }
}

var boundHello = function(thing) { return person.hello.call(person, thing); }

boundHello("world");

即使我们的boundHello调用仍然消失boundHello.call(window, "world"),我们右转并使用我们的原始call方法将this值更改回我们想要的值。

我们可以通过一些调整来使这个技巧成为通用目的:

var bind = function(func, thisValue) {
  return function() {
    return func.apply(thisValue, arguments);
  }
}

var boundHello = bind(person.hello, person);
boundHello("world") // "Brendan Eich says hello world"

为了理解这一点,您只需要两条信息。首先,arguments是一个类似于Array的对象,它表示传递给函数的所有参数。其次,该apply方法与call原语完全相同,只是它采用类似于Array的对象,而不是一次列出一个参数。

我们的bind方法只返回一个新函数。调用它时,我们的新函数只调用传入的原始函数,将原始值设置为this。它也通过参数传递。

因为这是一个有点常见的习惯用法,ES5 bind在Function实现此行为的所有对象上引入了一个新方法:

var boundHello = person.hello.bind(person);
boundHello("world") // "Brendan Eich says hello world"

当您需要将原始函数作为回调传递时,这非常有用:

var person = {
  name: "Alex Russell",
  hello: function() { console.log(this.name + " says hello world"); }
}

$("#some-div").click(person.hello.bind(person));

// when the div is clicked, "Alex Russell says hello world" is printed

当然,这有点笨拙,TC39(负责ECMAScript下一版本的委员会)继续研究更优雅,仍然向后兼容的解决方案。

在jQuery上

因为jQuery大量使用匿名回调函数,所以它在call内部使用该方法将this这些回调的值设置为更有用的值。例如,jQuery 不是window像this在所有事件处理程序中那样接收(就像没有特殊干预一样),而是call使用将事件处理程序设置为其第一个参数的元素调用回调。

这是非常有用的,因为默认值this的匿名回调不是特别有用,但它可以给JavaScript能给初学者的印象this是,一般一个怪,经常发生突变的概念是很难推论。

如果您了解将含糖函数调用转换为desugared的基本规则func.call(thisValue, ...args),您应该能够导航JavaScript this值不那么危险的水域。

PS:我被骗了

在一些地方,我从规范的确切措辞中略微简化了现实。可能最重要的作弊是我称之为func.call“原始”的方式。在现实中,该规范具有原始(内部称为[[Call]]),这两个func.call和[obj.]func()使用。

但是,看一下定义func.call:

  • 如果IsCallable(func)为false,则抛出TypeError异常。
  • 让argList为空List。
  • 如果使用多个参数调用此方法,则从arg1开始以从左到右的顺序将每个参数附加为argList的最后一个元素
  • 返回调用func的[[Call]]内部方法的结果,提供thisArg作为此值,并将argList作为参数列表。

如您所见,此定义本质上是一种非常简单的JavaScript语言绑定到基本[[Call]]操作。

如果你看一下调用函数的定义,第一七个步骤设置thisValue和argList,最后一步是:“返回调用的结果[电话]]上FUNC内部方法,提供thisValue作为该值,并提供列表argList作为参数值。“

一旦确定argList并且thisValue已经确定,它基本上是相同的措辞。

我在调用call一个原语时作了一些欺骗,但其含义基本上与我在本文开头提取规范并引用章节和诗句时的含义相同。

我还with没有在这里介绍一些其他案例(最值得注意的是涉及)。

保姆级教程:在Ubuntu 22.04上搞定PX4 V1.14 + ROS2 Humble的HITL仿真(避坑实录) 本文提供在Ubuntu 22.04上配置PX4 V1.14与ROS2 Humble的HITL仿真环境的详细教程,涵盖环境准备、固件编译、QGroundControl配置、ROS2通信设置及联调测试。特别针对版本兼容性、Gazebo插件构建和MicroDDS配置等常见问题提供避坑指南,帮助开发者高效搭建无人机仿真系统。 阅读详情

相关推荐

手把手教你用Three.js+Vue打造3D机房可视化系统(附完整代码)

本文详细介绍了如何使用Three.js与Vue 3构建企业级3D机房可视化系统。从技术选型、项目架构设计到3D场景构建、设备建模与渲染策略,提供了完整的开发方案和实战代码。重点讲解了如何实现设备状态实时监控、交互操作与性能优化,帮助开发者快速掌握3D可视化开发的核心技术。

ttt77的博客 320

【JavaScript核心】调用函数的四种方式

函数定义从function关键字开始,构成函数主体的 JavaScript 代码在定义之时并不会执行,只有调用该函数时,它们才会执行。关于变量提升:var 只有变量声明提前,变量的初始化代码仍然在原来的位置;然而 function 则会使函数名称和函数体均提前。作为函数调用作为方法调用作为构造函数调用(new调用)上下文调用(callapplybind在 ECMAScript 2015(ES6)之前,函数内部的this指向是由该函数的调用方式决定的。

coder 1173

第1部分 基础算法(提高篇)--第2章 二分与三分1439:【SCOI2010】传送带

1439:【SCOI2010】传送带 时间限制: 2000 ms 内存限制: 65536 KB 提交数: 372 通过数: 212 【题目描述】 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段。两条传送带分别为线段AB和线段CD。lxhgww在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R。现在lxhgww想从A点走到D点,他想知道最少需要走多长时间。 【输入】 输入数据第一行是4个整数,表示A和B的坐标,分别为Ax,Ay,Bx,By 第二行是4个整

zqhf123的博客 839

JavaScript 函数调用

但在 JavaScript 中,它默认是属于全局对象的。在 HTML 环境里,默认的全局对象就是 HTML 页面本身,而在浏览器中,页面对象其实就是浏览器窗口(这看起来好像是在创建一个新的函数,但实际上 JavaScript 是在创建一个新的对象。就是函数预定义的两个方法,它们都可以用来调用函数,而且这两个方法的第一个参数都必须是对象本身。关键字一开始是没有值的,它的值是在函数调用并实例化对象(创建新对象)的时候才确定的。当我们调用构造函数时,会创建一个新的对象,这个新对象会继承构造函数的属性和方法。

梁辰兴的博客 333

YOLOv8改进——引入可变形卷积DCNv3

本文只讲解在YOLOv8的代码中添加DCNv3的操作流程, 具体的原理参见上述的链接~是pytorch实现的版本,只要基础的pytorch环境安装正确就不会出错。是C++实现版本,必须先在上一步编译成功,或者安装好了轮子,否则会报错。但是在实际的训练过程中,C++版本的运行速度更快,推荐使用C++版本。(具体怎么修改,都可以自己决定,然后通过实验看看效果如何)文件夹,该文件夹下的内容就是实现DCNv3算子的核心代码。中,下图中的列表里添加。

zyw2002的博客 3万+

RT-DETR改进策略【损失函数篇】| 通过辅助边界框计算IoU提升检测效果(Inner_GIoU、Inner_DIoU、Inner_CIoU、Inner_EIoU、Inner_SIoU)

为了弥补现有 IoU 损失在不同检测任务中泛化性弱和收敛速度慢的问题,·Inner-IoU·通过引入比例因子 “ratio” 来控制辅助边界框的尺度大小,利用不同尺度的辅助边界框来计算损失,从而加速边界框回归过程。

Limiiiing的博客 1966

js函数里的函数怎么调用,js在函数里面调用函数

Function()

wenangou的博客 1639

手把手教你学Simulink——基于Simulink的重复控制抑制周期性负载转矩扰动

传统的PI调节器在面对这类特定频率的周期性扰动时,会因积分器的相位滞后而导致转速出现明显的纹波(如6次、12次谐波),严重影响系统的控制精度和静音性能。:要在频率 fd​处获得较高的开环增益以抑制扰动,必须大幅增加PI的比例系数 Kp​,但这会牺牲系统的相位裕度,导致高频共振甚至系统失稳;:电机转速变化时,基频 fbase​会变,导致 6次谐波频率 6×fbase​也在变。设系统的采样周期为 Ts​,目标抑制的周期性扰动频率为 fd​(rad/s),则对应的周期采样点数 N=fd​Ts​2π​。

MHD0815的博客 256

JavaScript的函数调用

函数调用有三种方式: 1.直接调用 2.call调用 3.apply调用 一、直接调用 直接调用函数是最常见、最普通的方式。这种方式直接以函数附加的对象作为调用者,在函数后括号内传入参数来调用函数。例如下代码: <script> function person(name, age) { this.name = name; this.age = age; ...

m566666的博客 7052

基于Simulink进行光伏逆变器效率分析的仿真建模

特点能量转换:将光伏电池板产生的直流电转换为交流电。效率评估:分析逆变器在不同负载条件和环境条件下的效率。动态特性:考虑温度、光照强度等外部因素对逆变器效率的影响。挑战精确建模:准确地模拟光伏电池板、逆变器及其交互的动力学过程。多因素考量:需要同时考虑输入电压、电流、温度、光照强度等多种因素对逆变器效率的影响。集成度:需要与其他系统(如最大功率点跟踪MPPT算法、电网连接等)无缝集成,以实现全面的系统分析。首先明确要模拟的任务内容。输入条件(如光照强度、温度等)。

amy_mhd的博客 142

20、基于图神经网络的脱氢焓预测方法研究

本文研究了基于图神经网络(GNN)的液态有机氢载体(LOHC)材料脱氢焓预测方法。通过分析现有GNN方法的局限性,提出了一种新的聚合自节点表示(AWS)方法,并结合丰富的原子特征和集成学习策略,显著提高了预测性能。实验结果表明,AWS在脱氢焓预测和多个基准化学性质预测任务中均优于现有GNN方法。此外,基于分子结构的集成方法进一步提升了模型的鲁棒性和准确性。研究展示了该方法在筛选LOHC材料中的潜力,并为未来预测更多化学性质奠定了基础。

pp12345的博客 103

JavaScript 函数的调用方式有哪些

5.定时器函数—这个函数是定时器自动1秒钟调用一次。6.立即执行函数---- 立即执行函数是自动调用。

老段的优快云的博客 322

【3天快速入门WPF】从小白到新手入门教程

嘿,小伙伴们!如果你已经有一定的C#开发基础,但想快速掌握WPF开发,达到企业级水准,那接下来的这个三天快速入门计划绝对适合你!虽然听起来有点挑战,但别担心,只要跟着这个高强度、结构化的学习计划,你一定能搞定它。首先,我们得搞清楚,WPF的核心知识点到底有哪些。要想达到企业级开发水准,XAML、数据绑定、MVVM、控件使用、命令系统、样式模板这些统统都得拿下。听起来是不是有点多?别急,我们把任务分解到三天里,每天安排合理的内容,保证理论和实践相结合,让你学得扎实又高效。

Zy100Papa的博客 1423

理解 JavaScript 函数调用和“this”

理解 JavaScript 函数调用和“this” 耶胡达·卡茨|2011 年 8 月 10 日| 5 分钟阅读 多年来,我看到了很多关于 JavaScript 函数调用的困惑。特别是,很多人抱怨thisin 函数调用的语义令人困惑。 在我看来,通过理解核心函数调用原语,然后将所有其他调用函数的方法视为该原语之上的糖,可以消除很多这种混淆。事实上,这正是 ECMAScript 规范的想法。在某些方面,这篇文章是对规范的简化,但基本思想是相同的。 核心原语 首先,让我们看看核心函数调用原语,一个函数的cal

m0_47202518的博客 188

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

理解javascript函数调用和“this”

2011.8.11 多年以来,我看到大量关于javascript函数调用的困惑。尤其,许多人抱怨函数调用中“this”的语意是混乱的。 在我看来,大量这样的混乱可以通过理解核心函数调用原语被清理,然后再看所有其他在原语之上进行包装的调用函数的方法。实际上,这正好是ECMAScript规格对这个问题的考虑。在某些领域,这个是一个规格的简化,但基本思想是一样的。 核心原语 首先,我们来看核

常绍新的专栏 3259

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

理解JavaScript函数调用和"this"

作者:Yehuda Katz 原文链接:http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/ 多年以来,我看到了许多人对于JavaScript函数调用方面存在困惑与不解。特别是许多人会抱怨,"this"在函数调用中的语义是令人疑惑的。 在我看来,通过理解核心函数调用的原始

fendouzhe123的专栏 752

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

理解Javascript中的函数调用和“this“

函数调用的核心 使用call方法进行的调用,任何函数调用都可以使用call来避免不必要的麻烦//第一个参数作为this,后面的作为参数,func是需要调用call()方法的函数 func.call(context, arg1, arg2, ...) 例如:function hello(thing) { console.log(this + " says hello " + thing); } hello.call("Yehuda", "world") //输出为:Yehuda says hel

AnitaSun的博客 145

(翻译)理解JavaScript函数调用和"this"(by Yehuda Katz)

多年以来,我看到大量关于javascript函数调用的困惑。尤其,许多人抱怨函数调用中“this”的语意是混乱的。 在我看来,大量这样的混乱可以通过理解核心函数调用原语被清理,然后再看所有其他在原语之上进行包装的调用函数的方法。实际上,这正好是ECMAScript规格对这个问题的考虑。在某些领域,这个是一个规格的简化,但基本思想是一样的。 核心原语 首先,我们来看核心函数调用原语,一个函数的...

weixin_33749131的博客 100

全面理解JS中的this

JavaScript开发中,我们经常会用到this,也经常会因为this头疼,为什么呢,因为有时候我们不知道this到底指的是什么?怎么用? 其实this是执行上下文对象的一个属性,执行上下文中有三个重要的属性,作用域链(scopeChain)、变量对象(VO)和this。 this是在进入执行上下文时确定的,也就是在函数执行时才确定,并且在运行期间不允许修改并且是永久不变的。 我们分各种执行上下文情况来详细解读下this: 一、在全局代码中的this 在全局代码中this 是不变的,this始终

txun123的博客 3038

深入理解this指向【JavaScript】

在JavaScript中,this是一个特殊的关键字,用于引用函数调用的上下文对象。理解this的指向对于掌握JavaScript至关重要。

2302_77228054的博客 1271

Vuejs和Javascript中的this变量的理解

文章目录1 javascript1.1 对象中this1.2 函数中this2 Vuejs2.1 vue组件中2.2 回调函数2.3 箭头函数 1 javascript 1.1 对象中this //test.js var out = { function inner(){ this } } // 此时函数中的this表示out对象 1.2 函数中this //test.js function inner(){ this } // 此时函数中的this表示全局对象window 2 Vuejs 2

上善若泪 4006

【JS】Javascript中的this到底是什么

JavaScript中的this是一个对于新手来说特别吓人并且不友好的概念。对于一个前端萌新来说,这个概念既模糊,又看不到它存在的意义。本文将解析JavaScript中的this,并且逐一分析他在每个常见场景中的意义和指向。

a1611107035的博客 3273

[译] 理解JS的函数调用和‘this’的指向

原标题:Understanding JavaScript Function Invocation and "this",出自Yehuda的这篇博客,是在Typescript的中文教程里看到的。 JS的函数调用一直以来给不少人带来疑惑,其中this的语义是人们抱怨的最多的。 在我看来,首先理解了函数调用的原始核心语法,然后弄清楚其他调用函数的语法糖,这些疑惑就能解决了。实际上这正式ECMA规范所...

weixin_34290352的博客 106
上一篇: 网络广告的新含义
下一篇: 关于双击.py文件闪退的问题
xosg
xosg 领域专家: 网络协议与安全技术领域 领域专家: 网络协议与安全技术领域
博客等级 码龄9年 1986粉丝 864原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xosg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值