代码如下:
var name="Window";
var object={
name:"object",
getName:function (){
var a= function(){
return this.name;
};
console.log(a());
return a;
}
}
object.getName()();
运行结果:
Window
Window
我的问题在于console.log(a());
这一行。return a
后,是在全局作用域中执行的返回的匿名函数,所以this
的值是window
。但是我在getName
函数中直接执行a()
的话,根据this
的定义“ this 指向函数运行时的执行环境对象”,而每个函数执行时都会创建一个自己的执行环境,那么a()
执行时this
的值应该是getName
函数的环境对象,这样 a 函数内对this.name
的引用就应该得到undefined
。请问我的理解有什么问题呢?谢谢:)
1
4ark 2019-01-30 18:50:23 +08:00 via Android 1
https://www.v2ex.com/t/529532 看看吧,另外贴代码的时候能不能带有格式啊,看着真糟心!
|
2
4ark 2019-01-30 18:54:27 +08:00 via Android 1
就你这段代码而言,a 并不属于任何对象,也没有通过 call 等方法调用,所有这样的函数 this 默认指向 Window,如果是严格默认下,就是 undefinde。
|
3
4ark 2019-01-30 18:56:13 +08:00 via Android
收回我说的第一句话,才发现我的文章代码也没有格式,为什么 V2EX 会这样??
|
9
Nitroethane 2019-01-30 19:15:59 +08:00 via Android
|
10
4ark 2019-01-30 19:20:31 +08:00 via Android
@Nitroethane 求下载链接,感激不尽!
|
11
4ark 2019-01-30 19:28:01 +08:00 via Android
@Nitroethane 已在 play 商店搜到
|
12
rabbbit 2019-01-30 19:41:38 +08:00 3
this 是什么取决于如何调用
本文中 a 函数的调用者是 a() 而非外层的 object.getName() 当 a 函数被作为变量调用时, 会将内部方法 GetBase(ref). ImplicitThisValue 返回的值作为 thisArg(通常是 undefined) http://ecma-international.org/ecma-262/5.1/#sec-11.2.3 在进入函数时 1 如果是严格模式,把 this 指向 thisArg 2 如果 thisArg 为 undefined,则将 this 指向 window http://ecma-international.org/ecma-262/5.1/#sec-10.4.3 总之一句话,作为变量调用的函数里边的 this 都指向 window |
13
OSF2E 2019-01-30 19:54:13 +08:00
《 Javascript 高级程序设计(第三版)》( P73 ) 4.2 执行环境与作用域
|
14
rabbbit 2019-01-30 19:55:32 +08:00 1
|
15
fourstring OP @OSF2E #13 谢谢,我看的就是这本书。我之所以会认为 a()的结果是"object",就是因为这本书 5.5.4 里说 this “引用的是函数执行的环境对象”,然后 4.2 节说“每个函数都有自己的执行环境”,我看了下面别的 v 友回复以后,觉得应该是我对“执行环境”理解有误,但是这本书里确实没有明确讲解函数的执行环境究竟是什么(也就是不是词法环境)。
|
16
fleam 2019-01-30 20:01:49 +08:00 via Android
我总结的就是相关性就近原则,菜鸟飞过。。。
|
17
X37B 2019-01-30 20:38:06 +08:00 1
this(4 个规则):函数位置上的调用形式
默认绑定 独立调用 fn() this -->window "use strict" this --> undefined 隐式绑定 对象.对象.fn() this -->最近的调用者 隐式丢失? 使用隐式调用的形式进行传参 赋值;使用其他形式进行调用 this 的指向发生改变 解决隐式丢失? 硬绑定 bind 返回一个新的函数 显示绑定 apply call 立即调用 this --> 绑定的对象 构造绑定 new fn() this --> 构造出来的实例对象 当一行代码出现多个绑定,this 绑定优先级:new --> 显示 --> 隐式 --> 默认 |
18
shintendo 2019-01-30 20:42:26 +08:00 1
其实很简单,你的 this 在哪个函数里面?在 a 里面,那么 this 的值只取决于 a 被谁调用,跟其它什么 getName 没有任何关系。
而所谓的 a 被谁调用,指的是“调用者.a()”这种调用,不是说 a()写在 getName 里面就是被 getName 调用了这种意思。 |
19
fourstring OP @X37B #17 请问知道这样的知识什么地方或者书能看到呢
|
20
secondwtq 2019-01-30 21:17:41 +08:00 1
@fourstring 犀牛书应该是这样理解语言设计者的意图的:词法作用域和 this 都属于函数运行环境 /上下文,现在的实践中,一般词法作用域是写代码时固定的,this 允许用户在运行时动态指定上下文,并且这东西和函数调用语法关联,因此也是一种“环境”
实际上现在的 OO 语言对于 this 的实现基本就是函数参数,也就是非要说的话所有参数都是“环境”的一部分 ... 不过 OO 的语义上 this 确实有环境的意思 词法作用域环境也可以动态绑定,用 eval 或者 with 就行,只不过现在没人用了 |
21
OSF2E 2019-01-30 21:19:07 +08:00
@fourstring
首先,这部分内容要结合第 7 章的内容来理解。 另外,这一节有一个关键概念是执行环境的变量对象( variable object ),每一个函数的执行环境( execution context )中所定义的变量和函数都保存在这个对象中。而 web 浏览器中最外层的执行环境(的变量对象)被认为是 window 对象,因此所有全局变量和函数都是作为 window 对象的属性和方法创建的。 因此,你的代码中的匿名函数 a 在定义之初便自动“挂靠”到全局执行环境中的(解析器就是这么解析的,没有为什么),即该匿名函数是 window 对象的方法,所以 this 自然指向 window。 可以使用 bind 方法改变该匿名函数“挂靠”的变量对象,代码如下: var a = function(){}.bind(this); |
22
rabbbit 2019-01-30 22:02:26 +08:00
|
23
rabbbit 2019-01-30 22:09:09 +08:00 1
如果指的是第 7 章 182 页 "匿名函数的执行环境具有全局性" 这句话的话
这句话是翻译错误,原文是 ``` Anonymous functions are not bound to an object in this context, meaning the this object points to window unless executing in strict mode (where this is undefined). ``` 翻译:在这个上下文(执行环境)中匿名函数并没有绑定到任何一个对象中,意味着 this 指向 window (除非这个上下文(执行环境)是在严格模式下执行的,而严格模式下该 this 指向 undefined ) https://www.zhihu.com/question/21958425/answer/278063919 |
24
palmers 2019-01-30 22:35:42 +08:00
this 是动态的指向当前对象 并不会主动的缓存 所以 是 window 在 getName 函数中将 this 缓存起来就会得到你预料的结果了
|
25
autoxbc 2019-01-30 23:12:17 +08:00 1
个人觉得我的理解最接近本质
记住唯一一条规则:this 是隐参数,在成员访问过程中,如果属性是函数,且不是箭头函数,宿主作为隐参数传递给属性 这里的关键是成员访问,这是个在宿主对象上查找属性的过程,语句反应了这个过程,隐参数就会传递 --------------------- console.log( a() ) 这里 a 是函数,但是查找 a 的过程不是成员访问,所以没有隐参数 --------------------- 既然 this 是隐参数,bind call apply 就是隐参数显式传递,这个是不是非常自然 |
26
OSF2E 2019-01-31 00:47:57 +08:00
|
27
mamahaha 2019-01-31 01:08:20 +08:00 1
这种问题你今天想明白了也许第二天又糊涂了,所以没必要浪费时间钻研,把它看成黑箱就得了
|
28
yxcoder 2019-01-31 09:13:22 +08:00
举个栗子:
let obj = { getThis(){ console.log(this); let func = function(){ console.log(this); } func(); } } obj.getThis(); 两个 console 分别会输出什么? |
30
X37B 2019-01-31 09:38:44 +08:00 1
|
31
libook 2019-01-31 10:52:34 +08:00 1
“每个函数执行时都会创建一个自己的执行环境”的前提是使用 new 指令来执行函数。
function a(){this.n=1;console.log(this.n);} 你直接执行 a()的时候,此时没有一个新的对象创建,this 默认指向全局作用域,就像你直接 var x=1 然后发现 x 变成了 global ( window )的属性,是一样的。 你执行 new a()的时候,依照 new 指令的原理,会创建一个新对象(类似于调用 Object.create(a.prototype)),然后再让 this 指向这个创建出来的对象,此时这个对象就是你所说的“自己的执行环境”。 为了方便理解,我上面说得比较浅显,具体你可以去网上搜一下 new 指令的功能,这个是原型和原型链的思想。 |
32
no1xsyzy 2019-01-31 11:43:34 +08:00
this 是栈帧上的动态作用域!
—— @libook 你可能没明白执行环境是什么意思?另一个名字叫帧( Frame )。 或者是 (eval exp env) 的那个 env。 其实说了这么多,其实就是说 func(...params) := (apply func params (new-env)) obj.func(...params) := (apply func params (assoc (('this obj)) (new-env))) func.call(obj) := (apply func '() (assoc (('this obj)) (new-env))) |
33
ian511 2019-01-31 12:02:19 +08:00
哈哈看到标题就知道在问 js
|
34
wly19960911 2019-01-31 13:45:28 +08:00
|
35
libook 2019-01-31 16:37:29 +08:00
@no1xsyzy
其实我是没听说过“执行环境”这个术语,也不知道应该对应哪个英文名称,所以为了帮助楼主解决问题,只能根据楼主的描述进行推测: 楼主的“那么 a()执行时 this 的值应该是 getName 函数的环境对象”,显然楼主是期望 a 内部的 this 指向的是“ getName 函数的环境对象”即 object 对象,符合楼主预期的结果,输出应该是'object'字符串才对。 所以我的判断是楼主说的“执行环境”就是指的是 this 指向的对象,@fourstring 楼主可以自己解释一下这个“执行环境”究竟是想说的什么。 然后 Stack frame 应该指的是 JS 的 Call Stack 里的原理,我承认这方面我确实不懂,不过一方面我觉得楼主也未必能理解,另一方面楼主的问题在 ES 语法规范上应该就能解决,不至于挖掘到 JS 解释器的实现方式。 |
36
no1xsyzy 2019-02-01 09:52:30 +08:00
@libook 实际上这块的名称英文似乎也很混乱,因为似乎每个语言都会新加一个名称,甚至本身用多个名称…… 也可能是侧重方面的区别。
大概 JS 里叫 Scope ? |
37
libook 2019-02-01 10:46:18 +08:00
@no1xsyzy 参考 MDN 英文的说法,https://developer.mozilla.org/en-US/docs/Glossary/Scope Scope 可能对应的是我们平时说的“作用域”。楼主的问题是 this 的问题,那么 this 是遵循原型链原理的,而原型链和作用域链貌似是 JS 里的两套独立体系。
|
38
no1xsyzy 2019-02-01 11:27:13 +08:00
@libook 一个变量就算是关键字也是有作用域的,而 this 是动态作用域(由调用方决定绑定到什么)而不是词法作用域(闭包)。你知道箭头函数不影响 this 吗?这实际上就是作用域的区别。
所以这就是 JavaScript 中 this 为什么是个大问题,包括 #33 说的一看就知道是在说 JS。 就是因为 JS 大量使用词法作用域以及广泛使用闭包——但却唯有 this 采用动态作用域。更糟糕的是采用的是隐式动态作用域。 Python 的 self (约定上)采用显式动态作用域(作为参数传递)。 C++ 根本没有词法作用域,似乎 lambda 也是手动传入环境的。 |
39
billyangg 2019-03-01 08:57:46 +08:00
在 node 里为啥是 undefined ?
|
40
billyangg 2019-03-01 09:03:15 +08:00
我觉得这个写法跟下面这种差不多:给 a 赋值 this 的指向就很明显了:
```js var name="Window"; var object={ name:"object", getName:function (){ var a= function(){ return this.name; }; console.log(a()); return a; } } var a = object.getName(); a() ``` ![TIM 截图 20190301090238.png]( https://i.loli.net/2019/03/01/5c7884cacf4c3.png) |