【03】const
魔芋总结:
1,声明的是常量,一经声明,不得修改。必须声明的同时并赋值。否则报错。
2, 只在声明所在的块级作用域内有效。
3, co nst命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
4, 也与
let
一样不可重复声明。 5, 对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。(指针的概念。)
const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。
6, ES5只有两种声明变量的方法:var命令和function命令。ES6除了添加let和const命令,另外两种声明变量的方法:import命令和class命令。所以,ES6一共有6种声明变量的方法。
7, ES6规定,var命令和function命令声明的全局变量,依旧是全局对象的属性;
let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。
const命令
const用来声明变量,但是声明的是常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;PI // 3.1415PI = 3;// TypeError: "PI" is read-only
上面代码表明改变常量的值会报错。
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const foo;// SyntaxError: missing = in const declaration
上面代码表示,对于const来说,只声明不赋值,就会报错。
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
if (true) { const MAX = 5;}MAX // Uncaught ReferenceError: MAX is not defined
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
if (true) { console.log(MAX); // ReferenceError const MAX = 5;}
上面代码在常量
MAX
声明之前就调用,结果报错。 const声明的常量,也与
let
一样不可重复声明。 var message = "Hello!";let age = 25;// 以下两行都会报错const message = "Goodbye!";const age = 30;
对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。
const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。
const foo = {};foo.prop = 123;foo.prop// 123foo = {} // TypeError: "foo" is read-only不起作用
上面代码中,常量
foo
储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把 foo
指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。 下面是另一个例子。
const a = [];a.push("Hello"); // 可执行a.length = 0; // 可执行a = ["Dave"]; // 报错
上面代码中,常量
a
是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给 a
,就会报错。 如果真的想将对象冻结,应该使用
Object.freeze
方法。 const foo = Object.freeze({});foo.prop = 123; // 不起作用
上面代码中,常量
foo
指向一个冻结的对象,所以添加新属性不起作用。 除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象彻底冻结的函数。
var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach((key, value) => { if (typeof obj[key] === 'object') { constantize(obj[key]); } });};
跨模块常量
上面说过,const声明的常量只在当前代码块有效。如果想设置跨模块的常量,可以采用下面的写法。
// constants.js 模块export const A = 1;export const B = 3;export const C = 4;// test1.js 模块import * as constants from './constants';console.log(constants.A); // 1console.log(constants.B); // 3// test2.js 模块import {A, B} from './constants';console.log(A); // 1console.log(B); // 3
全局对象的属性
全局对象是最顶层的对象,在浏览器环境指的是
window
象,在Node.js指的是 global
对象。ES5之中,全局对象的属性与全局变量是等价的。 window.a = 1;a // 1a = 2;window.a // 2
上面代码中,全局对象的属性赋值与全局变量的赋值,是同一件事。(对于Node来说,这一条只对REPL环境适用,模块环境之中,全局变量必须显式声明成
global
对象的属性。) 这种规定被视为JavaScript语言的一大问题,因为很容易不知不觉就创建了全局变量。
ES6为了改变这一点,一方面规定,var命令和function命令声明的全局变量,依旧是全局对象的属性;
另一方面规定,let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。
var a = 1;// 如果在Node的REPL环境,可以写成global.a// 或者采用通用方法,写成this.awindow.a // 1let b = 1;window.b // undefined
上面代码中,全局变量
a
由 var
命令声明,所以它是全局对象的属性;全局变量 b
由 let
命令声明,所以它不是全局对象的属性,返回 undefined
。 原文作者:阮一峰
原文地址:
**