请教一个关于 this 的问题

2019-01-30 18:34:13 +08:00
 fourstring

代码如下:

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。请问我的理解有什么问题呢?谢谢:)

4670 次点击
所在节点    JavaScript
40 条回复
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);
rabbbit
2019-01-30 22:02:26 +08:00
@OSF2E

能深入讲讲"匿名函数 a 在定义之初便自动“挂靠”到全局执行环境中的"这句吗?
没看懂意思,有哪本书提到过吗?
rabbbit
2019-01-30 22:09:09 +08:00
如果指的是第 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
palmers
2019-01-30 22:35:42 +08:00
this 是动态的指向当前对象 并不会主动的缓存 所以 是 window 在 getName 函数中将 this 缓存起来就会得到你预料的结果了
autoxbc
2019-01-30 23:12:17 +08:00
个人觉得我的理解最接近本质

记住唯一一条规则:this 是隐参数,在成员访问过程中,如果属性是函数,且不是箭头函数,宿主作为隐参数传递给属性

这里的关键是成员访问,这是个在宿主对象上查找属性的过程,语句反应了这个过程,隐参数就会传递

---------------------
console.log( a() )
这里 a 是函数,但是查找 a 的过程不是成员访问,所以没有隐参数

---------------------
既然 this 是隐参数,bind call apply 就是隐参数显式传递,这个是不是非常自然
OSF2E
2019-01-31 00:47:57 +08:00
@rabbbit @fourstring

这句是纯个人理解,没有原文可以引用,我自己也觉得表达的不够准确。
mamahaha
2019-01-31 01:08:20 +08:00
这种问题你今天想明白了也许第二天又糊涂了,所以没必要浪费时间钻研,把它看成黑箱就得了
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 分别会输出什么?
yxcoder
2019-01-31 09:14:33 +08:00
@yxcoder 这代码格式。。。哈哈
X37B
2019-01-31 09:38:44 +08:00
@fourstring https://github.com/getify/You-Dont-Know-JS
<你不知道的 js>,国内有译本,分上中下,不过中册翻译的不好
libook
2019-01-31 10:52:34 +08:00
“每个函数执行时都会创建一个自己的执行环境”的前提是使用 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 指令的功能,这个是原型和原型链的思想。
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)))
ian511
2019-01-31 12:02:19 +08:00
哈哈看到标题就知道在问 js
wly19960911
2019-01-31 13:45:28 +08:00
/t/519845

自己看吧
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 解释器的实现方式。
no1xsyzy
2019-02-01 09:52:30 +08:00
@libook 实际上这块的名称英文似乎也很混乱,因为似乎每个语言都会新加一个名称,甚至本身用多个名称…… 也可能是侧重方面的区别。
大概 JS 里叫 Scope ?
libook
2019-02-01 10:46:18 +08:00
@no1xsyzy 参考 MDN 英文的说法,https://developer.mozilla.org/en-US/docs/Glossary/Scope Scope 可能对应的是我们平时说的“作用域”。楼主的问题是 this 的问题,那么 this 是遵循原型链原理的,而原型链和作用域链貌似是 JS 里的两套独立体系。
no1xsyzy
2019-02-01 11:27:13 +08:00
@libook 一个变量就算是关键字也是有作用域的,而 this 是动态作用域(由调用方决定绑定到什么)而不是词法作用域(闭包)。你知道箭头函数不影响 this 吗?这实际上就是作用域的区别。
所以这就是 JavaScript 中 this 为什么是个大问题,包括 #33 说的一看就知道是在说 JS。
就是因为 JS 大量使用词法作用域以及广泛使用闭包——但却唯有 this 采用动态作用域。更糟糕的是采用的是隐式动态作用域。
Python 的 self (约定上)采用显式动态作用域(作为参数传递)。
C++ 根本没有词法作用域,似乎 lambda 也是手动传入环境的。
billyangg
2019-03-01 08:57:46 +08:00
在 node 里为啥是 undefined ?
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)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/531950

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX