javascript变量提升

前言:

JavaScript语言与其他语言非常不同,它一切的操作都是基于原型链,这是javascript的精髓。但是由于javascript定义了一个全局作用域,并把全局作用域绑定到了顶层对象中(浏览器环境中顶层对象是window,在node环境下顶层对象是global),顶层对象的属性与全局变量挂钩,被认为是 JavaScript 语言最大的设计败笔之一。由于顶层对象可以属性可以随意的修改新增,很不利于模块化开发或者团队合作开发。另外全局变量的定义没办法在编译期间检查变量是否未定义,因为无法确定一些全局属性是否由于顶层api操作而生成。

变量提升也是javaScript语言中非常大的一个问题,往往会将变量泄露到全局作用域,在团队协同开发中,经常会出现互相冲突替换的问题。因此es6又提出了块级作用域以及let、const声明方式,到此才解决了变量提升的问题

一、什么是变量提升?

在大量的编程语言中,变量或者函数都要求先定义后使用,而在javascript中存在变量提升和函数提升,因此产生了许多不同的行为。变量提升及变量定义后会将变量提升到作用域链的最顶端,如果未指定值,即得到undefined值。变量提升是为了生成便于管理的作用域链,作用链中包含函数执行的上下文(javascript作者对该语言的设计)。至于函数提升,也同变量提升一样,函数会提升到作用域链顶端,但比变量作用域提升的更高。

/**
*变量提升
*/

//此处变量为定义的情况下,直接报错
(function () {
    console.log(obj);//Uncaught ReferenceError
})();


//变量定义后,顺序不正确打印undefined
(function(){
    console.log(obj); //undefined
    var obj;
})();

//变量提示的demo
var scope = "global";
foo();
function foo(){
	console.log(scope);//undefined
	var scope = "local";
	console.log(scope);//local
}

//提升后的代码为
var scope;
function foo(){
	var scope;
	console.log(scope);//undefoned
	scope = "loacl";
	console.log(scope);//local
}
foo();
scope = "global";


/**
*函数提升
*/

func();//11111

function func(){
    console.log('1111111');
};

二、变量提升的危害

在开发过程中变量提升,我们随意使用var定义的变量,往往在不经意间,就提升为全局变量。虽然这样我们能够方便的访问变量,但是如果与团队分工合作的前提下,个人定义的变量一旦提升为全局变量,团队中的其他人也采用了同名的变量,那么这两个部分将会想回影响,最终会出现许多难以预料到的问题。

因此在es6之前,许多大型公司提出了seajs,requirejs等概念,为了规避这种全局变量的影响。其实现的基本概念也并不复杂,仅仅使用闭包和默认执行函数来规避全局作用域。这些概念逐渐成为现在主流的cmd、umd、amd等框架思路,同时也影响了es6的语言的定义,从而引出了import、export以及import() api的定义。

三、let与const

由于es6存在块级作用域,因此在块级作用域内,只允许声明一个变量,但不予许同名变量。由于存在暂时性死区,定义前使用,会报错提示。目前官方建议使用let或者const变量进行开发。

let a = "hey I am outside";
if(true){
    //此处存在暂时性死区
    console.log(a);//Uncaught ReferenceError: a is not defined
    let a = "hey I am inside";
}

//let与const不存在变量提升
console.log(a); // Uncaught ReferenceError: a is not defined
console.log(b);// Uncaught ReferenceError: b is not defined
let a = 1;
const b = 2;

//不存在变量提升,因此块级作用域外层无法访问
if(true){
	var bar = "bar";
	let baz = "baz";
	const qux = "qux";
}
console.log(bar);//bar
console.log(baz);//baz is not defined
console.log(qux);//qux is not defined

四、部分变量提升的面试题

在面试过程中遇到一些关于变量提升的面试题,但是大部分都记不太清楚了,目前只还能想起下面的几道经典的问题。因此写在此处,后期遇到新的会继续补充

//比较常见的一道面试题,一般会要求改写这道题,变成正确结果
//此处打印错误的根本原因是因为变量提升,由于setTimeout是定时器,方法不会立即执行,只会将方法加入到
//延迟执行队列中,那时for循环已经执行完毕,由于变量提升,for外层仍然可以访问变量i,因此打印5
for( var i = 0;i<5;i++) {
    setTimeout(function() {
        console.log(i);
    },1000)
}
//5 5 5 5 5

//比较常见的改写方法
for( var i = 0;i<5;i++) {
    (function(i){
        //注意此处的参数i,有的面试题会故意去掉参数i,问打印结果,需要特别注意
        setTimeout(function () {
            console.log(i);
        },1000)
    })(i);
}
//0 1 2 3 4

//混淆视听的写法,此处虽然打印出了正确的结果,但并未延迟执行,因此setTimeout方法参数传递的为默认
//执行函数,因此会提前执行,变量i在for循环执行时,就会传递正确的i,因此打印正确的结果
for (var i = 0; i < 5; i++) {
    setTimeout((function(i) {
        console.log(i);
    })(i),1000)
} 
//0 1 2 3 4 


//此处是最简单的变量提升面试题,仅需要注意即可
//注意函数提示优于变量
console.log(a);//function a(){}
console.log(b);//undefined
var a = 1;
function a(){}
var b= function(){};
console.log(a)//1


 

你可能感兴趣的:(javascript学习)