js中普通函数和箭头函数this指向

以下是js中常见this指向问题

函数中this指向

复制代码
 var name = '张三'
    function a(){
      var name = '李四';
      console.log(this.name);
    }
    a();//张三

    const b = ()=>{
      var name = '王五';
      console.log(this.name);
    }
    b();//张三
复制代码

两个函数最中输出都是张三,意味着它们的this均指向window。

普通函数是谁调用this指向谁,a()调用明显是被window调用,所以指向window。

箭头函数则是执行上下文是父级的执行上下文,此时b()的父级也是window,而window的上下文也是window,所以依旧指向window。

 

对象中this指向

复制代码
    var name = '张三'
    var obj = {
      name:'李四',
      fn:function(){
        console.log(this.name)
      },
      fn1:()=>{
        console.log(this.name)
      }
    }

    obj.fn();//李四
    obj.fn1();//张三
复制代码

同理

普通函数是谁调用this指向谁,obj.fn()调用是被obj调用,所以指向obj。

箭头函数则是执行上下文是父级的执行上下文,此时obj.fn1()的父级是obj,而obj的上下文也是window,所以依旧指向window。

 

定时器中this指向

复制代码
    var name = '张三';
    function a() { 
      var name = '李四';
      console.log(this.name);
     }

    var b =()=>{ 
      var name = '李四';
      console.log(this.name);
     }

    var obj = {
      name:'李四',
      fn:function(){
        console.log(this.name);
      },
      fn1:()=>{
        console.log(this.name);
      }
    }
    setTimeout(a,1000);//张三
    setTimeout(b,1000);//张三
    setTimeout(obj.fn,1000);//张三
    setTimeout(obj.fn1,1000);//张三
    setTimeout(obj.fn.call(obj),1000);//李四
      setTimeout(obj.fn1.call(obj),1000);//张三
 
复制代码

定时器的调用最终都是window调用,所以this均指向window,加了call绑定this之后输出结果如上,普通函数将this指向obj后,输出结果变成了李四,此时this指向obj,而箭头函数将this通过call指向obj后,因为没有其他函数包裹,this还是指向window。要想this指向

obj,可以进行如下改造

复制代码
 var obj = {
      name:'李四',
      fn:function(){
        console.log(this.name);
      },
      fn1:()=>{
        console.log(this.name);
      },
      fn2:function(){
        return ()=>{
          console.log(this.name);
        }
      }
    }
    const c = obj.fn2()
    setTimeout(c,1000);//李四
复制代码

将箭头函数作为返回值,此时箭头函数中的this指向即是外层普通函数的this指向。

 

事件绑定的this指向

1. 在 element 上绑定事件

此时 this 均指向 window

复制代码
  <button onclick="a()">点击</button>
  <button onclick="b()">点击</button>
  <script>function a() { 
      console.log(this);//window
     }

    var b =()=>{ 
      console.log(this);//window
     }
  </script>
复制代码
2. js 使用 addEventListener 绑定事件

此时的普通函数 this 指向该元素,箭头函数this指向window

复制代码
<button id="btn1">点击</button>
  <button id="btn2">点击</button>
  <script>
    function a() {
      console.log(this);//<button id="btn1">点击</button>
     }

    var b =()=>{ 
      console.log(this);//window
     }
     document.getElementById('btn1').addEventListener('click',a);
     document.getElementById('btn2').addEventListener('click',b);
复制代码

 

区别

借用MDN的话:

箭头函数表达式的语法比函数表达式更简洁,并且没有自己的thisargumentssupernew.target等,也不能用作构造函数。
箭头函数表达式更适用于那些本来需要匿名函数的地方。

 

执行上下文

也称执行环境,当JavaScript解释器初始化执行代码时,它首先默认进入全局执行环境;从此刻开始,函数的每次调用都会创建一个新的执行环境;每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中(execution stack);在函数执行完后栈将其环境弹出,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出也随之销毁,把控制权返回给之前的执行环境;ECMAScript程序中的执行流正是由这个便利的机制控制着;执行环境可以分为创建和执行两个阶段;在创建阶段,解析器首先会创建一个变量对象(variable object,也称为活动对象activation object),它由定义在执行环境中的变量/函数声明/参数组成;在这个阶段,作用域链会被初始化,this的值也会被最终确定;在执行阶段,代码被解释执行

function Fn1(){
    function Fn2(){
        alert(document.body.tagName);//BODY
        //other code...
    }
    Fn2();
}
Fn1();

执行环境栈:

 

 

活动对象(activation object)

如果所在环境是函数,那么就会把这个函数的活动对象作为变量对象(在函数中,变量对象==活动对象)。它一开始只包含arguments对象。一般而言,函数执行过程,可以分成两步:

1.进入执行环境;2.执行代码。

比如,下面这个test函数:

复制代码
function test(a, b) {
   var c = 10;   
   function d() {}
   var e = function _e() {};
   (function f() {});
   g = 10;
}

test(10);
复制代码

在执行test(10)的时候,分成了两步:

1. 进入执行环境。此时,会用arguments对象初始化活动对象(AO, activation object)。并且,会把形参、var声明的变量和函数声明放入活动对象AO中。

复制代码
AO: {
arguments: {
    callee: test,
    length: 1,
    0: 10
},
a: 10,
b: undefined,
c: undefined,
d: <reference to FunctionDeclaration "d">,
e: undefined
}
复制代码

2. 执行代码。AO会变成:

复制代码
AO: {
arguments: {
    callee: test,
    length: 1,
    0: 10
},
a: 10,
b: undefined,
c: 10,
d: <reference to FunctionDeclaration "d">,
e: <reference to FunctionDeclaration "_e">
}
复制代码

作用域链

作用域链与一个执行上下文相关,是内部上下文所有变量对象(包括父变量对象)的列表,用于变量查询。

作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。

全局执行环境的变量对象始终都是作用域链中的最后一个对象。

posted @   lijun12138  阅读(294)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示