资讯专栏INFORMATION COLUMN

【6】JavaScript 函数高级——执行上下文与执行上下文栈、变量提升(图解+典型实例分析)

niuxiaowei111 / 2937人阅读

摘要:函数和变量相比,会被优先提升。这意味着函数会被提升到更靠前的位置。仅提升声明,而不提升初始化。

JavaScript 函数高级——执行上下文与执行上下文栈(图解+典型实例分析) 变量提升与函数提升

变量声明提升

通过 var 定义(声明)的变量,在定义语句之前就可以访问到

值:undefined

/*
  面试题 : 输出 undefined
   */
var a = 3
function fn() {
    console.log(a) // undefined  调用fn()后进入函数体内部,通过 var 声明的变量提升,值为undefined
    var a = 4
    console.log(a) // 4
}
fn()
console.log("----")
可以用开发工具的代码调试工具设置断点,清楚地看到a的值的变化。

函数声明提升

通过 function 声明的函数,在该函数声明之前就可以直接调用

值:函数定义(对象)

console.log(b) // undefined  通过 var 声明的变量b         变量声明提升
fn2() // fn2()               通过 function 声明的函数fn2  函数声明提升
fn3() // 报错                 通过 var 声明的函数fn3       变量声明提升

var b = 3
function fn2() {
  console.log("fn2()")
}

var fn3 = function () {
  console.log("fn3()")
}

!!注意 var a = 3var fn = function(){ }只要是var声明的,都是变量声明提升,值都是undefined

!!注意:var fn = function(){ }属于变量声明提升,不是函数声明提升

问题:变量提升和函数提升是如何产生的?

这是因为在JavaScript中执行上下文的工作方式造成的。
函数和变量相比,会被优先提升。这意味着函数会被提升到更靠前的位置。
JavaScript 仅提升声明,而不提升初始化。

执行上下文
执行上下文个数 = n(调用的函数个数) + 1(全局执行上下文)
// 变量声明提升,函数声明提升,全局代码的函数和方法添加为Window的方法和属性
console.log(a1, window.a1) // undefined   undefined
window.a2() // a2()
console.log(this) // Window

var a1 = 3
function a2() {
    console.log("a2()")
}
console.log(a1) // 3
代码分类(位置)

全局代码

函数代码

全局执行上下文

执行全局代码前window确定为全局执行上下文

全局数据进行预处理:

var 定义的全局变量==>undefined,添加为window属性

function 声明的全局函数==>赋值(fun),添加为window的方法

this==>赋值(window)

开始执行全局代码

函数执行上下文

调用函数,准备执行函数体之前,创建对应的函数执行上下文对象

局部数据进行预处理

形参变量==>赋值(实参)==>添加为执行上下文的属性

arguments==>赋值(实参列表),添加为执行上下文的属性

var 定义的局部变量==>undefined==>添加为执行上下文的属性

function声明的函数==>赋值(fun)==>添加为执行上下文的属性

this==>赋值(调用函数的对象)

开始执行函数体代码

执行上下文栈 理解

在全局代码执行前, JS引擎就会创建一个来存储管理所有的执行上下文对象

全局执行上下文(window)确定后, 将其添加到栈中(压栈)

函数执行上下文创建后, 将其添加到栈中(压栈)

在当前函数执行完后,将栈顶的对象移除(出栈)

当所有的代码执行完后, 栈中只剩下window

流程分析
// 1. 进入全局上下文
var a = 10
var bar = function (x) {
    var b = 5
    // 3. 进入foo执行上下文
    foo(x + b)
}
var foo = function (y) {
    var c = 5
    console.log(a + c + y)
}
// 2. 进入bar函数执行上下文
bar(10)  // 30

用断点调试工具查看执行上下文调用栈

面试题 题1:递归调用的执行上下文
console.log("gb: " + i)  
var i = 1
foo(1)
function foo(i) {
    if (i == 4) {
        return
    }
    console.log("fb:" + i)
    foo(i + 1) //递归调用: 在函数内部调用自己
    console.log("fe:" + i)
}
console.log("ge: " + i) // ge:1

依次输出什么?

整个过程中产生了几个执行上下文?

5个 = 4个函数执行上下文 + 1个全局执行上下文

调试查看:

题2:函数提升优先于变量提升,且不会被变量声明覆盖,但是会被变量赋值覆盖。
console.log(foo)  // ƒ foo() {console.log("函数声明")}
console.log(foo()) // 我是一个函数  undefined
function foo() {
  console.log("我是一个函数")
}
var foo = 3
console.log(foo) // 3
console.log(foo()) // foo is not a function

相当于

function foo(){
    console.log("函数声明")
}  // 函数声明先提升
var foo   // 变量声明提升,变量初始化未提升,此时foo的值为undefined,所以不会覆盖函数声明
console.log(foo)  // 打印出ƒ foo() {console.log("函数声明")}
console.log(foo()) // 我是一个函数  undefined
foo = 3 // 此时给foo赋值为3,则会覆盖前面的
console.log(foo) // 3
console.log(foo()) // foo is not a function,因为此时foo=3
题3:考察全局环境下的变量提升
if (!(t in window)) {
    var t = 1
}
console.log(t)  // undefined
t in window
// true

原因就在于变量t被提升到了全局环境最顶部,通过关键字var在全局作用域中声明的变量将作为全局对象(window)的一个属性,上面的代码JavaScript其实是这样解析的:

var t
if (!(t in window)) {
    var t = 1
}
console.log(t) // undefined
题4(好题)
var c = 1
function c(c) {
  console.log(c)
  var c = 3
}
c(2) // 报错  c is not a function 

注意:函数提升为c(),整个函数提升,所以上面的代码相当于下面这样,这样就很清楚了吧!

function c(c) {
    console.log(c)
    var c = 3
}
var c

c = 1
c(2) //所以 c is not a function

函数体内的代码根本就没机会执行。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104157.html

相关文章

  • javascript高级程序设计》笔记:内存执行环境

    摘要:因此,所有在方法中定义的变量都是放在栈内存中的当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用因为对象的创建成本通常较大,这个运行时数据区就是堆内存。 上一篇:《javascript高级程序设计》笔记:继承近几篇博客都会围绕着图中的知识点展开 showImg(https://segmentfault.com/img/bVY0C4?w=1330&h=618);...

    fuyi501 评论0 收藏0
  • 【进阶1-2期】JavaScript深入之执行下文变量对象

    摘要:本计划一共期,每期重点攻克一个面试重难点,如果你还不了解本进阶计划,点击查看前端进阶的破冰之旅本期推荐文章深入之执行上下文栈和深入之变量对象,由于微信不能访问外链,点击阅读原文就可以啦。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,今天是第二天。 本计划一共28期,每期...

    Richard_Gao 评论0 收藏0
  • 【进阶1-1期】理解JavaScript 中的执行下文执行

    摘要:首次运行代码时,会创建一个全局执行上下文并到当前的执行栈中。执行上下文的创建执行上下文分两个阶段创建创建阶段执行阶段创建阶段确定的值,也被称为。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,,今天是第一天 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进...

    import. 评论0 收藏0
  • 【进阶1-3期】JavaScript深入之内存空间详细图解

    摘要:进阶期理解中的执行上下文和执行栈进阶期深入之执行上下文栈和变量对象但是今天补充一个知识点某些情况下,调用堆栈中函数调用的数量超出了调用堆栈的实际大小,浏览器会抛出一个错误终止运行。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,今天是第3天。 本计划一共28期,每期重点攻...

    coordinate35 评论0 收藏0
  • 【进阶2-2期】JavaScript深入之从作用域链理解闭包

    摘要:使用上一篇文章的例子来说明下自由变量进阶期深入浅出图解作用域链和闭包访问外部的今天是今天是其中既不是参数,也不是局部变量,所以是自由变量。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第二期,本周的主题是作用域闭包,今天是第7天。 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进阶计...

    simpleapples 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<