3

理解this作用域

 1 year ago
source link: https://jelly.jd.com/article/624edeaa3e1c4f01b1a995e0
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
深入理解this作用域问题
上传日期:2022.08.30
this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window ,而当函数被作为某个对象调用时,this等于那个对象。不过,匿名函数具有全局性,因此this对象同常指向window

理解this作用域

《javascript高级程序设计》中有说到:

this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window ,而当函数被作为某个对象调用时,this等于那个对象。不过,匿名函数具有全局性,因此this对象同常指向window

不过,在全局函数中,this等于window,匿名函数具有全局性,因此this对象通常指向window,针对于匿名函数this具有全局性的观点仍是有争议的,可参考 https://www.zhihu.com/question/21958425

this的指向取决于函数(不包含箭头函数)执行时的环境

验证过程如下:

关于闭包经常会看到这么一道题:

var name = "The Window";
    var object = {
        name : "My Object",
        getNameFunc : function(){
            return function(){
                return this.name;
            };
        }
    };
console.log(object.getNameFunc()());//result:The Window
11d36b920f7b79f2.png
在这里,getNameFunc return了1个匿名函数,可能你会认为这就是输出值为The Window的原因

但是,我们再来尝试写1个匿名函数

var name = "The Window";
 var object = {
  name : "My Object",
  getNameFunc : function(){
   return this.funAA;
  },
  funAA:function(){
   return this.name
  }
 };
 console.log(object.getNameFunc()(),object.funAA())
11d36b920f7b79f2.png
可以发现,同样是匿名函数,却输出了The Window, My Object

在作用域链中,执行函数时会创建一个称为“运行期上下文(execution context)”的内部对象,运行期上下文定义了函数执行时的环境。

因为函数在全局作用域中被object.getNameFunc()独立调用,funAA的作用域链被初始化为undefined即window的[[Scope]]所包含的对象,导致输出结果为window.name

对作用域链不是很了解的同学,可以查看这边文章 【Javascript】深入理解javascript作用域与作用域链

实践是检验真理的唯一标准,让我们用代码测试一下

var name = "The Window";
 var object = {
  name : "My Object",
  getNameFunc : function(){
   return this.funAA();
  },
  funAA:function(){
   return this.name
  }
 };
console.log(object.getNameFunc(),object.funAA())
11d36b920f7b79f2.png
可以发现,输出了 My Object, My Object getNameFunc仍为匿名函数,但是return的是this.funAA(),此时,this.funAA变成了由object调用,验证了我们之前的猜想:

函数执行环境影响了this作用域, 对这个demo的代码不太理解的同学,可以看一下另一个比较简单的案例

this.x = 9;   
var module = {
  x: 81,
  getX: function() { console.log(this.x) }
};

module.getX(); // 81

var retrieveX = module.getX; function A(){ this.x = 22; retrieveX() //22 } A()

### new运算符对this作用域的影响
还是实践出真理,我们先来写一段代码

var a = 2 function test(){ this.a = 1 console.log(window.a) } new test() test()

![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/4/30/16a69f64c63e82e3~tplv-t2oaga2asx-image.image)
可以看出输出结果为`2,1` 
new运算符改变了test函数内this的作用域,改变的原理是通过在函数内创建一个对象obj,并通过`test.call(obj)`,执行`obj.test()`,call函数原理:

Function.prototype.call1 = function(obj,...args){ obj.fn = this obj.fn(...args) delete obj.fn }

这样test函数被对象obj调用,test复制的是obj的作用域链,而不是window

function subNew(){ var obj = {} var res = test.call(obj,...arguments) } subNew() // 作用等于new test()

### let/var/const对this作用域的影响
继续写代码通过事实来说明

var a = 1 // 全局作用域 let b = 1 // 块级作用域 const c = 1 // 块级作用域 function foo(){ var d = 1 // 函数作用域 this.a = 2 this.b = 2 this.c = 2 this.d = 2 console.log(a,b,c,d) // 2,1,1,1 } foo()

>a为全局作用域中的变量,可以被this对象访问,b/c/d则不行

可以发现,全局作用域中的a变量被改变,b变量与c变量都没有被改变,说明在fn()中通过this访问不到window作用域中的b/c变量  
<font color="red">注:这里说的访问不到与const定义的变量是常量没有关系,因为如果访问到的话,是会报typeError的</font>

### 箭头函数对this作用域的影响

var num = 1 const object = { num:2, foo: function(){ return ()=>{ console.log(this.num) } } } object.foo() // 2

箭头函数 this 指向 所处环境的上下文的 this 值,与是否独立调用或作为属性被调用,没有关系。
箭头函数没有`arguments`/`prototype`,不能作为构造函数,不能使用`new`

### 总结
1.  this的指向取决于`函数执行时`所创建`运行期上下文(execution context)`的内部对象,它与当前运行函数的[[scope]]所包含的对象组成了1个新的对象,这个对象就是`活动对象`,然后此对象会被推入作用域链的前端  
2.  如果调用的函数,`被某一个对象所拥有`,那么该函数在调用时,内部的this指向该对象。  
3.  this指向与匿名函数没有关系,如果函数在`全局作用域window`中被`独立调用`,那么该函数内部的this,则指向undefined。但是在非严格模式中,当this指向undefined时,它会被自动指向全局对象。  
4.  在函数被独立调用时,并处于`非严格模式`下,函数内的this对象有能力也仅能访问到全局作用域中定义的变量即window对象, 块级作用域/函数作用域内的变量都无法被访问
5. 箭头函数 this 指向 `所处环境的上下文的 this 值`,与是否独立调用或作为属性被调用,没有关系。



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK