《前端实战总结》之变量提升,函数声明提升及变量作用域详解

徐小夕 等级 350 0 0

之所以会写这篇文章,主要源于笔者在重构老项目的时候发现了一个bug,导致某个插件不生效了,在review加search code加断点调试之后,发现了原因:一个同名的变量将插件方法给覆盖了,ohmyGad。

正文

1.变量是如何被覆盖的

在一般情况下,js代码都是自上而下执行的,对于同一个变量,我们可以通过如下方式来修改:

var a = 1;
a = 2;
console.log(a)   // 2
a = function(){};
console.log(a)   // function(){};

2.变量提升

上面的覆盖过程大家都很好理解,那么看如下的操作呢?

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

这个时候console.log()都会输出undefined而不会报错,这是为什么呢?这里就是变量提升起到的作用。我们在用var或者函数声明的方式定义一个变量时,这个变量的定义会提升到方法体的最顶端,即如下所示:

var a = undefined;
var b = undefined;
console.log(a)
// ..
console.log(b)

因此我们得出一条结论:

函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部。

值得注意的是,我们使用let,const定义变量的时候,并不会发生提升,因为它存在局部(块)作用域的概念,会出现暂时性死区,所以在它们之前打印变量将报错。如果对暂时性死区或者对es6不太了解的朋友可以参考我的另一篇文章,

快速掌握es6+新特性及es6核心语法盘点;

对let和const以及es6的新特性有详细的介绍。

3.更近一步——变量提升的优先级

直接剖出问题:

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

此时代码会打印什么呢?答案是会打印1。这个问题也是我之前面试一些求职者的过程中错误高发区,这里隐藏着一个概念:函数声明提升的优先级高于变量声明的提升。浏览器底层的实现过程是这样的:当js解析器在遇到函数声明时,会优先将其提升到定义体顶部,其次再是var声明的变量,这样就导致函数a被变量a给覆盖的情况,所以最终将打印1。

4.函数参数作用域与作用域链

作用域就是变量和函数的可访问范围,当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain),来保证对执行环境有权访问的变量和函数的顺序访问。作用域第一个对象始终是当前执行代码所在环境的变量对象。然后会一层层向外查找,直到发现第一个指定的变量为止。

在了解完以上概念之后,我们来看看下面这个问题:

var a = {name: 'xuxi'};
function b(a){
    a.age = 12;
    a = {num: 1};
    return a
}
var a1 = b(a);
console.log(a, a1)

上面代码打印的是什么呢?其实这个是我今天出的面试题,还是因为一个朋友之前问了我这个问题,我觉得有必要总结一下。虽然今天的候选人没有答出来,但是我相信在给他解释完之后他应该不虚此行(说过了,不好意思)。

这块主要还是函数内部作用域和引用类型的一个问题。具体过程如下:

(1)我们根据之前介绍的作用域和作用域链的概念可以知道,在函数体内,变量会就近查找,而函数参数会存在于函数体内部作用域中,所以当我们把全局变量a当作入参传递给函数时,又由于全局a是引用类型,此时只是引用了它的地址,那么我们通过a.age设置属性时,全局a也会改变。 (2)第二步是将a赋予了一个新的值,此时的a根据就近查找其实是参数a,本质上是将参数a赋予了一个新的对象,这个时候和全局变量的a没有任何关系了,此时函数最后会返回一个新的对象。

综上两步分析,我们就会明白为什么打印a时输出的是{name: 'xuxi', age: 12},打印a1会输出{num: 1}了。

总结

函数声明提升,变量作用域以及作用域链这块一直是学习javascript的基础也是重点,所以希望这篇文章可以让大家更好的掌握它。 如果想了解更多webpack,node,gulp,css3,javascript,nodeJS,canvas等前端知识和实战,欢迎在公众号《趣谈前端》加入我们一起学习讨论,共同探索前端的边界。

《前端实战总结》之变量提升,函数声明提升及变量作用域详解

更多推荐

收藏
评论区

相关推荐

JS排序算法
引子 有句话怎么说来着: 雷锋推倒雷峰塔,Java implements JavaScript. 当年,想凭借抱Java大腿火一把而不惜把自己名字给改了的JavaScript(原名LiveScript),如今早已光芒万丈。node JS的出现更是让JavaScript可以前后端通吃。虽然Java依然制霸企业级软件开发领域(C/C 的大神们不要打
《前端实战总结》之变量提升,函数声明提升及变量作用域详解
之所以会写这篇文章,主要源于笔者在重构老项目的时候发现了一个bug,导致某个插件不生效了,在review加search code加断点调试之后,发现了原因:一个同名的变量将插件方法给覆盖了,ohmyGad。 正文 1.变量是如何被覆盖的 在一般情况下,js代码都是自上而下执行的,对于同一个变量,我们可以通过如下方式来修改: js var a 1;
《前端实战总结》之使用解释器模式实现获取元素Xpath路径的算法
前端领域里基于javascript的设计模式和算法有很多,在很多复杂应用中也扮演着很重要的角色,接下来就介绍一下javascript设计模式中的解释器模式,并用它来实现一个获取元素Xpath路径的算法。 上期回顾 《前端实战总结》之迭代器模式的N1种应用场景(https://juejin.im/post/6844904008616771591)
Cors跨域解决
一、浏览器跨域问题产生 1、跨源资源共享(CORS)中文文档: https://developer.mozilla.org/zhCN/docs/Web/HTTP/Access_control_CORS 2、什么是浏览器跨域问题 指的是浏览器不能执行其他网站的脚本。JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象,即同源策略。就好比我
2B 领域下的低代码探索之路
前言 大家好,我是钉钉宜搭前端一个小团队的负责人,在阿里做了五年的低代码。今天的分享我们不讲技术细节,主要会分享下我们这五年的探索过程和当前的市场分析,希望能给大家带来一个对低代码搭建不一样视角的认识。 什么是低代码 说起低代码(LowCode)这个词,是在 2014 年,Forrester Research 第一次正式使用低代码来描述这个市场
JS - 作用域
一、作用域 作用域,即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合 换句话说,作用域决定了代码区块中变量和其他资源的可见性 举个例子 function myFunction() {     let inVariable  "函数内部变量"; } myFunction();//要先执行这个函数,否则根本不知
彻底理解js的作用域链
在之前的文章(https://www.helloworld.net/p/G4dFV7tALU4J)中我已经介绍了执行上下文的变量对象。在这一篇文章我要介绍执行上下文的作用域链了。 执行上下文.作用域链(scope chain) 作用域链与变量对象有着密不可分的关系,因为作用域链就是变量对象的数组!其中第
nslookup命令使用技巧【珍藏版】
目录前言正文一、缺省查询二、指定DNS查询三、查询命名服务器四、反向解析域名五、查询邮件服务器信息 前言nslookup是一个DNS查询工具,可以指定查询的类型,可以查到DNS记录的生存时间,还可以指定使用那个DNS服务器进行解释。 正文一、缺省查询不指定 dnsserver,使用系统默认的 dns 服务器。 命令: nslookup 域名实例:
DevOps与CICD的区别 及 docker、k8s的CICD思路
1\. DevOps简介DevOps 就是开发(Development)、测试(QA)、运维(Operations)这三个领域的合并。image.png为什么要合并这三个领域?主要是开发和运维的脱节。DevOps是一种思想、一组最佳实践、以及一种文化。DevOps落地实施,从组织架构、设计人员、流程、人员分工、人员技能到工具,变化
JS核心原理理解闭包
前置概念在正式看闭包之前,我们先来学习一下前置知识,那就是JS中的作用域,我们知道,在ES5之中,作用域分为两种:全局作用域和函数作用域,随着ES6的到来,新增了块级作用域,想更好的理解闭包,那么搞清楚作用域是首要条件全局作用域我们知道,对于变量而言,我们一般会分成两类:全局变量和局部变量,一般定义在最外围环境的为全局变量,定义在函数当中的为局部变量,在we
你所知道的JS变量作用域
变量的作用域,指的是变量在脚本代码中的可读、可写的有效范围,也就是脚本代码中可以使用这个变量的区域。在ES6之前,变量的作用域主要分为全局作用域、局部作用域(也称函数作用域)两种;在ES6及其之后,变量的作用域主要分为全局作用域、局部作用域、块级作用域这3种。相应作用域变量分别称为全局变量、局部变量、块级变量。全局变量声明在所有函数之外;局部变量是在函数体内
重学JavaScript第1集|变量提升
变量提升就好比JavaScript引擎用一个很小的代码起重机将所有var声明和function函数声明都举起到所属作用域(所谓作用域,指的是可访问变量和函数的区域)的最高处。这句话的意思是:如果在函数体外定义函数或使用var声明变量。则变量和函数的作用域会提升到整个代码的最高处,此时任何地方访问这个变量和调用这个函数都不会报错;而在函数体内定义函数或使用va
秒懂js作用域与作用域链
JavaScript 中有一个被称为作用域(Scope)的特性。虽然对于许多新手开发者来说,作用域的概念并不是很容易理解,本文我会尽我所能用最简单的方式来解释作用域和作用域链,希望大家有所收获!好了下面开始我们的正文作用域常见的解释(什么是作用域)1. 一段程序代码中所用到的名字并不总是有效,而限定它的可用性的范围就是这个名字的作用域;2. 作用域规定了
阿里Java面试必问:Java开发热门前沿知识
策略 1——停止挖掘Law of Holes 是说当自己进洞就应该停止挖掘。对于单体式应用不可管理时这是最佳建议。换句话说,应该停止让单体式应用继续变大,也就是说当开发新功能时不应该为旧单体应用添加新代码,最佳方法应该是将新功能开发成独立微服务。如下图所示:除了新服务和传统应用,还有两个模块,其一是请求路由器,负责处理入口(http)请求,有点像之前提到的
(转载)Java内存区域(运行时数据区域)和内存模型(JMM) - czwbig
转载自:Java 内存区域和内存模型是不一样的东西,内存区域是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分。而内存模型(Java Memory Model,简称 JMM )是定义了线程和主内存之间的抽象关系,即 JMM 定义了 JVM 在计算机内存(RAM)中的工作方式,如果我们要想深入了解Java并发编程,就要先理解好Java内存模型。Java