首页 \ 问答 \ js:ES5和ES6之间关于'this'关键字用法的一个令人困惑的观点(js: one confusing point about 'this' keyword usage between ES5 and ES6)

js:ES5和ES6之间关于'this'关键字用法的一个令人困惑的观点(js: one confusing point about 'this' keyword usage between ES5 and ES6)

我目前正在学习ES6标准,对于箭头功能部分,我发现了一个令人困惑的问题如下:

function foo() {
    setTimeout(() => {
        console.log('id:', this.id);
    }, 100);
}

var id = 21;

foo.call({ id: 42 });

以上是ES6标准,ES5如下

function foo() {
    setTimeout(function(){
        console.log('id:', this.id);
    }, 100);
}

var id = 21;

foo.call({ id: 42 });

In ES5, the output is id:21
In ES6, the output is id:42

我能理解ES6的结果。 但与ES5的结果非常混淆。 foo()函数的调用站点是foo.call({id:42}) ,因此调用对象是{id:42},但是当它执行时,调用对象变为window。 怎么会这样?


I am currently learning ES6 standard, for the arrow function part, I found a confusing point as following:

function foo() {
    setTimeout(() => {
        console.log('id:', this.id);
    }, 100);
}

var id = 21;

foo.call({ id: 42 });

the above is ES6 standard, and ES5 is as following

function foo() {
    setTimeout(function(){
        console.log('id:', this.id);
    }, 100);
}

var id = 21;

foo.call({ id: 42 });

In ES5, the output is id:21
In ES6, the output is id:42

I can understand the ES6's result. But very confused with the result from ES5. The call site for foo() function is foo.call({id:42}),so the calling object is {id:42}, but when it executes, the calling object changes to window. How so?


原文:https://stackoverflow.com/questions/43670878
更新时间:2019-07-08 12:55

最满意答案

这里有几件。 首先,要明白this实际上是一个由调用函数的东西自动设置的参数。 这对理解至关重要。 所以在这种情况下, setTimeout负责调用你的函数,它本质上是fn.call(undefined) ,因此没有明确的传入。

接下来,您的示例:

function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

VS

function foo() {
  setTimeout(function(){
    console.log('id:', this.id);
  }, 100);
}

箭头功能案例基本上是:

function foo() {
  var _this = this;

  setTimeout(function(){
    console.log('id:', _this.id);
  }, 100);
}

因此,在这两种情况下比较this ,第一个使用foo调用它,你的示例代码可以工作,因为你是foo.call({ id: 42 }); 显式设置比。 第二个例子使用setTimeout传递的this ,正如我上面提到的那样是undefined

但是,从.call传入的内容可能实际上并不是最终暴露在函数内部的内容。

  • 在非严格的代码中:如果它是一个对象,它会直接传递,但如果它不是一个对象,那么this就是全局对象(浏览器中的window ,Node中的global
  • 在严格的代码中: this是直接传递不变的

由于您的示例代码未标记为"use strict"; ,这意味着你正在打击这种全球行为。 这意味着你的两个例子基本上是这样的:

function foo() {
  setTimeout(function(){
    console.log('id:', window.id); // reads "id" from the global object
  }, 100);
}

VS

function foo() {
  var _this = this;

  setTimeout(function(){
    console.log('id:', _this.id); // reads "this" from `foo.call(...)`
  }, 100);
}

这里的最后一块是你的

var id = 21;

是在全局范围内,所以它实际上是在做window.id = 21 。 因此,这个价值贯穿始终。

我推荐的主要内容:

  • 不要在顶级作用域中执行代码,以便var声明不会在window结束。 如果您使用CommonJS或Webpack的ES6模块语法等模块系统,则会自动执行此操作。 如果这样做,您的代码将按原样记录undefined因为window.id将不存在。
  • 始终使用"use strict";标记您的代码"use strict"; 。 这通常很容易。 如果您正在使用CommonJS,只需将它放在文件的顶部,如果您使用的是ES6模块,它将自动添加,因为ES6模块必须严格。 如果你这样做,你的非箭头回调内部将是undefined所以this.id将引发错误。

There are a few pieces here. First off, understand that this is essentially an argument that is automatically set by the thing calling the function. That is critical to understand. So in this case, setTimeout is responsible for calling your function and it essentially does fn.call(undefined), so no explicit this is passed in.

Next, your examples:

function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

vs

function foo() {
  setTimeout(function(){
    console.log('id:', this.id);
  }, 100);
}

The arrow function case is essentially:

function foo() {
  var _this = this;

  setTimeout(function(){
    console.log('id:', _this.id);
  }, 100);
}

So comparing this in the two cases, the first uses the this that foo is called with, and your example code works because you're foo.call({ id: 42 }); explicitly sets than. The second example uses the this that setTimeout passes, which as I mentioned above is undefined.

However, the this that is passed in from .call may not actually be the this that ends up exposed inside the function.

  • In non-strict code: this is passed straight through, if it is an object, but if it is not an object, this is the global object (window in a browser, global in Node)
  • In strict code: this is passed straight through unchanged

Since your example code is has not been marked with "use strict";, this means you are hitting this global behavior. This means your two examples are essentially this:

function foo() {
  setTimeout(function(){
    console.log('id:', window.id); // reads "id" from the global object
  }, 100);
}

vs

function foo() {
  var _this = this;

  setTimeout(function(){
    console.log('id:', _this.id); // reads "this" from `foo.call(...)`
  }, 100);
}

The last piece here is that your

var id = 21;

is in the global scope, so it is essentially doing window.id = 21. Thus this value carries through.

The main takeaways here that I'd recommend:

  • Don't execute code in the top-level scope so that your var declarations don't end up on window. This will happen automatically if you use a module system like CommonJS or Webpack's ES6 module syntax. If you do this, your code as-is would log undefined since window.id would not exist.
  • Always mark your code with "use strict";. This usually is easy. If you're using CommonJS just put it at the top of your file, and if you're using ES6 modules, it will be added automatically because ES6 modules are required to be strict. If you do this, this inside your non-arrow callback would be undefined so this.id would throw an error.
2017-04-28

相关问答

更多

NodeJS计划支持导入/导出es6(es2015)模块(NodeJS plans to support import/export es6 (es2015) modules)

2017年2月更新: https://medium.com/@jasnell/an-update-on-es6-modules-in-node-js-42c958b890c#.6ye7mtn37 NodeJS的人已经决定, 最不好的解决方案是使用.mjs文件扩展名。 从这个外包是: 换句话说,给定两个文件foo.js和bar.mjs ,使用import * from 'foo'将foo.js视为CommonJS,而import * from 'bar'将会将bar.mjs视为ES6模块 至于时间表 ...

如何运行启用ES6功能的Node.js应用程序?(How to run Node.js app with ES6 features enabled?)

将babel-cli和babel-preset-es2015 (也称为ES6)依赖关系添加到应用程序的package.json文件中,并定义一个start脚本: { "dependencies": { "babel-cli": "^6.0.0", "babel-preset-es2015": "^6.0.0" }, "scripts": { "start": "babel-node --presets es2015 app.js" } } 然后,您可以简单 ...

怎么知道用的es5 还是es6

es6 比 es5 增加了很多特殊的方法,如果你遇到了这些特殊的方法,你就可以确定它是 es6。 如果你的代码中没有引用这些特殊的方法,那我们就可以认为他是 es5 的。 所以前提你需要了解 es6 的语法才能做判断,高频使用的特性有箭头函数、解构赋值、let、const。

何时应使用花括号进行ES6导入?(When should I use curly braces for ES6 import?)

这是默认导入 : // B.js import A from './A' 它仅在A具有默认导出时有效 : // A.js export default 42 在这种情况下,导入时为您分配的名称无关紧要: // B.js import A from './A' import MyA from './A' import Something from './A' 因为它总是会解决到A的默认导出 。 这是一个名为A导入 : import { A } from './A' 它仅在A包含名为 A的命名 ...

ES6类多重继承(ES6 Class Multiple inheritance)

一个对象只能有一个原型。 通过创建一个父对象作为两个父原型的组合,可以从两个类继承。 用于子类化的语法使得可以在声明中执行此操作,因为extends子句的右侧可以是任何表达式。 因此,您可以编写一个根据您喜欢的任何标准组合原型的函数,并在类声明中调用该函数。 An object can only have one prototype. Inheriting from two classes can be done by creating a parent object as a combinati ...

WebStorm,ES5 / ES3中的异步函数或方法需要'Promise'构造函数(WebStorm, An async function or method in ES5/ES3 requires the 'Promise' constructor)

添加 "lib": [ "es2015" ] 到tsconfig.json应该解决这个问题。 但是,您的spec文件似乎未包含在您的tsconfig.json中 (请选中"include":[]和"exclude":[] values)。 因此,Typescript服务必须为您的文件使用不同的tsconfig.json (如果没有找到包含您的规范的tsconfig.json文件,则可能是默认文件)要解决此问题,请确保在config中指定lib属性用于您的spec文件处理 Adding "lib" ...

@@(“at at”)在ES6 JavaScript中的含义是什么?(What does @@ (“at at”) mean in ES6 JavaScript?)

@@描述了所谓的着名符号。 (请注意,它在JS中实际上不是有效的语法。)根据ES6 / ES2015 1 规范 : 众所周知的符号是本规范算法明确引用的内置符号值。 它们通常用作其值作为规范算法的扩展点的属性的键。 除非另有说明,否则所有代码领域(8.2)共享众所周知的符号值。 代码领域指的是JavaScript环境的不同实例。 例如,根文档的代码领域将与在<iframe>运行的JavaScript代码领域不同。 一个关于对象来自什么代码领域的地方的例子是当试图使用instanceof来确定一个对 ...

使用Jest模拟Es6类(Mock Es6 classes using Jest)

更新: 所有这些信息和更多信息现已添加到Jest文档中,在一个新的指南“ ES6 Class Mocks”中 。 充分披露:我写了它。 :-) 嘲笑ES6类的关键是知道ES6类是一个函数 。 因此, 模拟也必须是一个功能 。 致电jest.mock('./mocked-class.js'); ,并且还导入'./mocked-class.js'。 对于任何需要跟踪调用的类方法,创建一个指向模拟函数的变量,如下所示: const mockedMethod = jest.fn(); 。 在下一步中使用这 ...

相关文章

更多

最新问答

更多
  • Android宽度:100%修复(网站接管问题)(Android width:100% fix (website takeover issue))
  • C ++函数/方法设计的良好实践(Good practice in C++ function/method design)
  • 计算其他表中不存在的所有记录 - SQL查询(Count all records that does not exist to other table - SQL Query)
  • 为什么我要用JPA共享ID?(Why do I get shared Ids with JPA?)
  • asp.net - 如何显示来自html格式的数据行的字段(asp.net - how to display a field from data row that is in html format)
  • 我们如何使用ActiveRecord从连接表中删除行?(How can we delete rows from a join table by using ActiveRecord?)
  • ng-class搞乱了类的顺序(ng-class messing with the order of classes)
  • oracle 12g无效数字错误(oracle 12g invalid number error)
  • 更改ng-src值onclick(Change ng-src value onclick)
  • 如何在android中自动添加自定义依赖项以创建新项目?(How to add custom dependencies automatically in android for ever a new project is created?)
  • datetime函数在PHP中(datetime function in php)
  • 在javascript中获取会话数组的值(in javascript get the value of a session array)
  • 如何在UTF8中编译LaTeX?(How can I compile LaTeX in UTF8? [closed])
  • Rspec:“array.should == another_array”,但不用担心订单(Rspec: “array.should == another_array” but without concern for order)
  • Logcat错误:无法在android片段中加载视图(Logcat error: unable to load view in android fragments)
  • JavaFX的。(JavaFX. Adding items to the list in different threads is not working)
  • 从GDATA日历资源迁移到Google Calendar Resource api(Migrate from GDATA calendar resource to Google Calendar Resource api)
  • SSRS 2008 - 以零情景处理分割(SSRS 2008 - Dealing with division by zero scenarios)
  • 我如何以编程方式添加一个listView列标题的点击事件(How can I add a listView column header a click event programmatically)
  • Wxpython:无法检索有关列表控件项XXX的信息(Wxpython: Couldn't retrieve information about list control item XXX)
  • 使用Tortoise SVN在SVN存储库中移动目录(Move Directory across SVN repository using Tortoise SVN)
  • 天蓝色服务结构集群中的web api无状态服务是否在一段时间不活动后进入休眠状态?(Do web api stateless services in azure service fabric cluster go to sleep after a period of inactivity?)
  • 我可以设置intelliJ来突出显示PHP编码风格吗?(Can I set intelliJ to highlight php coding style?)
  • 用javafx创建一个Truetype字体文件(Creating a Truetype Font file with javafx)
  • Spring ftp配置错误(Spring ftp configuration is wrong)
  • 使用gsub去除多个字符(Using gsub to strip multiple characters)
  • 续订推送证书并保持当前的App Store App正常工作(Renew Push certificate and keep current App Store App working)
  • js:ES5和ES6之间关于'this'关键字用法的一个令人困惑的观点(js: one confusing point about 'this' keyword usage between ES5 and ES6)
  • window.onload vs $(document).ready()(window.onload vs $(document).ready())
  • 在Swift中,如何声明一个符合一个或多个协议的特定类型的变量?(In Swift, how can I declare a variable of a specific type that conforms to one or more protocols?)