《前端实战》之变量提高,函数声明提高及变量做用域详解

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

正文

1.变量是如何被覆盖的

在通常状况下,js代码都是自上而下执行的,对于同一个变量,咱们能够经过以下方式来修改:css

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或者函数声明的方式定义一个变量时,这个变量的定义会提高到方法体的最顶端,即以下所示:vue

var a = undefined;
var b = undefined;
console.log(a)
// ..
console.log(b)
复制代码

所以咱们得出一条结论:java

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

值得注意的是,咱们使用let,const定义变量的时候,并不会发生提高,由于它存在局部(块)做用域的概念,会出现暂时性死区,因此在它们以前打印变量将报错。若是对暂时性死区或者对es6不太了解的朋友能够参考个人另外一篇文章,webpack

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

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

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

直接剖出问题:web

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等前端知识和实战,欢迎在公众号《趣谈前端》加入咱们一块儿学习讨论,共同探索前端的边界。

更多推荐

相关文章
相关标签/搜索