资讯专栏INFORMATION COLUMN

javascript基础var、 let、const

lindroid / 2015人阅读

摘要:全局作用域在最顶层声明的变量成为全局变量,全局变量拥有全局作用域,它们在程序的任何地方都是能够被访问到。作用域的主要作用是能够控制变量是使用范围。程序将会被中断,这个特性被称为暂存死区。

1. 变量声明、初始化

Javascript中使用一个变量之前需要先声明,我们可以使用var、let、const来声明一个变量。如果在给声明的变量指定初始值,就是初始化。如:

var a = 1; 
let b =1; 
const c = "123"

初始化后,这个变量就在内存中分配了空间,后面的程序就可以调用了。var、let、const都可以声明变量,但是各自又不相同。在ES6中,const 定义的变量是必须要初始化赋值,而且以后不能变更, 是一个固定值。而像var,let是可以只声明,但是不进行初始化。

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

而在我们熟知java代码中,这样只声明,但是未赋值是会报错的

int a;
System.out.print(a); // The local variable a may not have been initialized

这是因为javascript的执行引擎(例如V8)会在运行这段代码之前检查会这段代码, 也被称为预编译。发现有var和let声明的变量,会给这个变量提供一个默认的初始化值undefined。也正是因为要检查代码,所以像const所声明的变量必须初始化、let,const所声明的变量不能重复声明。都是在检查代码时候,抛出错误。

const 必须初始化

console.log("hello world")
const a  // a 必须初始化,如果不初始化将会报 Missing initializer in const declaration


这段代码在执行的时候,没有先打印"hello world", 而是直接报错。我这里理解是代码检查的时候,就已经把错误抛出来,程序没有运行.

let声明后的变量不能重复声明

let b = 1; 
console.log("hello world"); 
let b =2;


这段代码在执行的时候,没有先打印"hello world", 而是直接报错。我这里理解是代码检查的时候,就已经把错误抛出来,程序没有运行.

2. 作用域

一个变量的作用域是源程序代码中定义这个变量的区域。

全局作用域:在最顶层声明的变量成为全局变量,全局变量拥有全局作用域,它们在程序的任何地方都是能够被访问到。

函数作用域:在函数中声明的变量只能在函数中被访问到,函数外面是访问不到的。

块级作用域:任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

作用域的主要作用是能够控制变量是使用范围。

在Javascript中,var声明的变量使用的是函数作用域,而let声明的变量使用的块级作用域。ES5中已经存在var来声明变量,那为啥到ES6还要使用let来声明变量?我个人觉得是能够更好的规范我们程序代码,避免出现出现一些我们所无法预料的错误。
经典例子如下:

var arr = [];
for(var i = 0; i < 10; i++) {
    var j = i;
    arr[i] = function() {
        console.log(j)
    }
}
arr[0](); // =>9 而我们预想的是0

而使用let就能避免出现这样的问题

var arr = [];
for(var i = 0; i < 10; i++) {
    let j = i;
    arr[i] = function() {
        console.log(j)
    }
}
arr[0]() // =>0

这是因为let声明的变量属于块级作用域,上面let所声明的变量j是只能在for循环每一个{}中能被访问。大致流程如下
当 i = 0,大致生成这样一个块

{
   let j = 0;  
   arr[i] = function() {
       console.log(j)
   }
}

当i = 1

{
  let j = 1;
  arr[i] = function() {
     console.log(j)
  }
}

. . . . . .
当i = 9

{
  let j = 10;
  arr[i] = function() {
     console.log(j)
  }
}

当后面去调用arr[0]()这个方法, 这是第一个块定义的方法, 因为let声明的变量j是块{}作用域, 这个方法就只能访问第一个块{}中j的值,也就是0;
而当我们使用var的时候
当 i = 0

 {
   var j = 0;  
   arr[i] = function() {
      console.log(j)
   }
}

当 i = 1

{
  var j = 1;
  arr[i] = function() {
     console.log(j)
  }
}

当我们调用arr[0]()这个方法, 是第一个块定义的方法,因为var声明的变量是函数作用域,
所以上面的程序,可以理解为

var j; // var 声明的变量具有声明提前的作用
var arr = [];
{
    j = 0;
    arr[i] = function() {
        console.log(j)
    }
}
{
    j = 1;
    arr[i] = function() {
        console.log(j)
    }
}
**. . . . . .**
{
    j = 9;
    arr[i] = function() {
        console.log(j)
    }
}
arr[0](); // =>9 

arr[0]()运行的时候,由于没有作用域的限制,j已经被赋值为9。所以输出结果就是9

有人会疑问,为啥不能直接使用i,而是使用中间变量j去替换?

这里是为了让大家更好的理解,下面直接使用i

当使用var声明变量
var arr = [];
for(var i = 0 ; i < 10; i++) {
   arr[i] = function() {
       console.log(i);
  }
}
arr[0](); // => 10
换成let声明变量
var arr = [];
for(let i = 0 ; i < 10; i++) {
   arr[i] = function() {
       console.log(i);
  }
}
arr[0](); // => 0 这是正常的

这样也是正常的符合预期,但是当我把代码做了修改后

var arr = []; let i;
for(i = 0 ; i < 10; i++) {
   arr[i] = function() {
       console.log(i);
  }
}
arr[0](); // => 10 

这里也是let声明的变量i,但是输出的结果是10,这里我看完后很不理解。于是去阅读了ES的规范 http://www.ecma-international...

这里是被认为第一种情况。let声明的i和var声明i在上面的代码执行的逻辑是一样的,就导致上面的情形。这里小伙伴们需要特别注意。

3. let暂存死区,var声明提前

先从一个简单的示例开始吧
执行代码:

console.log(a); 

浏览器运行结果如下:

执行代码:

if (false) {var b = 1}; 
console.log(b);

浏览器运行结果如下:

执行代码:

if (false) {let c = 1}; console.log(c); 

浏览器运行结果如下:

第一种情形下: 当我们直接在浏览器输入未声明的变量a, 有报 a is not defined的错误。
第二种情形下: b => undefined
第三种情形下: c is not defined
第二种情形下,变量在声明之前就已经能够被使用,这种特性被称非官方的称为声明提前。变量能够提前到哪里呢?所有使用var声明的变量被提前至函数顶部。
于是面试中常用这样的问题出现

var global = "global"
function test () {
    console.log(global);
    var global = "local";
    console.log(global);
}
test() // undefined, local

这是因为在test函数中,var声明的global被提前到test函数顶部,注意声明提前是在预编译时候执行的,并且给global初始化赋值undeifined。所以实际执行的逻辑,应该是这样

var global = "global";
function test() {
    var global = undefined;
   console.log(global);
  global = "local";
  console.log(global);
}
test(); //undefined, local

变量声明提前好不好,很明显,这样很不好。一个变量还没有声明就能够被使用,太奇怪了,也太危险了。尤其是那些习惯了java、c的人看起来,这就是一个异类的特性。
所以,在ES6中,就推荐使用了let来声明变量。

作用域内 let 声明的变量不能在声明之前被使用,如果发现在let声明的变量在声明之前被使用,将会抛出 is not defined的错误。程序将会被中断,这个特性被称为暂存死区
关于暂存死区,我没有看过V8的源码,不过有个大胆的猜想

let 变量声明的作用域内,javaScript引擎会把let声明的变量名收集起来。当程序开始执行的时候,代码一行行的往下执行,引擎获取变量之前,首先会判断此变量的变量名是否是在let声明的变量集合中。不存在,程序就继续往下执行。如果存在,就判断变量名的上一个字符串是不是let。如果是let,就把此变量名从let声明的集合中去掉,如果不是就抛出错误,程序被中断。
(纯属于个人猜想,方便理解,大家不作为参考)

下面两个小例子用于理解暂存死区
console.log("hello world"); c = 1; let c;

这里是先打印"hello world",然后再进行报错,说明是程序在执行到 c = 1的时候报错。
是运行的错误。而大家可以对比前面的let声明同一个变量多次,是程序还没有执行就报错。所以这里是运行中的错误。可以推断出变量暂存死区是发生程序运行中产生的,let声明的变量不能在声明之前使用,发现有声明之前使用的,就会抛出错误,中断程序。

另外一个程序

let x = x; 
// x is not defined
x
// x is not defined
let x
// Uncaught SyntaxError: Identifier "x" has already been declared


let x = x, 赋值从右往左执行,所以先获取变量 x, 于是x陷入暂存死区。抛出x is not defined的错误,程序中断。后面再获取x,还是抛出x is not defined,x还是陷入暂存死区。后面的let x,抛出Uncaught SyntaxError: Identifier "x" has already been declared 错误是因为检查这段代码的时候,发现let x 已经被声明,于是抛出错误。不能被重复声明了。

总结 let特性大致就几条 1. let 声明的变量的作用域是块级的; 2. let 不能重复声明已存在的变量; 3. let 有暂时死区,不会被提升; const特性 1. let的特性它基本都有 2. const必须初始化赋值 var特性 1. var 声明的变量的作用域是函数作用域; 2. var 能够重复声明变量; 3. var 声明的变量能够声明提前

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93066.html

相关文章

  • Javascript基础之-varletconst深入解析(二)

    摘要:规范对其是这样进行的描述的。声明定义了在正在运行的执行上下文作用域内的变量环境中的变量。在执行时,由带有的定义的变量被赋其设定项的的值。由于变量已经被声明,是可访问的,因此会打印出正确的结果。 你想在在变量声明之前就使用变量?以后再也别这样做了。 新的声明方式(let,const)较之之前的声明方式(var),还有一个区别,就是新的方式不允许在变量声明之前就使用该变量,但是var是可以...

    XiNGRZ 评论0 收藏0
  • JavaScript基础系列---变量及其值类型

    摘要:但对于引用类型的数据主要是对象和数组,变量指向的内存地址,保存的只是一个引用地址指针,只能保证这个引用地址指针是固定的,至于它指向的堆内存中的存储的值是不是可变的,就完全不能控制了。 基础概念 变量是存储信息的容器,这里需要区分一下:变量不是指存储的信息本身,而是指这个用于存储信息的容器,可以把变量想象成一个个用来装东西的纸箱子 变量需要声明,并且建议在声明的同时进行初始化,如下所...

    sugarmo 评论0 收藏0
  • 快速掌握JavaScript面试基础知识(一)

    摘要:根据调查,自年一来,是最流行的编程语言。在一个函数体中声明的变量和函数,周围的作用域内无法访问。也就是说被大括号包围起来的区域声明的变量外部将不可访问。一个常见的误解是使用声明的变量,其值不可更改。 译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer ...

    acrazing 评论0 收藏0
  • javascript基础学习一(变量)

    var 用var申明一个变量: var a = 1; console.log(a) // 1 console.log(a) // undefined var a = 1; js的申明过程: var a; // undefined,只申明,不赋值。会有个默认值undefined a = 1 // 1 例子: var a = 1; var a; console.log(a) // 1, 对重复定义且未...

    Carbs 评论0 收藏0
  • 【重温基础】1.语法和数据类型

    摘要:语法和数据类型正文开始本章节复习的是中的基本语法,变量声明,数据类型和字面量。声明一个块作用域的局部变量,可赋一个初始值。变量声明有三种方式如,声明局部变量和全局变量。 最近开始把精力放在重新复习JavaScript的基础知识上面,不再太追求各种花枝招展的前端框架,框架再多,适合实际项目才是最重要。 上星期在掘金发布了几篇文章,其中最大块算是 【复习资料】ES6/ES7/ES8/ES...

    Darkgel 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<