面试官常问之说说js中var、let、const的区别

前言

关于 var、let 和 const 三个关键字的区别,是一个老生常谈的问题,也是经典的面试题。本篇文章将全面讲解三者的特性,以及它们之间的区别,由浅入深让你彻底搞懂这个知识点。

变量声明

ECMAScript 变量是松散类型的,意思就是变量可以用于保存任何类型的数据,每个变量只不过是一个用于保存任意值的命名占位符。

有3个关键字可以声明变量:var、let和const,var在 ECMAScript 的所有版本中都可以使用,而let和const只能在 ES6 及更晚的版本中使用。

var

要定义变量,可以使用var操作符(注意 var 是一个关键字),后跟变量名(即标识符,如前所述):

var message;

上面这行代码定义了一个名为message的变量,它可以保存任何类型的值。不初始化的情况下,变量会保存一个特殊值undefined。

ECMAScript 实现变量初始化,因此可以同时定义变量并设置它的值:

var messgae = "hi";

message被定义为一个保存字符串值 "hi" 的变量。像这样初始化变量不会将它标识为字符串类型,只是一个简单的赋值而已。随后,不仅可以改变保存的值,还可以改变值的类型:

var message = "hi";
message = 666; // 合法,但不推荐
console.log(message); // 666

如果需要定义多个变量,可以在一条语句中用逗号分隔每个变量(及可选的初始化):

var message,
    name = "孙悟空",
    age = 18;

作用域

使用var操作符定义的变量会成为包含它的函数的局部变量。例如,使用var在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁:

function test() {
    var message = "hi"; // 局部变量
}
test();
console.log(message); // error: message is not defined

不过,在函数内部定义变量时省略var操作符,可以创建一个全局变量:

function test() {
    message = "hi"; // 全局变量
}
test();
console.log(message); // ok

虽然可以通过省略var操作符定义全局变量,但不推荐这么做。在严格模式下,如果像这样给未声明的变量赋值,会报错。

在同一个作用域内,反复多次使用var声明同一个变量也是没问题的:

function test() {
    var message = "hi";
    var message = false;
    var message = 666;

    console.log(message);
}
test(); // 666

提升

所谓的提升,就是把所有变量声明都拉到函数作用域的顶部。注意,只提升声明,不提升赋值操作。

举个栗子:

function test() {
    console.log(message);
    var message = "hi";
}
test(); // undefined

上面的代码不会报错,因为使用var关键字声明的变量会提升到函数作用域的顶部,跟下面的代码是等价的:

function test() {
    var message;
    console.log(message);
    message = "hi";
}
test(); // undefined

let

let 和 var 的作用差不多,但有着非常重要的区别。

作用域

var声明的范围是函数作用域,而let声明的范围是块作用域:

if (true) {
    var message = "hi";
    console.log(message); // hi
}
console.log(message); // hi

if (true) {
    let message = "hi";
    console.log(message); // hi
}
console.log(message); // error: message is not defined

message变量之所以不能在 if 块外部被引用,是因为它的作用域仅限于该块内部。块作用域是函数作用域的子集,因此适用于var的作用域限制同样也适用于let。

let 不允许同一个块作用域中出现冗余声明:

if (true) {
    // error: 无法重新声明块范围变量“a”
    let a;
    let a;
}

JS 引擎会记录用于变量声明的标识符及其所在的块作用域,因此嵌套使用相同的标识符不会报错,这是因为同一个块中没有重复声明:

let a = 666;
console.log(a); // 666
if (true) {
    let a = '啊哈哈';
    console.log(a); // 啊哈哈
}

var和let声明的并不是不同类型的变量,它们只是指出变量在相关作用域如何存在,所以对声明冗余报错不会因混用var和let而受影响:

// error
var a;
let a;

// error
let b;
var b;

暂时性死区

let声明的变量不会在作用域中被提升:

if (true) {
    console.log(x); // error: 在赋值前使用了变量“x”
    let x = 520;
}

在解析代码时,JS 引擎也会注意出现在块后面的let声明,只不过在此之前不能以任何方式来引用未声明的变量。在let声明之前的执行瞬间被称为暂时性死区,在此阶段引用任何后面才声明的变量都会报错。

const

const的行为与let基本相同,唯一一个重要的区别是:用const声明变量时必须同时初始化变量,且尝试修改const声明的变量会导致运行时报错:

const a; // error: 必须初始化 "const" 声明

const b = 250;
b = 520; // error: 无法赋值给 "b" ,因为它是常数

注意,const声明的限制只适用于它指向的变量的引用,换句话说,如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const限制:

const obj = { x: 666 };
obj.x = 888; // ok
obj.y = '啊哈哈'; // ok

扩展

全局声明

使用var在全局作用域中声明的变量会成为window对象的属性,let和const声明的变量则不会:

var a = 666;
console.log(window.a); // 666

let b = 666;
console.log(window.b); // undefined

const c = 666;
console.log(window.c); // undefined

for 循环中的 let 声明

先看个:

for (var i = 0; i < 5; i++) {
    ...
}
console.log(i);

思考一下,打印结果会是什么?

由于var声明的变量没有块作用域,所以迭代变量i会渗透到循环体外部,当i递增到5时退出循环,打印出结果为5。

如果用let声明迭代变量,就可以把迭代变量的作用域限制为 for 循环块内部:

for (let i = 0; i < 5; i++) {
    ...
}
console.log(i); // error: i is not defined

再看个栗子:

for (var i = 0; i < 5; i++) {
    setTimeout( () => {
        console.log(i);
    }, 0 )
}

你可能以为会输出:0、1、2、3、4,实际上会输出:5、5、5、5、5。

之所以是这样的结果,是因为在退出循环时,迭代变量保存的是导致循环退出的值,也就是 5。在之后异步执行超时逻辑时,所有的i都是同一个变量,因此输出的都是同一个最终值。

而在使用let声明迭代变量时,JS 引擎在后台会为每个迭代循环声明一个新的迭代变量,每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值:

for (let i = 0; i < 5; i++) {
    setTimeout( () => {
        console.log(i); // 0、1、2、3、4
    }, 0 )
}

这是一道经典面试题,不是很理解的话一定要多看几遍,最好动手实践一下,彻底搞懂为止。

总结

  • var 声明的范围是函数作用域,let 和 const 声明的范围是块作用域
  • var 声明的变量会被提升到函数作用域的顶部,let 和 const 声明的变量不存在提升,且具有暂时性死区特征
  • var 允许在同一个作用域中重复声明同一个变量,let 和 const 不允许
  • 在全局作用域中使用 var 声明的变量会成为 window 对象的属性,let 和 const 声明的变量则不会
  • const 的行为与 let 基本相同,唯一一个重要的区别是,使用 const 声明的变量必须进行初始化,且不能被修改

声明风格及最佳实践

ECMAScript 6 增加 let 和 const 从客观上为这门语言更精确地声明作用域和语义提供了更好的支持,行为怪异的 var 所造成的问题,已经让 JS 社区为之苦恼了很多年。随着这两个关键字的出现,新的有助于提升代码质量的最佳实践方式也逐渐显现。

var 已经被时代所抛弃,不建议再使用。限制自己只使用 let 和 const 有助于提升代码质量,因为变量有了明确的作用域、声明位置以及不变的值。

优先使用 const,let 次之。使用 const 声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。只在提前知道未来会有修改时,才使用 let。这样可以让开发者更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为。

到此这篇关于ja中var、let、const区别的文章就介绍到这了,更多相关var、let、const区别内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript变量声明var,let.const及区别浅析

    var声明变量的作用域限制在其声明位置的上下文中 var x = 0; // x是全局变量,并且赋值为0. console.log(typeof z); // undefined,因为z还不存在. function a() { // 当a被调用时, var y = 2; // y被声明成函数a作用域的变量,然后赋值成2. console.log(x, y); // 0 2 function b() { // 当b被调用时, x = 3; // 全局变量x被赋值为3,不生成全局变量. y = 4;

  • JavaScript中var、let、const区别浅析

    下面通过代码给大家介绍JavaScript中var.let.const区别,具体代码如下所述: //1.var定义的变量可以修改,如果不初始化会输出undefined,不会报错. var a; console.log(a); //undefined //2.let是块级作用域,函数内部使用let定义后,对函数外部无影响. let c = 3; console.log(c) function change(){ let c = 6; console.log(c) } change(); (1)只要

  • 详解javascript中var与ES6规范中let、const区别与用法

    随着ES6规范的到来,Js中定义变量的方法已经由单一的 var 方式发展到了 var.let.const 三种之多.var 众所周知,可那俩新来的货到底有啥新特性呢?到底该啥时候用呢?下面就是小编总结出的关于javascript中var与ES6规范中let.const区别详解 我们先来絮叨絮叨 var 方式定义变量有啥 bug ? Js没有块级作用域 请看这样一条规则:在JS函数中的var声明,其作用域是函数体的全部. for(var i=0;i<10;i++){ var a = 'a'; }

  • javascript中let和var以及const关键字的区别

    1.声明后未赋值,表现相同 //一个例子 'use strict'; (function() { var varTest; let letTest; console.log(varTest); //输出undefined console.log(letTest); //输出undefined }()); 2.使用未声明的变量,表现不同 //一个例子 (function() { console.log(varTest); //输出undefined(注意要注释掉下面一行才能运行) console.

  • JS变量中有var定义和无var定义的区别以及es6中let命令和const命令

    之前我们在写js代码的时候都知道可以用var定义全局变量和局部变量,也可以省略var,而且在非严格模式下不会报错,但是并不知道两者的区别... var x = 1; y = 4; console.log(x);//1 console.log(y);//4 console.log(window.x);//1 console.log(window.y);//4 简单测试下可以知道定义的x和y都被挂载在window对象上,变为window下的属性,这并不能说明什么... delete x; delet

  • 5分钟快速掌握JS中var、let和const的异同

    前言 首先,一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系? ECMAScript是一个国际通过的标准化脚本语言.JavaScript由ECMAScript和DOM.BOM三者组成.可以简单理解为:ECMAScript是JavaScript的语言规范,JavaScript是ECMAScript的实现和扩展. 2011 年,ECMAScript 5.1 版发布.之前我们大部分人用的也就是ES5 2015 年 6 月,ECMAScript 6 正式通过,成为国际标准

  • 深入浅析var,let,const的异同点

    一.let和var区别 1.关于变量提升,var能变量提升,let不能 // 关于var 如下所示 console.log(a); //输出undefined,此时就是变量提升 var a = 2; console.log(a); //2 //相当于下面的代码 var a; //声明且初始化为undefined console.log(a); //输出undefined a=2; //赋值 console.log(a); //2 // 关于let 如下所示 console.log(a); //

  • JavaScript ES6中const、let与var的对比详解

    ECMAScript 6 新增 const 和 let 命令,用来声明变量. 声明方式 变量提升 作用域 初始值 重复定义 const 否 块级 需要 不允许 let 否 块级 不需要 不允许 var 是 函数级 不需要 允许 变量提升:const 和 let 必须先声明再使用,不支持变量提升 console.log(c1, l1, v1); // 报错 // Uncaught ReferenceError: c1 is not defined const c1 = 'c1'; let l1 =

  • JavaScript中const、var和let区别浅析

    在JavaScript中有三种声明变量的方式:var.let.const.下文给大家介绍js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始化. const b = 2;//正确 // const b;//错误,必须初始化 console.log('函数外const定义b:' + b);//有输出值 // b = 5; // console.log('函数外修改const定义b:' + b);//无法输出 2.var定义的变量可以修改,

  • 面试官常问之说说js中var、let、const的区别

    前言 关于 var.let 和 const 三个关键字的区别,是一个老生常谈的问题,也是经典的面试题.本篇文章将全面讲解三者的特性,以及它们之间的区别,由浅入深让你彻底搞懂这个知识点. 变量声明 ECMAScript 变量是松散类型的,意思就是变量可以用于保存任何类型的数据,每个变量只不过是一个用于保存任意值的命名占位符. 有3个关键字可以声明变量:var.let和const,var在 ECMAScript 的所有版本中都可以使用,而let和const只能在 ES6 及更晚的版本中使用. var

  • Java多线程面试题(面试官常问)

    进程和线程 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的.系统运行一个程序即是从一个进程从创建.运行到消亡的过程.在Java中,当我们启动main函数时其实就是启动了一个JVM的进程,而mian函数所在的线程就是这个进程中的一个线程,称为主线程. 线程是比进程更小的执行单位.一个进程在其执行的过程中可以产生多个线程.与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程都有自己的程序计数器.虚拟机和本地方法栈,所以系统在产生一个线程,或在各个线程之间切换工作是,

  • SpringBoot服务监控机制原理解析(面试官常问)

    前言 任何一个服务如果没有监控,那就是两眼一抹黑,无法知道当前服务的运行情况,也就无法对可能出现的异常状况进行很好的处理,所以对任意一个服务来说,监控都是必不可少的. 就目前而言,大部分微服务应用都是基于 SpringBoot 来构建,所以了解 SpringBoot 的监控特性是非常有必要的,而 SpringBoot 也提供了一些特性来帮助我们监控应用. 本文基于 SpringBoot 2.3.1.RELEASE 版本演示. SpringBoot 监控 SpringBoot 中的监控可以分为 H

  • 面试官常问React的生命周期问题

    React的生命周期 两张图带你理解 React的生命周期 React的生命周期(旧) class Life extends React.Component{ // 构造器 constructor(props){ console.log('Life构造器---constructor'); super(props) this.state={num:0} } // 计算+1功能 add=()=>{ const {num} = this.state this.setState({num:num+1})

  • Web面试常问回流reflow与重绘repaint原理及区别

    目录 浏览器的渲染机制 回流 与 重绘 回流 导致回流的操作: 重绘 导致重绘的操作: 浏览器的渲染机制 1.浏览器采用两个引擎来处理页面的工作, 不同的浏览器使用的渲染引擎不一样 渲染引擎: Chrom和Safari使用"WebKit", Firefor使用"Geoko" js引擎 2.浏览器会把html解析成 DOM树, 把css解析成 CSSOM(CSS对象模型); 3.接着会把 DOM树 和 CSSOM, 结合产生 render tree(渲染树); 4.渲

  • 面试最常问的13种Vue修饰符

    目录 1.lazy 2.trim 3.number 4.stop 5.capture 6.self 7.once 8.prevent 9.native 10.left,right,middle 11.passive 12.camel 12.sync 13.keyCode 1.lazy lazy修饰符作用是,改变输入框的值时value不会改变,当光标离开输入框时,v-model绑定的值value才会改变 <input type="text" v-model.lazy="v

  • node.js中grunt和gulp的区别详解

    node.js中grunt和gulp的区别详解 自nodeJS登上前端舞台,自动化构建变得越来越流行.目前最流行的当属grunt和gulp,这两个光看名字挺像,功能也差不多,不过gulp能在grunt这位大哥如日中天的境况下开辟出自己的一片天地,有着她独到的优点. 易用 Gulp相比Grunt更简洁,而且遵循代码优于配置策略,维护Gulp更像是写代码. 高效 Gulp相比Grunt更有设计感,核心设计基于Unix流的概念,通过管道连接,不需要写中间文件. 高质量 Gulp的每个插件只完成一个功能

  • 基于js中this和event 的区别(详解)

    今天在看javascript入门经典-事件一章中看到了 this 和 event 两种传参形式.因为作为一个初级的前端开发人员平时只用过 this传参,so很想弄清楚,this和event的区别是什么,什么情况下用什么比较合适. onclick = changeImg(this)       vs     onclick = changeImg(event) <img src='usa.gif' onclick="changeImg(event)" /> <scrip

  • JS中innerHTML和pasteHTML的区别实例分析

    本文实例讲述了JS中innerHTML和pasteHTML的区别.分享给大家供大家参考,具体如下: innerHTML 是一个属性,可以取得或者设定该元素内的 HTML 内容,可以是任意能包含 HTML 子节点的元素都使用它 pasteHTML()是一个方法,在指定的文字区域内替换该区域内的文本或者HTML,该方法必须应用于一个 createTextRange() 或者 document.selection.createRange() 创建的区域上 例: <Script Language="

  • 浅谈JS中String()与 .toString()的区别

    我们知道String()与 .toString()都是可以转换为字符串类型,但是String()与 .toString()的还是有区别的 1..toString()可以将所有的的数据都转换为字符串,但是要排除null 和 undefined 例如将false转为字符串类型 <script> var str = false.toString(); console.log(str, typeof str); </script> 返回的结果为 false,string 看看null 和

随机推荐