JS基础系列--理解var/const/let

    对于写js的人来讲,这三个关键字再熟悉不过了吧。ES6以前,只要是定义变量,哪儿都丢一个var过去,都不须要多想的。到了ES6标准,忽然新增了两个新玩意儿"const"和"let"。之前是没得选,如今在定义变量的时候老是得想一下应该用哪个了。那么既然已经有了var能够定义变量了,为何还须要提供新的东西来提升使用复杂度呢?咱们必须相信,一个新事物可以流行或替代旧的事物,一定是之前的东西存在缺点和不足,新东西可以解决这些问题。那么接下来咱们就来聊一聊这几个关键字之间的关系。html

按照惯例,咱们先罗列几个常识:数组

  1. var是ES5中的,let和const是ES6标准中新增的变量定义关键字
  2. let和const定义的变量的做用域在块级做用域中;var变量在函数外则是全局变量,在函数内则是局部变量
  3. 使用var来建立变量,声明后未赋值的变量输出会提示 “undefined”。在方法函数外声明的变量未使用var ,会报错“x is not defined”,在函数内未使用var声明的变量自动变为全局变量
  4. var会致使变量提高(预编译),const和let定义的变量则不会

问:var到底有什么问题,以致于ES6中须要提供const和let来定义变量?bash

1.解决ES5使用var初始化变量时会出现的变量提高问题函数

案例1:变量定义前置ui

console.log('var: ',a1); // undefined
console.log('let: ',a2); // 报错
var a1 = '这是var变量';
let a2 = '这是let变量';复制代码

打印结果:lua


缘由:程序在检查完语法问题以后,会进行预编译,预编译就是在执行代码会把全部的变量声明和函数声明预先处理,换句话说就是将函数以及变量先提出来,var定义的变量会提出来,并赋值undefined,函数会提出但不传值,不执行。可是let定义的变量不动。因此上面的代码在预编译的时候就等价于:spa

var a1;
console.log(a1); // undefined
console.log(a2); // 报错
a1 = '这是var变量';
let a2 = '这是let变量';复制代码

案例2:内层变量覆盖外层变量.net

var a = 'outerA';
function fn(){
    console.log(a); // undefined;
    var a = 'innerA'; 
    console.log(a);
}复制代码


我记得对于外面定义的变量,函数内部是能够访问的,为何到了上面的例子,第一个打印的a确实undefined呢?3d

缘由是由于在函数内部又定义了一个a变量,内层变量在预编译的时候,提高到了函数顶部,至关于:code

var a = 'outerA';
function fn(){
    var a ;
    console.log(a); // undefined;
    console.log(window.a); // outerA
    a = 'innerA'; // 单纯的赋值
    console.log(a);
}复制代码

案例3:函数提高优先于变量提高

fn1();
function fn1(){
    console.log(a);
}
var a = 'outerA';复制代码


函数执行了,可是获取到的a是undefined,上面代码就至关于:

function fn1(){
    console.log(a);
}
var a;
fn1();
a = 'outerA';复制代码

那么怎么才能获取到a呢?很简单,函数在变量赋值以后调用就好了呀~

2. var在for和if这种块级做用域中,变量会泄露

咱们来看一个很是经典的循环例子:

经过循环,将计算a数组中每一个数字平方的函数一一对应存储在aFn这个数组中,记住,存的是函数,不是结果值。

var a = [0,1,2,3,4,5,6,7,8,9,10]
var aFn = [];
for (var i = 0; i < 10; i++) {
    aFn[i] = function() {
        return a[i]**2;
    } 
}
//执行aFn中函数
for (var j = 0; j < 10; j++) {
    console.log(aFn[j]());
}复制代码

结果:


缘由:上面案例1咱们已经提到过,程序在执行以前会进行预编译,函数声明提出来,不传值不执行,函数自身的做用域在这一个阶段已经产生了。因为var定义的变量,没有块做用域的概念,因此循环中的i处于全局做用域。当咱们调用aFn[j](),正式执行函数的时候,其实i的值都是10,以致于结果都是aFn数组中的每个函数执行的结果都是100。那咱们来看看let定义的结果:

var a = [0,1,2,3,4,5,6,7,8,9,10]
var aFn = [];
for (let i = 0; i < 10; i++) {
    aFn[i] = function() {
        return a[i]**2;
    }
    
}
//执行aFn中函数
for (var j = 0; j < 10; j++) {
    console.log(aFn[j]());
}复制代码

结果:


缘由:函数在预编译的时候已经肯定本身的做用域了,由于let是块级做用域,每一个循环至关于定义了属于本身的块级做用域,无论何时执行,函数取得的i都是各自块内部的值。

上面的示例只是为了讲解var存在的问题,以及let和块级做用域在程序中解决了一个什么样的问题(变量泄露),固然若是单从结果而言,打出aFn这样一个结果列表,彻底不必将很差控制的函数做为数组项存储,当即执行函数存结果,遍历结果就好了,这里切不要纠结这种问题。

var a = [0,1,2,3,4,5,6,7,8,9,10]
var aFn = [];
for (var i = 0; i < 10; i++) {
    aFn[i] = function() {
        return a[i]**2;
    }()
    
}
//执行aFn中函数
for (var j = 0; j < 10; j++) {
    console.log(aFn[j]);
}复制代码

接下来简单说一下const这个关键字

const用来定义常量,使用时必须初始化(即必须赋值),只能在块做用域里访问,并且不能修改。const 和 let 的做用域是一致的,不一样的是 const 变量一旦被赋值,就不能再改变了;

这里说的不可变是指数据的内存地址,对于基本数据类型的数据来讲,数据自己是存在栈内存的,因此const定义的变量就至关于一个常量,不可修改。可是对于引用数据类型(函数、数组、对象)来讲,只要是自己内存地址不变,里面数据的修改是能够的。

常量:

const name = 'moose';
name = 'Lavar';复制代码

结果:


引用数据类型:

const moose = {
	id: '0001',
	age: '18'
}
moose.id ='0002';
console.log(moose.id);复制代码

结果:


到此为止,咱们大致的梳理了var、let以及const三个变量定义关键字相关的内容

特别感谢提供相关内容参考:

    www.cnblogs.com/ykli/p/9681…

    blog.csdn.net/recoluan/ar…

附:感谢您的阅读,但愿对您有所帮助。若是以上内容中存在疑问和错误,欢迎留言或者私信。

相关文章
相关标签/搜索