当前位置: 首页 > news >正文

JavaScript 变量声明var、let、const

在 JavaScript 中,varletconst是用于声明变量的关键字。
letconst是JavaScript里相对较新的变量声明方式。
let用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
对于const来说,只声明不赋值,就会报错。
示例:

const PI = 3.1415;
console.log(PI) // 3.1415PI = 3; // Uncaught TypeError: Assignment to constant variable.const a; // Uncaught SyntaxError: Missing initializer in const declaration

varletconst 的区别:

重复声明

  • var:可以重复声明同一个变量。
  • letconst:不允许在同一作用域内重复声明同一个变量。
var a = 1;
var a = 2;    // 不会报错let b = 3;
let b = 4;    // Uncaught SyntaxError: Identifier 'b' has already been declaredconst c = 5;
const c = 6;  // Uncaught SyntaxError: Identifier 'c' has already been declared

变量提升

  • var:存在变量提升,即在变量声明之前使用该变量,不会报错,只是值为 undefined
    当函数开始的时候,就会处理 var 声明(脚本启动对应全局变量)。
    换言之,var 声明的变量会在函数开头被定义,与它在代码中定义的位置无关(这里不考虑定义在嵌套函数中的情况)。
  • letconst:不存在变量提升,在声明之前使用会报错。

使用 var 声明,示例:

console.log(a);  // 输出 undefined
var a = 10;// 上面代码的实际执行顺序如下:
// var a;
// console.log(a);
// a = 10

使用var声明变量 a ,会发生变量提升,即脚本开始运行时,变量 a 已经存在了,但是没有值,所以会输出undefined

使用letconst声明,示例:

console.log(b);  // Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 20;console.log(c) // Uncaught ReferenceError: Cannot access 'c' before initialization
const c = 30;message = "message";  // Uncaught ReferenceError: Cannot access 'message' before initialization
let message;

从程序执行进入代码块(或函数)的那一刻起,变量bc 就开始进入“未初始化”状态。它一直保持未初始化状态,直至程序执行到相应的 letconst语句。

ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
在代码块内,使用 letconst命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

作用域

  • var:函数作用域或全局作用域。在函数内使用 var 声明的变量,在函数外部无法访问;在函数外部使用 var 声明的变量则是全局变量。
  • letconst:块级作用域。即它们在 {} 内声明的变量,在 {} 外部无法访问。

注意:ES6 的块级作用域必须有大括号{},如果没有大括号,JavaScript 引擎就认为不存在块级作用域。

由于 var 会忽略代码块,因此我们有了一个全局变量 a

if (true) {var a = 10; // 使用 "var" 而不是 "let"
}
console.log( a ); // 10,变量在 if 结束后仍存在

对于循环,var 声明的变量没有块级作用域也没有循环局部作用域:

for (var i = 0; i < 10; i++) {var one = 1;
}
console.log(i);   // 10,"i" 在循环结束后仍可见,它是一个全局变量
console.log(one); // 1,"one" 在循环结束后仍可见,它是一个全局变量

在函数内部,var 声明的变量的作用域将为函数作用域:

function sayHi() {if (true) {var phrase = "Hello";}alert(phrase); // 能正常工作
}
sayHi();
console.log(phrase); // ReferenceError: phrase is not defined

使用 letconst, 该变量仅在if内部可见:

if (true) {let b = 20; // 使用 "let", 该变量仅在if内部可见const c = 30; 
}
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // Uncaught ReferenceError: c is not defined// 没有{},就没有块级作用域。词法声明不能出现在单语句上下文中
if(true) let a = 1;  // Uncaught SyntaxError: Lexical declaration cannot appear in a single-statement context// 必须要用{},才有块级作用域
if(true) { let a = 1; }

对于 ifforwhile 等,使用letconst{...} 中声明的变量也仅在内部可见:

for (let i = 0; i < 3; i++) {// 此时,想在这里输出循环条件的i,报错。// console.log(i); // Uncaught ReferenceError: Cannot access 'i' before initializationlet i = 'abc';console.log(i);  // abc
}

执行第一句console.log(i);报错是因为在循环体内部再次声明了i,并且在let声明之前使用了变量。
for循环还有一个特别之处:设置循环变量的那部分是一个父作用域,循环体内部是一个单独的子作用域。

值的可修改性

  • varlet:声明的变量的值可以修改。
  • const:声明的变量的值不能修改,但如果是对象或数组等引用类型,其属性可以修改。
var a = 10;
a = 20; // 可以修改let b = 30;
b = 40;  // 可以修改const c = 50;
c = 60;  // Uncaught TypeError: Assignment to constant variable.const arr = [1, 2, 3];
// 可以修改数组的元素
arr.push(4); 
console.log(arr);  // [1, 2, 3, 4]
// 将 arr 指向另一个数组,就会报错
arr = [4, 5, 6];  // Uncaught TypeError: Assignment to constant variable.const obj= {};// 为 obj 添加一个属性,可以成功
obj.prop = 123;
console.log(obj.prop); // 123// 将 obj 指向另一个对象,就会报错
obj = {}; // Uncaught TypeError: Assignment to constant variable.

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

如果真的想将对象冻结,应该使用Object.freeze方法。

const obj = Object.freeze({});// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
obj.prop = 123;

常量obj指向一个冻结的对象,所以添加新属性不起作用,严格模式时还会报错。

除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象彻底冻结的函数。

var constantize = (obj) => {Object.freeze(obj);Object.keys(obj).forEach( (key, i) => {if ( typeof obj[key] === 'object' ) {constantize( obj[key] );}});
};
http://www.lryc.cn/news/412025.html

相关文章:

  • ipvlan: operation not supported 导致的POD不断重启
  • 组蛋白乳酸化和RNA甲基化如何联动?请大数据把这个思路推给科研人
  • 操作文件-Path
  • RAC(Teamcenter )开发,Bom行解包和打包的方法
  • log4j2漏洞练习
  • OpenEuler安装部署教程
  • Canto - hackmyvm
  • 【数据结构进阶】手撕红黑树
  • 【C++从小白到大牛】类和对象
  • Kafka 为什么这么快的七大秘诀,涨知识了
  • 一文解决3D车道线检测:最新单目3D车道线检测综述
  • 稳中向好,今年新招6000人
  • 使用kettle开源工具进行跨库数据同步
  • Golang | Leetcode Golang题解之第307题区域和检索-数组可修改
  • Golang | Leetcode Golang题解之第301题删除无效的括号
  • 【Story】《程序员面试的“八股文”辩论:技术基础与实际能力的博弈》
  • 初步了解泛型
  • 【C#】.net core 6.0 webapi 使用core版本的NPOI的Excel读取数据以及保存数据
  • C++推荐的oj网站
  • springmvc处理http请求的底层逻辑
  • 干货满满,从零到一:编程小白如何在大学成为编程大神?
  • 前端-如何通过docker打包Vue服务成镜像并在本地运行(本地可以通过http://localhost:8080/访问前端服务)
  • 零基础学习【Mybatis】这一篇就够了
  • Shell入门(保姆级教学)
  • 【JDK11和JDK8并行与切换】
  • vue大数据量列表渲染性能优化:虚拟滚动原理
  • 昇思25天学习打卡营第1天|快速入门
  • LinkedList 实现 LRU 缓存
  • ubuntu安装workon
  • (面试必看!)锁策略