技术解析

请教一个关于 this 的问题
0
2021-09-01 00:25:15
idczone

代码如下:

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


https://www.v2ex.com/t/529532 看看吧,另外贴代码的时候能不能带有格式啊,看着真糟心!

就你这段代码而言,a 并不属于任何对象,也没有通过 call 等方法调用,所有这样的函数 this 默认指向 Window,如果是严格默认下,就是 undefinde。

收回我说的第一句话,才发现我的文章代码也没有格式,为什么 V2EX 会这样??

其实我这边手机看着两个帖子里的代码都有格式…

因为我用的是 v2er 的客户端,才发现这个问题,现在已卸载。

想问一下您手机端用的啥客户端?

chrome

使用起来体验没有客户端好,虽然不会有显示问题

最新的 beta 版已经实现了代码格式化功能:
https://imgur.com/a/0uF0P3M

求下载链接,感激不尽!

已在 play 商店搜到

this 是什么取决于如何调用
本文中 a 函数的调用者是 a() 而非外层的 object.getName()
当 a 函数被作为变量调用时, 会将内部方法 GetBase(ref). ImplicitThisValue 返回的值作为 thisArg(通常是 undefined)
http://ecma-international.org/ecma-262/5.1//>1 如果是严格模式,把 this 指向 thisArg
2 如果 thisArg 为 undefined,则将 this 指向 window
http://ecma-international.org/ecma-262/5.1/
总之一句话,作为变量调用的函数里边的 this 都指向 window

《 Javascript 高级程序设计(第三版)》( P73 ) 4.2 执行环境与作用域

另外补充下
ImplicitThisValue 通常返回 undefined, with 除外


谢谢,我看的就是这本书。我之所以会认为 a()的结果是"object",就是因为这本书 5.5.4 里说 this “引用的是函数执行的环境对象”,然后 4.2 节说“每个函数都有自己的执行环境”,我看了下面别的 v 友回复以后,觉得应该是我对“执行环境”理解有误,但是这本书里确实没有明确讲解函数的执行环境究竟是什么(也就是不是词法环境)。

我总结的就是相关性就近原则,菜鸟飞过。。。

this(4 个规则):函数位置上的调用形式
默认绑定
独立调用 fn()
this -->window "use strict" this --> undefined
隐式绑定
对象.对象.fn()
this -->最近的调用者
隐式丢失?
使用隐式调用的形式进行传参 赋值;使用其他形式进行调用 this 的指向发生改变
解决隐式丢失?
硬绑定 bind 返回一个新的函数
显示绑定
apply
call
立即调用
this --> 绑定的对象
构造绑定
new fn()
this --> 构造出来的实例对象

当一行代码出现多个绑定,this 绑定优先级:new --> 显示 --> 隐式 --> 默认

其实很简单,你的 this 在哪个函数里面?在 a 里面,那么 this 的值只取决于 a 被谁调用,跟其它什么 getName 没有任何关系。
而所谓的 a 被谁调用,指的是“调用者.a()”这种调用,不是说 a()写在 getName 里面就是被 getName 调用了这种意思。

请问知道这样的知识什么地方或者书能看到呢

犀牛书应该是这样理解语言设计者的意图的:词法作用域和 this 都属于函数运行环境 /上下文,现在的实践中,一般词法作用域是写代码时固定的,this 允许用户在运行时动态指定上下文,并且这东西和函数调用语法关联,因此也是一种“环境”
实际上现在的 OO 语言对于 this 的实现基本就是函数参数,也就是非要说的话所有参数都是“环境”的一部分 ... 不过 OO 的语义上 this 确实有环境的意思
词法作用域环境也可以动态绑定,用 eval 或者 with 就行,只不过现在没人用了


首先,这部分内容要结合第 7 章的内容来理解。
另外,这一节有一个关键概念是执行环境的变量对象( variable object ),每一个函数的执行环境( execution context )中所定义的变量和函数都保存在这个对象中。而 web 浏览器中最外层的执行环境(的变量对象)被认为是 window 对象,因此所有全局变量和函数都是作为 window 对象的属性和方法创建的。
因此,你的代码中的匿名函数 a 在定义之初便自动“挂靠”到全局执行环境中的(解析器就是这么解析的,没有为什么),即该匿名函数是 window 对象的方法,所以 this 自然指向 window。
可以使用 bind 方法改变该匿名函数“挂靠”的变量对象,代码如下:
var a = function(){}.bind(this);


能深入讲讲"匿名函数 a 在定义之初便自动“挂靠”到全局执行环境中的"这句吗?
没看懂意思,有哪本书提到过吗?

如果指的是第 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

this 是动态的指向当前对象 并不会主动的缓存 所以 是 window 在 getName 函数中将 this 缓存起来就会得到你预料的结果了

个人觉得我的理解最接近本质
记住唯一一条规则:this 是隐参数,在成员访问过程中,如果属性是函数,且不是箭头函数,宿主作为隐参数传递给属性
这里的关键是成员访问,这是个在宿主对象上查找属性的过程,语句反应了这个过程,隐参数就会传递
---------------------
console.log( a() )
这里 a 是函数,但是查找 a 的过程不是成员访问,所以没有隐参数
---------------------
既然 this 是隐参数,bind call apply 就是隐参数显式传递,这个是不是非常自然


这句是纯个人理解,没有原文可以引用,我自己也觉得表达的不够准确。

这种问题你今天想明白了也许第二天又糊涂了,所以没必要浪费时间钻研,把它看成黑箱就得了

举个栗子:
let obj = {
getThis(){
console.log(this);
let func = function(){
console.log(this);
}
func();
}
}
obj.getThis();
两个 console 分别会输出什么?

这代码格式。。。哈哈

https://github.com/getify/You-Dont-Know-JS
<你不知道的 js>,国内有译本,分上中下,不过中册翻译的不好

“每个函数执行时都会创建一个自己的执行环境”的前提是使用 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 指令的功能,这个是原型和原型链的思想。

this 是栈帧上的动态作用域!
——
你可能没明白执行环境是什么意思?另一个名字叫帧( 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)))

哈哈看到标题就知道在问 js

/t/519845
自己看吧


其实我是没听说过“执行环境”这个术语,也不知道应该对应哪个英文名称,所以为了帮助楼主解决问题,只能根据楼主的描述进行推测:
楼主的“那么 a()执行时 this 的值应该是 getName 函数的环境对象”,显然楼主是期望 a 内部的 this 指向的是“ getName 函数的环境对象”即 object 对象,符合楼主预期的结果,输出应该是'object'字符串才对。
所以我的判断是楼主说的“执行环境”就是指的是 this 指向的对象,@fourstring 楼主可以自己解释一下这个“执行环境”究竟是想说的什么。
然后 Stack frame 应该指的是 JS 的 Call Stack 里的原理,我承认这方面我确实不懂,不过一方面我觉得楼主也未必能理解,另一方面楼主的问题在 ES 语法规范上应该就能解决,不至于挖掘到 JS 解释器的实现方式。

实际上这块的名称英文似乎也很混乱,因为似乎每个语言都会新加一个名称,甚至本身用多个名称…… 也可能是侧重方面的区别。
大概 JS 里叫 Scope ?

参考 MDN 英文的说法,https://developer.mozilla.org/en-US/docs/Glossary/Scope Scope 可能对应的是我们平时说的“作用域”。楼主的问题是 this 的问题,那么 this 是遵循原型链原理的,而原型链和作用域链貌似是 JS 里的两套独立体系。

一个变量就算是关键字也是有作用域的,而 this 是动态作用域(由调用方决定绑定到什么)而不是词法作用域(闭包)。你知道箭头函数不影响 this 吗?这实际上就是作用域的区别。
所以这就是 JavaScript 中 this 为什么是个大问题,包括 说的一看就知道是在说 JS。
就是因为 JS 大量使用词法作用域以及广泛使用闭包——但却唯有 this 采用动态作用域。更糟糕的是采用的是隐式动态作用域。
Python 的 self (约定上)采用显式动态作用域(作为参数传递)。
C++ 根本没有词法作用域,似乎 lambda 也是手动传入环境的。

在 node 里为啥是 undefined ?

我觉得这个写法跟下面这种差不多:给 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)

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服