JavaScript基础二:理解执行栈与This指向

字节寻幽鹤
• 阅读 1217
哪里调用this就指向哪里。this指向依赖于调用时的位置和调用方法,而非创建时的位置。

全局上下文

默认情况下,全局上下文环境的this指向于window对象。

 var a = 1;
 console.log(this.a);     // 1 
 console.log(this == window);   // true

值得注意的是,在ES6中let关键字声明定义的全局变量,不挂在顶层对象window中

 let a = 1;
 console.log(this.a);   // undefined
 console.log(this == window);  // true

函数上下文

在分析函数上下文之前,我们先来了解下调用位置。JavaScript引擎在执行代码时按照顺序执行,则全局上下文(global text)应当首先进入调用栈。之后才是函数上下文进栈,当函数被调用时则进行出栈。

举个例子

  function foo(){
    foo2();
  }
  function foo2(){
    foo3();
  }
  function foo3(){
    console.log("i am foo3");
  }
  foo();

如上这段代码,最开始是全局上下文首先入栈,而后foo入栈,看到foo中还调用foo2则继续进栈,同理入栈foo3。根据栈后进先出的原则,则foo3首先被弹出栈,而后foo2、foo依次出栈。该函数上下文所在栈元素的前一个元素,则为调用位置。而实际上,需要结合调用方式来判定。

var a  = 1;
function foo(){
  var a = 2;
  foo2();
}
function foo2(){
  var a = 3;  
  console.log(this.a);    // 1
  foo3();
}
function foo3(){
  console.log(this.a);  // 1
  console.log("i am foo3");
}
foo();

此处为函数独立调用,所以this实际指向于window

普通函数调用

在非严格模式下,普通函数的this指向于window

  var a = 1;
  function foo(){
   console.log(this.a);
  }

而在严格模式下,普通函数的this是undefined

'use strict'
 var a  = 1;
 function foo(){
  console.log(this);  // undefined
  console.log(this.a);  // 报错
 }
 foo();

嵌套函数调用

  var a  = 1;
  function foo(){
   return function(){
    var a = 2;
    console.log(this.a);
   }
  }
  foo()();   // 1
  
  var f = foo(); 
  f();   // 1

对象中的函数调用

在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用者函数,被某一个对象所拥有,那么该函数在调用时,内部的this指向该对象。

  var a = 1;
  function getA(){
    return this.a;
  }
  var obj = {
    a:2,
    foo:getA  
  }
  console.log(obj.foo());  // 2

如果函数独立调用,那么该函数内部的this,则指向undefined。但是在非严格模式中,当this指向undefined时,它会被自动指向全局对象。

 var a  = 1;
 function getA(){
  return this.a;
 }
 var obj  = {
   a:2,
   foo:getA
 };
 var f = obj.foo;
 console.log(f());   // 1

需要注意的是,这仅是针对函数上下文而言。

 var a  = 1;
 function getA(){
  return this.a;
 }
 var obj = {
  a:2,
  b:this.a,
  foo:function(){
    return this.a;
  }
 };
 console.log(obj.b);  // 1
 console.log(obj.foo());  // 2

call/apply调用

使用call/apply调用时,this作用域指向call/apply的第一个参数对象。call/apply本身差别不大,主要差别就是call传递的形参是一个一个的,而apply是一整个数组传。

 var song = 'hello';
 function sing(){
   console.log(this.song);
 }
 var obj  = {
   song:'let it go'
 };
 sing.call(obj);  // let it go

构造函数调用

this指向被创建的对象

 var age = 13;
 function Animal(age){
   this.age = age;
 }  
 var dog = new Animal(12);
 console.log(dog.age);   //12

DOM事件处理函数调用

this指向于触发事件的DOM元素

var ele = document.getElementById("id");
ele.addEventListener("click",function(e){
 console.log(this);
 console.log(this === e.target); // true
})

箭头函数调用

箭头函数调用应当是this指向在函数调用里面的特例了,在箭头函数中,会捕获其所在上下文的this值,作为自己的this值。简单来说,就是包裹箭头函数的第一个普通函数中的this。

 function foo() {  
  setTimeout(()=>{
   console.log(this.a);
  },100)
}
 var obj = {
 a: 2
}
 foo.call(obj);   // 2

总结

确定This指向时,首先找到函数调用位置及调用方式

  1. new调用:绑定到新创建的对象
  2. callapplybind调用:绑定到指定的对象
  3. 由上下文对象调用:绑定到上下文对象
  4. 默认:全局对象
参考文章:https://github.com/axuebin/ar...
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java 复制Map对象(深拷贝与浅拷贝)
java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象
巴拉米 巴拉米
4年前
bind、call、apply 区别?如何实现一个bind?
一、作用call、apply、bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向那么什么情况下需要改变this的指向呢?下面举个例子var name"lucy";const obj{    name:"martin",    say:function (){        co
Python进阶者 Python进阶者
3年前
手把手教会你JavaScript引擎如何执行JavaScript代码
JavaScript在运行过程中与其他语言有所不一样,如果不理解JavaScript的词法环境、执行上下文等内容,很容易会在开发过程中产生Bug,比如this指向和预期不一致、某个变量不知道为什么被改了,等等。所以今天我们就来聊一聊JavaScript代码的运行过程。大家都知道,JavaScript代码是需要在JavaScript引擎中运行
九路 九路
4年前
【干货】Javascript千面之变幻莫测的this指向
相信很多前端人对“this”的指向是很懵逼的,因为this的指向总是变幻莫测,在不同的调用环境中,它的指向总是各不相同。在面试中,this也是经常考的必考题之一,很多前端老鸟经常会在this这里掉坑。接下来,看笔者来一层一层的揭开this指向的面纱。1.事件调用环境中的this指向<divclass"b
菜园前端 菜园前端
2年前
JS难点:this 指向
原文链接:this指向分为两种情况,一种是普通函数中使用的this,另外一种是箭头函数中的this。普通函数this指向调用者。场景1javascriptfunctionsayHi()console.log(this)sayHi()//window这里的t
御弟哥哥 御弟哥哥
4年前
彻底理解js的执行上下文,以及变量对象
在js中,执行上下文(ExecutionContext)是非常重要的一种对象,它保存着函数执行所需的重要信息,其中有三个属性:变量对象(variableobject),作用域链(scopechain),this指针(thisvalue),它们影响着变量的解析,变量作用域和函数this的指向。上下文栈(ExecutionContextS
待兔 待兔
4年前
Java多态实现原理
Java多态概述多态是面向对象编程语言的重要特性,它允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。Java对于方法调用动态绑定的实现主要依赖于方法表,但通过类引用调用(invokevirtual)和接口引用调用(invokeinterface)的实现则有所不同。类引用调用的大致过程为:Java编译器将Java源代码编译成c
Wesley13 Wesley13
3年前
Java 多态
类型的检测——向上转型向下转型向上转型:父类对象的引用指向子类对象,向下转型:向上转型的基础上再次指向子类的对象1.向上转型!(https://oscimg.oschina.net/oscnet/dd0d05d39a724e781b799ff5e35b921775d.jpg)!(https://oscimg.oschina.net/o
Stella981 Stella981
3年前
JS 中的this指向问题和call、apply、bind的区别
this的指向问题一般情况下this对象指向调用函数的对象,全局环境中执行函数this对象指向window。functiona(){console.log(this);//输出函数a中的this对象}functionb(){};varc{name:"call"}
Wesley13 Wesley13
3年前
ES6 箭头函数
一、在es6中函数的定义和es5之间有明显区别。不需要关键字function来进行定义,使用来指向函数。不可以new也就是做构造函数以及没有arguments参数。箭头函数的this是在定义的时候确定指向这和es5不一样,es5是谁调用他,他就指向谁。1document.addEventListene
Stella981 Stella981
3年前
JavaScript中this指向问题,暴力理解终极方法
前言:前端面试题总有问this是谁?对于java程序员来说,this很好理解,就是当前对象本身。对于js来说,this就是传说中的当前运行环境,其实理论知识一大把,但是很多程序员看到有些写法还是会懵逼,比如dom.onclick事件里调用事件里自己的方法,为啥指向的是window对象?先看看下面的代码:varobj{ bb: