hello,大家好!先摆一张图,大家可以看一下,是否能看懂呢

OK,在JavaScript,声明变量和函数是做项目开发不可避免的,甚至大部分都是声明变量和函数,JavaScript最核心的就是函数.js编译器是如何找到这些变量的呢?

我们还得对执行上下文有一个进一步的了解。

在上一篇文章中已经知道,当调用一个函数时(激活),一个新的执行上下文就会被创建。一个执行上下文的生命周期可以分为两个阶段。

  • 创建阶段

在这个阶段中,执行上下文会分别创建变量对象,建立作用域链,以及确定this指向。

  • 代码执行阶段

创建完成之后,就会开始执行代码,会完成变量赋值,函数引用,以及执行其他代码。

执行上下文生命周期

从这里可以看出详细了解执行上下文极为重要,因为其中涉及到了变量对象,作用域链,this等很多人没有怎么弄明白,但是却极为重要的概念,它关系到我们能不能真正理解JavaScript。在后面的文章中我们会一一详细总结,本文的核心是变量对象。

变量对象(Variable Object)

变量对象的创建,依次经历了以下几个过程。

// 这里a为属性名,20是属性值
{a: 20
}

一、建立arguments对象:检查当前上下文中的参数,建立该对象下的属性与
属性值。

函数参数

二、检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用

三、检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined

如果变量与函数同名,则在这个阶段,以函数值为准

console.log(foo); // function foo
function foo() { console.log('function foo') }
var foo = 20;
// 上栗的执行顺序为// 首先将所有函数声明放入变量对象中
function foo() { console.log('function foo') }// 其次将所有变量声明放入变量对象中,但是因为foo已经存在同名函数,此时以函数值为准,而不会被undefined覆盖
// var foo = undefined;// 然后开始执行阶段代码的执行
console.log(foo); // function foo
foo = 20;

我知道有的人不喜欢看文字

根据这个规则,理解变量提升就变得十分简单了。在很多文章中虽然提到了变量提升,但是具体是怎么回事还真的很多人都说不出来,以后在面试中用变量对象的创建过程跟面试官解释变量提升,简直逼格满满。

在上面的规则中我们看出,function声明会比var声明优先级更高一点。为了帮助大家更好的理解变量对象,我们结合一些简单的例子来进行探讨。

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

在上例中,我们直接从test()的执行上下文开始理解。全局作用域中运行test()时,test()的执行上下文开始创建。为了便于理解,我们用如下的形式来表示

// 创建过程
testEC = {// 变量对象VO: {},scopeChain: {}
}// 因为本文暂时不详细解释作用域链,所以把变量对象专门提出来说明// VO 为 Variable Object的缩写,即变量对象
VO = {arguments: {...},  //注:在浏览器的展示中,函数的参数可能并不是放在arguments对象中,这里为了方便理解,我做了这样的处理foo: <foo reference>  // 表示foo的地址引用a: undefined
}

未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。

这样,如果再面试的时候被问到变量对象和活动对象有什么区别,就又可以自如的应答了,他们其实都是同一个对象,只是处于执行上下文的不同生命周期。不过只有处于函数调用栈栈顶的执行上下文中的变量对象,才会变成活动对象。

// 执行阶段
VO ->  AO   // Active Object
AO = {arguments: {...},foo: <foo reference>,a: 1,this: Window
}

因此,上面的例子demo1,执行顺序就变成了这样

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

再来一个例子,巩固一下我们的理解。

// demo2
function test() {console.log(foo);console.log(bar);var foo = 'Hello';console.log(foo);var bar = function () {return 'world';}function foo() {return 'hello';}
}test();
// 创建阶段
VO = {arguments: {...},foo: <foo reference>,bar: undefined
}
// 这里有一个需要注意的地方,var声明的变量与函数同名,以函数为准
// 执行阶段
VO -> AO
VO = {arguments: {...},foo: 'Hello',bar: <bar reference>,this: Window
}

需要结合上面的知识,仔细对比这个例子中变量对象从创建阶段到执行阶段的变化,如果你已经理解了,说明变量对象相关的东西都已经难不倒你了。

全局上下文的变量对象

以浏览器中为例,全局对象为window。
全局上下文有一个特殊的地方,它的变量对象,就是window对象。而这个特殊,在this指向上也同样适用,this也是指向window。

// 以浏览器中为例,全局对象为window
// 全局上下文
windowEC = {VO: Window,scopeChain: {},this: Window
}

除此之外,全局上下文的生命周期,与程序的生命周期一致,只要程序运行不结束,比如关掉浏览器窗口,全局上下文就会一直存在。其他所有的上下文环境,都能直接访问全局上下文的属性。

let/const

ES6中,新增了使用let/const来声明变量。我想他们的使用肯定难不倒大家。可是有一个问题不知道大家思考过没有,let/const声明的变量,是否还会变量提升?

是的,这个刁钻的问题也成为了各大面试官爱问的细节。很贱!可也没办法,还是要弄明白怎么回事!

我们来做个试验,验证一下这个问题:

第一步,我们直接使用一个未定义的变量

console.log(a);

报错信息如下:

not defined

第二步,我们在let之前调用变量

console.log(a);
let a = 10;

会发生什么?会打印出undefined吗?

看看结果

不能在初始化之前访问a

这个报错说明了什么问题呢?变量定义了,但是没有初始化。

所以在这里我们就可以得出结论:let/const声明的变量,仍然会提前被收集到变量对象中,但和var不同的是,let/const定义的变量,不会在这个时候给他赋值undefined。

因为完全没有赋值,即使变量提升了,我们也不能在赋值之前调用他。这就是我们常说的暂时性死区

当然,变量提升的现象有的时候确实会对我们的代码造成一些负面影响,所以在开发中要养成好的习惯,把将要声明的变量放在最前面来写.

感谢大家的阅读,谢谢!

前端基础进阶(三)-史上最详细的变量对象详解相关推荐

  1. boost log 能不能循环覆盖_前端基础进阶(十四):深入核心,详解事件循环机制...

    Event Loop JavaScript的学习零散而庞杂,很多时候我们学到了一些东西,但是却没办法感受到进步!甚至过了不久,就把学到的东西给忘了.为了解决自己的这个困扰,在学习的过程中,我一直在试图 ...

  2. 史上最全JavaScript数组对象详解(二)

    JavaScript数组对象详解(二) 上一篇博客我们讲到了JavaScript数组对象的创建,访问和属性,接下来一篇博客主要讲一下JavaScript数组对象的方法及使用.说到数组的方法,主要分为两 ...

  3. 史上最详细的maven仓库详解

    写在前面: 我是「沸羊羊_」,昵称来自于姓名的缩写 fyy ,之前呕心沥血经营的博客因手残意外注销,现经营此账号. 本人是个小菜,正向着全栈工程师的方向努力着,文章可能并不高产,也很基础,但每写一篇都 ...

  4. android 红包功能,史上最详细的微信抢红包详解

    一.技术背景 辅助功能设计初衷在于帮助残障用户使用android设备和应用,在后台运行,可以监听用户界面的一些状态转换,例如页面切换.焦点改变.通知.Toast等,并在触发AccessibilityE ...

  5. centos7中ps显示的内容_值得收藏,史上最全Linux ps命令详解

    原标题:值得收藏,史上最全Linux ps命令详解 一.程序员的疑惑 大概在十多年前,我当时还是一个产品经理.由于一些工作的原因,需要向运维工程师学习一些linux常用命令. 当使用linux ps这 ...

  6. 史上最全的IP地址详解,速来get

    史上最全的IP地址详解 白在了解虚拟机网络之前,我们首先得了解ip地址是什么? 1.简单局域网的构成 局域网:一般称为内网 简单局域网的构成:交换机.网线.PC(其他IT终端) 交换机:用来组建内网的 ...

  7. axrue9不显示右侧文件_Axure 9.0基础教程:史上最详细的元件说明,建议你认真看完(一)...

    元件说明摘要:元件作为Axure 9.0的基础功能,线框图的绘制与交互事件的设置都离不开它,熟练掌握并了解每个元件的功能及用途,对原型设计来说尤为重要.这是一篇细到令人发指的关于元件的使用说明,不仅有 ...

  8. 史上最全YYModel的使用详解

    原文链接:http://www.jianshu.com/p/25e678fa43d3 demo链接:https://github.com/walkertop/YYModel---Demo 插件链接:h ...

  9. 「万字图文」史上最姨母级Java继承详解

    原创公众号:「bigsai」 除公众号以外拒绝任意擅自转载 文章收录在bigsai公众号和回车课堂 课程导学 在Java课堂中,所有老师不得不提到面向对象(Object Oriented),而在谈到面 ...

  10. 广成子:值得收藏-史上最全Linux ps命令详解

    From 闻茂泉(广成)AliDataOps 一.程序员的疑惑   大概在十多年前,我当时还是一个产品经理.由于一些工作的原因,需要向运维工程师学习一些linux常用命令.当使用linux ps这个十 ...

最新文章

  1. mapreduce理解_大数据
  2. 中国唯一的“国际数字化转型专家”,阿里云获Forrester认可
  3. Hibernate查询语言(HQL)
  4. IE弹出窗口显示URL地址栏
  5. python 绘制围棋棋盘_奇思妙想(2)五子棋棋盘落子
  6. java实验十三io_Java语言基础13—IO
  7. 遗传算法求解立体仓库货位优化
  8. 移动网络安装测试软件,adsl网速测试(中国移动宽带专用测速软件)
  9. 防拷贝U盘在软件开发行业的应用,软件怎样防复制防拷贝?
  10. 计蒜客:Adjoin the Networks
  11. Windows安装git图文教程
  12. 视觉中国图片编码_学习编码第14天的应用视觉设计第4部分
  13. linux编译aborted,Ubuntu Linux上编译kernel出错__stack_chk_fail
  14. Modbus学习总结
  15. matlab电位图仿真实验,基于MATLAB的静电场描绘实验仿真
  16. JavaBean输入圆的半径,求圆的面积和周长
  17. EasyExcel CellWriteHandler注入CellStyle不生效问题
  18. iCMS的spider_rule.admincp.php存在报错SQL注入
  19. 做云端数据有备无患的“杀手锏”
  20. windows bat批处理解压文件

热门文章

  1. java源代码实现判断闰年和平年
  2. mysql捕获1300的错误_pt-osc 变更时遇到 “MySQL error 1300” 报错问题解决
  3. wxid 微信号设置隐私 微信号搜不到 恢复好友总结
  4. Windows 2000/XP IIS5.1安装
  5. CFBI中国金融科技与区块链创新峰会2017年度盛会
  6. 仿古建筑为什么那么丑
  7. 1987年,国际C语言混乱代码大赛
  8. java+svm多分类器_svm多分类的java源码
  9. 迅捷在线PDF转换成Word转换器简介
  10. #####好好好好######Neo4j 第三篇:Cypher查询入门