重学前端--执行上下文

执行上下文

执行上下文时代码执行时的环境

类型

全局执行上下文 函数执行上下文 eval函数指向上下文

执行栈

当代码第一次执行时会创建一个全局执行环境压入执行栈的栈底,在遇到函数调用时,会把该函数创建的执行栈压入栈顶。当函数执行结束后用,这个执行栈会从栈顶弹出,把控制权交给下一个执行上下文

作用域

词法作用域 :j s的作用域是词法作用域 (静态作用域),是指函数的作用域是在函数定义时就确定好的。
定义:规定如何查找变量,变量在那个范围有用
分类:1.全局作用域 在执行代码最外层 2.函数作用域 在函数里面 3.块级作用域 let const

function a {
  
}
// a的作用域 
a[[scope]] = {
   globalContext.VO // 全局变量
}
var let const
  1. var声明提交 声明的变量会被提到当前作用域的最前端,可以先使用后声明;可重复声明,重复声明就会忽略;不存在块级作用域,在函数内重复赋值会覆盖;函数也存在变量提示,函数声明提升优先于变量声明提升,函数声明会覆盖变量声明
  2. let const 存在暂时性死区,要先声明后使用;而且不能重复声明;存在块级作用域,只能在最近的代码块{}使用
  3. const 常量 不能修改值的引用

es3创建执行上下文

创建变量对象

变量对象是引擎实现的不可在js中访,当进入到执行上下文中,这个执行上下文的变量对象会变成活动对象 。变量对象包括:函数的形参,函数声明,变量声明

创建作用域链

父级作用域和自身作用域形成的链条,在进入函数时,会把活动对象添加到作用域链的最前端,可以一级一级向上访问查找变量,作用域的最底端是全局作用域,最前端是活动对象。 例如在函数a内部创建函数b 函数b的作用域链包含函数a的作用域,所以函数b可以访问函数a的变量

function a {
  function b {}
}

// a的作用域
a[[scope]] = {
   globalContext.VO
}
// b的作用域
b[[scope]] = {
  aContext.AO  // 活动对象
  globalContext.VO
}

// 作用域链
Scope [AO].concat([[Scope]])
确定this执行

取决于函数的调用方式

实例
var scope = "global scope";
function checkscope(){
    var scope2 = 'local scope';
    return scope2;
}
checkscope();

1. 执行全局代码 创建执行栈 全局变量压入栈底
ECStack = [globalContext]

2. 全局上下初始化 包括全局变量 作用域链仅有全局作用域 this指向全局作用域
globalContext = {
  VO: [global,scope,checkscope],
  Scope:[globalContext.VO],
  this: globalContext.vo
}

2. 函数创建 创建checkscope的作用域链scope
checkscope.[[scope]] = [globalContext.VO]

3. 执行checkscope 函数,创建checkscope的执行上下文,压入执行栈
ECStack = [checkscopeContext,globalContext]

4. 进入checkscope checkscope的执行上下文初始化
checkscopeContext = {
  AO: {arguments: {length:0},scope:undefined},
  Scope:[AO,globalContext.VO],
  this:undefined
}

5.执行checkscope 变量赋值
checkscopeContext = {
  AO: {arguments: {length:0},scope2:'local scope'},
  Scope:[AO,globalContext.VO],
  this:undefined
}

6. 函数执行完毕 checkscope从执行栈弹出
ECStack = [globalContext]

es5 创建执行上下文

this 绑定

This 是在运行时绑定的,取决于函数的调用方式而不是编写方式

**默认绑定 **

  • 独立函数调用 this 指向全局对象

    function func () {
       var b = 1
      console.log(this.b) 
    }
    var b = 2;
    
    func() // 输出2 this指向全局变量
    

隐式绑定

  • 函数作为对象的属性时 被对象直接调用 指向调用的最近对象对象

    function func () {
       var b = 1
       console.log(this.b)  
    }
    var b = 2;
    var a = {
        b:3,
        func:func
    }
    a.func(); // 输出3 this指向对象a
    

    如果中间被赋给其他变量 这指向全局对象,参数传递也是一种隐式赋值,传入的函数会被隐式赋值

    function func () {
       var b = 1
       console.log(this.b)  
    }
    var b = 2;
    var a = {
        b:3,
        func:func
    }
    var c = a.func
    c(); // 输出2 this指向对象全局
    func(); // 输出2 this指向对象全局
    
    function doFoo(fn) {
       fn();
    }
    doFoo(a.func) // 输出2 this指向对象全局
    

原理

参考:https://www.ruanyifeng.com/blog/2018/06/javascript-this.html

var a = {b:2,func:func}运行时 引擎会现在内存生成一个对象{b:2,func:func的地址},函数会单独保存在另一个内存中, 然后把这个函数内存地址复制给变量a.func属性,在把这个整个内存地址赋值给变量a, 这样func就会在两个环境中,在那个环境变量就使用那个环境的值,如下图 黑色的时在全局环境 灰色的是对象a的环境。当直接被对象调用时就去拿灰色的值。
请添加图片描述
显式绑定 call apply bind 传入的第一次参数时对象 这个对象会绑定到this

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

var obj = {
    a: 2
};

foo.call( obj );

new绑定 创建的对象会绑定到传入的构造函数的this

箭头函数 this是根据外层作用域来决定的

  1. 没有this this由外层作用域确定
  2. 没有arguments 通过…rest访问 let nums = (...nums) => nuns
  3. 不能通过new关键字调用 没有[[Construct]]方法 不能作为构造函数
  4. 没有new.target
  5. 没有原型
  6. 没有super
词法环境

标识符和变量的映射

环境记录器 :存储变量和函数声明的实际位置

  • 声明式环境记录器 用于函数执行上下文 包括存储变量 函数 参数
  • 对象环境记录器 用于全局执行上下文 定义全局上下文中出现的变量和函数的关联

外部环境的引用 :可以访问外部词法环境

语法环境

与词法环境不同的是 词法环境存储的是let const 声明的变量 语法环境存储的是var声明的变量

实例
let a = 20;
const b = 30;
var c;

function multiply(e, f){
    var g = 20;
    return e*f*g;
}

c = multiply(20, 30);


GlobalContext = {
  this:global,
   词法环境:{
    变量环境记录器: {
      Tyepe:Object,
      a: uninitialized,
      b:uninitialized,
      multiply:undefined
   },
  外部环境的引用: null
   },
  语法环境: {
    变量环境记录器: {
      Tyepe:Object,
      c:undefined,
   },
    外部环境的引用: null
  }
}

FunctionCOntext = {
  this:globalm
  词法环境:{
    声明式环境记录器: {
      Tyepe:Declarative,
      Arguments: {0:10,1:20,length:2}
   },
    外部环境的引用: 外部词法环境
   },
  语法环境: {
     声明式环境记录器: {
      Tyepe:Object,
      g:undefined,
   },
    外部环境的引用: 外部语法环境
  }
}

闭包

概念

一个函数有权访问另一函数的内存变量,表现形式是:一个函数返回另一个函数,在外部也可以访问内部函数的变量。

原理

在函数内部创建了自己的作用域链,包括了外部的函数变量因此可以访问。

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}

var foo = checkscope();
foo();
es3步骤
  1. 进入全局环境 创建全局执行上下文 加入执行栈底部

    globalContext = {
     VO: {
        scope:global scope,
        checkscope:function
        foo: undefined
     }
      Scope: [globalScope.VO]
    }
    ESStack = [globalContext]
    
  2. 执行函数 checkscope 创建执行上下文 ,压入执行栈

    checkscopeContext = {
      AO:{
         scope:'local scope',
         f:function
      }
      Scope: [checkscopeContext.AO,globalScope.VO]
    }
    ESStack = [checkscopeContext,globalContext]
    
  3. 执行完毕,从栈中退出 ESStack = [globalContext]

  4. 执行f 函数,创建执行上下文,压入执行栈

    fContext = {
      AO:{
      }
      Scope: [AO, checkscopeContext.VO, globalContext.V]
    }
    ESStack = [fContext,globalContext]
    
Es5步骤
  1. 进入全局环境 创建全局执行上下文 加入执行栈底部

    GlobalContext = {
      this:global,
       词法环境:{
        变量环境记录器: {
          Tyepe:Object,
          checkscope: function,
       },
      外部环境的引用: null
       },
      语法环境: {
        变量环境记录器: {
          scope:global scope,
           foo: undefined
       },
        外部环境的引用: null
      }
    }
    
  2. 执行函数 checkscope 创建执行上下文 ,压入执行栈

    checkscopeContext = {
      this:global
      词法环境:{
        声明式环境记录器: {
          Tyepe:Declarative,
          Arguments: {length:0},
          f:function
       },
        外部环境的引用: 外部词法环境
       },
      语法环境: {
         声明式环境记录器: {
          Tyepe:Object,
          scope:'local scope',
       },
        外部环境的引用: 外部语法环境
      }
    }
    
  3. 执行完毕,从栈中退出 ESStack = [globalContext]

  4. 执行f 函数,创建执行上下文,压入执行栈

fContext = {
  this:globalm
  词法环境:{
    声明式环境记录器: {
      Tyepe:Declarative,
      Arguments: {0:10,1:20,length:2}
   },
    外部环境的引用: 外部词法环境
   },
  语法环境: {
     声明式环境记录器: {
      Tyepe:Object,
   },
    外部环境的引用: 外部语法环境
  }
}

经典面试题

for(var i = 0;i<3;i++) {
  setTimeout(function() {
    console.log(i)
  })
}// 闭包常见考题
for(var i = 0;i<3;i++) {
  setTimeout(function() {
    console.log(i) // 输出 3 3 3 当前的作用域链为Scope: [AO, globalContext.VO] AO中没有会去全局环境中找 就是3
  })
}


//  解决办法1:利用闭包
for(var i = 0;i<3;i++) {
  (function(i) {
    setTimeout(function(){
      console.log(i)  // 输出 0,1,2 当前的作用域链为Scope: [AO, 匿名函数执行上下文VO,globalContext.VO] AO中没有会去匿名函数执行上下文中找
    })
  })(i)
}

//  解决办法2:利用es6 let
for(let i = 0;i<3;i++) {
  setTimeout(function() {
    console.log(i) // 输出 0,1,2 let使得for循环为块级作用域 当前的作用域链为Scope: [AO, 块级作用域,globalContext.VO] AO中没有块级作用域找
  })
}



// 变体
var data = []
for(var i = 0;i<3;i++) {
 data[i]=function() {
   console.log(i) // 输出 3 3 3 当前的作用域链为Scope: [AO, globalContext.VO] AO中没有会去全局环境中找 就是3
 }
}
data[0]();
data[1]();
data[2]();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值