面试官常问之说说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区别内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2022-03-08

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变量声明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;

深入浅析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中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)只要

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

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.

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定义的变量可以修改,

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 正式通过,成为国际标准

详解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如何在跨域请求中携带cookie

目录 1.搭建环境 2.测试同源cookie 3.跨域请求携带cookie 4.总结 5.知识点 1. 搭建环境 1.生成工程文件 npm init 2.安装 express npm i express --save 3.新增app1.js,开启服务器1 端口:3001 const express = require('express') const app = express() const port = 3001 // 设置`cookie` app.get("/login", (r

详解javascript中的Strict模式

简介 在ES5中,引入了strict模式,我们可以称之为严格模式.相应的sloppy mode就可以被称为非严格模式. 严格模式并不是非严格模式的一个子集,相反的严格模式在语义上和非严格模式都发生了一定的变化,所以我们在使用过程中,一定要经过严格的测试.以保证在严格模式下程序的执行和非严格模式下的执行效果一致. 使用Strict mode strict mode会改变javascript的一些表现,我们将会在下一节中进行详细的讲解. 这里先来看一下,怎么使用strict mode. Strict

详解Javascript中prototype属性(推荐)

在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不存在类(Class)的概念的,javascript中不是基于'类的',而是通过构造函数(constructor)和原型链(prototype chains)实现的.但是在ES6中提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板.通过class关键字,可以定义类.基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能

JavaScript中的普通函数和箭头函数的区别和用法详解

最近被问到了一个问题: javaScript 中的箭头函数 ( => ) 和普通函数 ( function ) 有什么区别? 我当时想的就是:这个问题很简单啊~(flag),然后做出了错误的回答-- 箭头函数中的 this 和调用时的上下文无关,而是取决于定义时的上下文 这并不是很正确的答案--虽然也不是完全错误 箭头函数中的 this 首先说我的回答中没有错误的部分:箭头函数中的 this 确实和调用时的上下文无关 function make () { return ()=>{ consol

JavaScript中ES6规范中let和const的用法和区别

ES6-- let 和 const 命令 引言 本文主要讲解ES6中变量的相关操作,变量的命名, 讲解 var. let . const 三者的区别 正文 ES6中的 let 和 const 都是用来声明变量的, 他们与 var 有所区别 let 命令 我们都知道在for循环中,我们命名的变量 i 一般都只是为了在这个循环中使用,才临时命名的, 我们希望循环结束后,这个变量就消失, 但是却相反,用 var 命名的变量,在 for 循环结束后并不会销毁,而会存在于全局中. for(var i=0;

详解javaScript中Number数字类型的使用

目录 前言 Number数字 自带属性值 基础使用 总结 源码地址 前言 Number和Math都属于JavaScript中的内置对象,Number数字类型作为基础数据类型,我们在开发过程中会经常用到,包括数字精度的格式化,还有字符串转换成数字等操作. Number数字 自带属性值 Number.EPSILON 两个可表示(representable)数之间的最小间隔. Number.MAX_SAFE_INTEGER JavaScript 中最大的安全整数 (2^53 - 1). Number.

详解JavaScript中的4种类型识别方法

具体内容如下: 1.typeof [输出]首字母小写的字符串形式 [功能] [a]可以识别标准类型(将Null识别为object) [b]不能识别具体的对象类型(Function除外) [实例] console.log(typeof "jerry");//"string" console.log(typeof 12);//"number" console.log(typeof true);//"boolean" console

详解Javascript 中的 class、构造函数、工厂函数

到了ES6时代,我们创建对象的手段又增加了,在不同的场景下我们可以选择不同的方法来建立.现在就主要有三种方法来构建对象,class关键字,构造函数,工厂函数.他们都是创建对象的手段,但是却又有不同的地方,平时开发时,也需要针对这不同来选择. 首先我们来看一下,这三种方法是怎样的 // class 关键字,ES6新特性 class ClassCar { drive () { console.log('Vroom!'); } } const car1 = new ClassCar(); consol

详解Javascript中new()到底做了些什么?

前言 和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 new 来产生对象? 本文将带你一起来探索 javascript 中 new 的奥秘... 要创建 Person 的新实例,必须使用 new 操作符. 以这种方式调用构造函数实际上会经历以下 4个步骤: (1) 创建一个新对象: (2) 将构造函数的作用域赋给新对象(因此 this 就指向了