Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

由一个简单的问题重新思考this的引用问题 #19

Open
shiiiiiiji opened this issue May 25, 2018 · 7 comments
Open

由一个简单的问题重新思考this的引用问题 #19

shiiiiiiji opened this issue May 25, 2018 · 7 comments

Comments

@shiiiiiiji
Copy link
Owner

shiiiiiiji commented May 25, 2018

刚才,群里有一位同学提出这样一个问题:

var x = 1;
var obj = {
	x: 2,
	y: function(){
		console.log(this.x);
	}
}
setTimeout(obj.y, 1000);

这个问题其实是考查的是js的异步执行的问题,不深入的去解释的话,js高程(p203)对超时调用的函数特别强调:

超时调用的代码都是在全局作用域中执行的,因此函数中this的值在非严格模式下指向window对象,在严格模式下指向undefined
image

因此,就认定this指向的是window,因此结果是1,当然正确结果也是1。

这里obj.y不是调用关系,而是赋值,传递给超时函数作为形参。

最开始我却认为这和异步执行没关系,即使直接不用超时函数,obj.y();,这个结果应该也是1(最简单的js代码,啪啪打脸),而且有理有据,obj.y仅仅只是一个函数的名称而已,而且在全局作用域下执行的,因此结果应当是1,甚至群里许多人都被我带偏了,认为我说的对。

但是其实不然,我也不知道为什么我这个解释为什么错了(哈哈哈),obj.y();这个非常常规的对象函数调用结果是2。

首先,函数执行的this值,是指向函数据以执行的环境对象(js高程p114),但是如何确定是哪个环境对象呢?

由此,进一步我提出了下面问题,怎么解释下面4段代码:

(function(){return obj.y})()();  // 1
var c = obj.y; c(); // 1
(obj.y)();  // 2
(function(){return this.x})();  // 1
@shiiiiiiji
Copy link
Owner Author

(function(){return obj.y})()();  // 1

obj.y作为函数的返回值返回(第一个括号),再调用(第二个括号),obj.y函数作为函数的返回值。

var c = obj.y; c(); // 1

将函数作为值赋值给变量c,再执行。

(obj.y)();  // 2
(function(){return this.x})();  // 1

这个主要是和上面对比,但是不知道前者结果为何是2,后者好理解。

@shiiiiiiji
Copy link
Owner Author

最终的问题是:

obj.y()和(obj.y)()执行结果为什么是一样的?

@Chenjiayuan195
Copy link

Chenjiayuan195 commented May 26, 2018

在作业上看到,提供一下自己的看法,不知道能不能帮到您。
1.匿名函数的执行环境是在window中,所以

(function(){return obj.y})()();  // 1
(function(){return this.x})();  // 1

以上就很好理解了
2.您的疑问是为何obj.y()和(obj.y)()执行结果为何一致

(obj.y)()=(function(){  //这里的等于是为了方便看函数执行的过程发生了什么
     //本来的环境应该是window的,但是this的指向当前调用的时候的执行环境,由于是obj调用,所以指向了obj本身
        console.log(this.x)
        })()

以上可以看到后面的可以看成一个函数表达式,解析器解析的时候会知道这是一个函数表达式的执行。
以下的写法可能就比较清楚了

(obj.y)()  =>(obj.y())

如有错误,烦请指正

@shiiiiiiji
Copy link
Owner Author

shiiiiiiji commented May 26, 2018

@Chenjiayuan195
1、关于第一段代码,我认为这么解释可能更好:
拿前半段来看,(function(){return obj.y})(),这里是一个匿名函数的执行是没问题的,这个匿名函数将obj.y以返回,这就是一种“赋值”操作,而obj并没有调用其中的y方法,而是通过obj“查询”到y函数并返回,而再执行时,已经是在全局环境里执行的,可以通过下面代码来佐证我的想法:
(代码执行结果不符合上述解释)

var x = 1;
var obj = {
	x: 2,
	y: function(){
		console.log(this.x);
	}
}

var obj2 = {
  x: 3,
  z: function(){
    console.log(this.x);
    console.log((function(){return obj.y})()());
  }
}
obj2.z(); // 3 1

后者如果依照我的解释,那么匿名函数执行完后,返回的那个函数所处的执行环境应当是obj2,但是结果依然是window。这也就说明了你解释的正确性。

总结说来,其实(一个函数)()这种形式无论前者嵌套了多少层,函数中的this一定是window

2、第二段代码的解释我还是有点疑惑的:
其实第二点解释我都很认同,唯独“由于是obj调用,所以指向了obj本身”这句话我存在异议,这里为什么说是obj调用。

我理解的是(obj.y)是一个表达式,输出结果是一个函数,然后再将其执行,这样既然在全局环境下执行,就应当指向的是全局环境(后来,我把这段代码也写在了obj2的z函数中,结果依然显示的是obj的x值,所以我这种想法其实是错误的),但是我不知道我错在了哪里?


关于这个问题,我在js高程p183看到了同样的问题:
image

@Chenjiayuan195
Copy link

Chenjiayuan195 commented May 27, 2018

是的,有一句话有助于理解这个现象,this的指向取决于执行的时候谁调用它。
具体的解释可以这么理解:

(obj.y)()

obj.y取的是function的地址,而且解析器解析(obj.y)是一个函数表达式,且立即执行了这个函数,此时函数的执行环境就是obj的环境,再结合上面的一句话this的指向取决于执行的时候谁调用它,很显然是obj调用了它并且立即执行,所以就是指向了obj的变量。
高程的第三个:

(object.name=object.name)()   =>  (function(){return this.name})()  

显而易见了,这首先是一个赋值的操作,此时的object.name赋给它一个function,而不是它去调用,所以指向window, 而上面第二个是去直接调用,所以指向自身,希望能够解释清楚。

@Chenjiayuan195
Copy link

我也写了一篇相关的文章 http://chenjy195.sc2yun.com/2018/05/26/ 望赏脸 =。=

@shiiiiiiji
Copy link
Owner Author

@Chenjiayuan195
嗯嗯,写的很深入,赞👍

@shiiiiiiji shiiiiiiji removed the NOTE label Oct 31, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants