当前位置: 首页 > 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] );}});
};

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 实验2-5-7 求阶乘序列前N项和【多看看函数】
  • 文件夹判断操作类 - C#小函数类推荐
  • 哈夫曼树及哈夫曼编码
  • 前端小白安装node、vue、Express、Electron及(Electron桌面端exe应用开发)
  • 干货满满,从零到一:编程小白如何在大学成为编程大神?
  • 滴滴官宣潘展乐为滴滴网约车“快”乐大使
  • AI产品经理的职责与能力:将AI技术转化为实际价值
  • 算法小白的进阶之路(力扣1~5)
  • 复制知乎文字内容
  • 本地VSCode连接远程linux环境服务器的docker
  • 【Linux】文件系统
  • TypeScript中 ?, ??, !, !! 的使用
  • 6小时之可笑中文乱码bug
  • 面试:MySQL 数据库中的 count(1)、count(*)、count(字段)有什么区别?
  • ssh免密认证配置
  • php的引用
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • chrome扩展demo1-小时钟
  • co.js - 让异步代码同步化
  • css系列之关于字体的事
  • echarts花样作死的坑
  • fetch 从初识到应用
  • iOS编译提示和导航提示
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • JAVA_NIO系列——Channel和Buffer详解
  • Python_OOP
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Vue.js源码(2):初探List Rendering
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • Yeoman_Bower_Grunt
  • 阿里云应用高可用服务公测发布
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 从零开始在ubuntu上搭建node开发环境
  • 仿天猫超市收藏抛物线动画工具库
  • 检测对象或数组
  • 来,膜拜下android roadmap,强大的执行力
  • 前端面试题总结
  • 微信公众号开发小记——5.python微信红包
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • ​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
  • #define与typedef区别
  • #stm32整理(一)flash读写
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (33)STM32——485实验笔记
  • (Java)【深基9.例1】选举学生会
  • (k8s)Kubernetes 从0到1容器编排之旅
  • (SpringBoot)第七章:SpringBoot日志文件
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (剑指Offer)面试题34:丑数
  • (六)激光线扫描-三维重建
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • (四) 虚拟摄像头vivi体验
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)