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

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

之所以会写这篇文章,主要源于笔者在重构老项目的时候发现了一个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等前端知识和实战,欢迎在公众号《趣谈前端》加入我们一起学习讨论,共同探索前端的边界。



目录
相关文章
|
2月前
|
前端开发 UED 开发者
颠覆你的前端知识:防抖与节流的区别及实战解析!
【8月更文挑战第23天】在Web前端开发中,处理用户界面交互产生的事件可能会影响性能。为此,我们有两种优化方法:防抖(debounce)和节流(throttle)。防抖确保函数仅在事件停止触发一段时间后执行一次,适用于如搜索自动补全场景。而节流则确保函数按固定时间间隔执行,不管用户操作频率如何。本篇技术博客将深入解析两者差异并提供示例代码,帮助开发者更好地理解和应用这些技巧以提升应用性能和用户体验。
63 0
|
3月前
|
前端开发
Element UI 【实战】纯前端对表格数据进行增删改查(内含弹窗表单、数据校验、时间日期格式)
Element UI 【实战】纯前端对表格数据进行增删改查(内含弹窗表单、数据校验、时间日期格式)
123 6
|
6天前
|
存储 前端开发 JavaScript
前端基础(二)_JavaScript变量、JavaScript标识符、JavaScript获取元素、JavaScript的鼠标事件
本文介绍了JavaScript变量的声明和使用、标识符的命名规则、如何获取和操作HTML元素,以及JavaScript的鼠标事件处理,通过示例代码展示了这些基础知识点在实际开发中的应用。
19 2
前端基础(二)_JavaScript变量、JavaScript标识符、JavaScript获取元素、JavaScript的鼠标事件
|
6天前
|
存储 前端开发 JavaScript
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
本文介绍了JavaScript中作用域的概念,包括全局变量和局部变量的区别,预解析机制(变量提升),以及函数返回值的使用和类型。通过具体示例讲解了变量的作用域、函数的返回值、以及如何通过return关键字从函数中返回数据。
11 1
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
|
6天前
|
存储 前端开发 JavaScript
前端基础(十一)_函数声明及调用、函数的形参与实参、arguments参数、函数的参数类型、函数中的问题
本文介绍了JavaScript中函数的声明及调用、形参与实参的概念、arguments对象的使用、函数参数的类型以及函数中this的作用。通过示例代码详细解释了函数如何接收参数、如何处理参数个数不匹配的情况,以及函数在不同上下文中this的指向。
13 1
|
29天前
|
缓存 监控 前端开发
前端性能优化实战:让你的网站快如闪电的十大秘籍
【9月更文挑战第3天】通过以上十大秘籍的实践,您可以显著提升网站的前端性能,让您的网站在竞争激烈的互联网环境中脱颖而出,为用户带来更加流畅和愉悦的体验。记住,前端性能优化是一个永无止境的过程,只有不断迭代和优化,才能保持网站的竞争力。
|
2月前
|
编解码 前端开发 开发者
【前端设计达人必备】揭秘CSS尺寸单位的魔力:从基础到实战,打造灵动响应式网页!
【8月更文挑战第26天】本文深入探讨了CSS中常用的尺寸单位,包括像素(px)、百分比(%)、视窗单位(vw/vh/vmin/vmax)、可伸缩相对单位(em/rem)以及Flexbox和Grid中的fr单位。通过具体案例展示了每种单位的特点及其适用场景。像素适用于固定尺寸元素;百分比和em/rem利于构建响应式布局;视窗单位适合全屏设计;fr单位则能有效管理复杂网格布局的空间分配。掌握这些单位有助于开发者设计出更加灵活、高质量的网页布局。
38 4
|
2月前
|
前端开发 JavaScript 开发者
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
本文提供了一个前端JS按钮点击事件、弹出式窗口和遮罩层的实战示例,包括HTML、CSS和JavaScript的具体实现代码,以及功能解析,演示了如何实现按钮点击后触发弹窗显示和遮罩层,并在2秒后自动关闭或点击遮罩层关闭弹窗的效果。
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
|
2月前
|
存储 前端开发 JavaScript
解锁前端高手之路:Vuex 状态管理实战,从零到精通的旅程!
【8月更文挑战第27天】状态管理在大型单页应用开发中至关重要。Vue.js 通过其官方工具 Vuex 提供了一套强大且直观的 API。本文从零开始引导你逐步掌握 Vuex。首先介绍如何安装和配置,然后通过具体示例深入探讨状态(State)、变更(Mutations)、动作(Actions)以及模块化 Store 的使用方法。最后,通过购物车管理实战案例展示如何运用 Vuex 解决复杂状态管理问题。掌握这些技巧后,你将能在项目中高效地利用 Vuex。
18 1
|
2月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
49 0

热门文章

最新文章