在ES6以前,JavaScript被其余编程语言诟病没有定义常量的能力,甚至在大多数企业的开发文档中,对于常量的定义都使用var。通常常常会使用大写字母和下划线组成的变量名进行规范约束。固然这种妥协的“常量”是随时可变的。例如如下代码:javascript
var MAX_COUNT=0;
MAX_COUNT=1 //WARNING复制代码
好在E6引入了const,让JavaScript得到了真正的定义常量的能力,接下来小编将和你们一块儿学习const,经过本篇文章,你将学到如下内容:前端
本篇文章阅读时间预计10分钟java
使用const语法建立变量,一旦建立初始化,咱们就不能改变他们的值,所以这就称为常量。若是你尝试改变一个const变量,则会抛出异常。此外,若是你使用const只声明变量,不进行初始化,也会抛出异常。如如下代码,试图改变一个常量,引擎就会抛出异常:编程
const pi = 3.141;
pi = 4; // not possible in this universe, or in other terms,
// throws Read-only error复制代码
因为ES6能够为程序工程化提供内存安全的优点,即是由于const定义常量的原理是阻隔变量所对应的内存地址被改变。数组
变量与内存之间的关系由三个部分组成:变量名、内存绑定和内存地址。以下图所示:安全
ES6在对变量的引用进行读取时,会从该变量当前所对应的内存地址所指向内存空间中读取内容。当变量改变时,引擎会从新从内存分配一个新的内存空间以存储新值,并将新的内存地址与变量进行绑定。const的原理即是在变量名与内存地址之间创建不可变的绑定,当尝试从新分配新的内存空间时,引擎便会抛出异常。bash
在某些状况,并不是值不可变。以V8引擎为例,如字符串、数字、布尔值、undined等值类型只占用一组内存空间的,这些类型的值再内存空间中是连续的、不可拆分的。而对于对象、数组等稀疏的引用类型值,因为属性值是能够变化的,因此为了最快地进行内存调度,当对象的属性被改变或添加了新的属性时,都须要从新计算内存地址偏移值。所以使用const定义对象时,因为所建立的内存只绑定一处的,因此默认状况下对象这种由若干内存空间片断组成的值并不会所有被锁定,所以使用const定义对象时,对象的属性值是可变的。微信
上一小节咱们说起到,当咱们使用const定义对象时,因为对象是引用类型值,而非对象自己,所以更改对象的属性是可行的,从新更改整个对象变量会抛出异常,以下段代码所示:编程语言
const a = {
name: "Mehul"
};
console.log(a.name);
a.name = "Mohan";
console.log(a.name);
a = {}; //throws read-only exception复制代码
上述代码输出post
Mehul
Mohan
<Error thrown>复制代码
在此示中,a变量是引用值类型,对象地址是不能改变的,可是这个对象自己的属性是能够改变的。所以,当咱们尝试将顶一个对象分配给a变量时,引擎就会抛出异常。
上一小节,咱们了解了,使用const定义变量时,变量的属性是能够更改的,如何让其不能更改呢,其实只要配合ES5中的Object.freeze()方法,即可以得到一个第一层属性(首层)不可变的对象。若是第一层属性中存在对象嵌套,嵌套对象的属性仍然是能够改变的。以下段代码所示:
const ob1 = {
prop1 : 1,
prop2 : {
prop2_1 : 2
}
};
Object.freeze( ob1 );
ob1.prop1 = 4; // (frozen) ob1.prop1 is not modified
ob1.prop2.prop2_1 = 4; // (frozen) modified, because ob1.prop2.prop2_1 is nested
ob1.prop2 = 4; // (frozen) not modified, bar is a key of obj1
ob1 = {}; // (const) ob2 not redeclared (used const)复制代码
如何实现全部层级的属性不可变呢?咱们能够用递归的方式调用Object.freeze进行实现,以下段代码所示(代码来源MDN):
function deepFreeze(object) {
// Retrieve the property names defined on object
var propNames = Object.getOwnPropertyNames(object);
// Freeze properties before freezing self
for (let name of propNames) {
let value = object[name];
object[name] = value && typeof value === "object" ?
deepFreeze(value) : value;
}
return Object.freeze(object);
}
var obj2 = {
internal: {
a: null
}
};
deepFreeze(obj2);
obj2.internal.a = 'anotherValue'; // fails silently in non-strict mode
obj2.internal.a; // null复制代码
关于做用域的概念,小编在这篇文章《【ES基础】let和做用域》已经介绍过了,不清楚的能够点击连接进行查看,const和let同样,也是块做用域变量,他们遵循相同的做用域规则,以下段代码所示:
const a = 12; // accessible globally
function myFunction() {
console.log(a);
const b = 13; // accessible throughout function
if(true) {
const c = 14; // accessible throughout the "if" statement
console.log(b);
}
console.log(c);
}
myFunction();复制代码
上述代码输出
12
13
ReferenceError Exception复制代码
从ES6引入let的语法,设计的初衷即是替代var。从工程化的角度来讲,咱们应从ES6后听从如下三原则:
今天的内容就介绍到这里,为了更好的使用ES6,咱们应该尽快适应使用const定义常量,使用let定义变量。
更多精彩内容,请微信关注”前端达人”公众号!