前言
在很多人的意识中,JS里的const变量仿佛就是其他语言中的常量一样完全不可变,——这样理解const的作用当然是完全错误的,然而不幸的是这种误解从没消失过,甚至还相当流行。本文将会直截了当的将const的真正作用展现出来。
正文
ES6中的const创造的是一种不可变得binding(绑定),这不代表被const定义的变量是一个constant(常量)或是immutable(不可变的)。一个const变量是可以改变的。下面的例子就为你们展示这一点,这段代码是完全合法的ES6代码,并且不会抛出异常:
const foo = {};foo.bar = 42;console.log(foo.bar);// → 42
在这段代码里,对于const变量来讲唯一不可变的是binding,即const分配给变量名foo一个值:{},并且保证这种分配操作不会再次发生(译者:但他里面的值{}怎么变不保证)。对于可能造成重新分配的操作,如使用任何assignment operator、unary或postfix操作都会导致抛出TypeError异常,请看下面的测试:
const foo = 27;// 以下操作都会抛出异常// Assignment operators:foo = 42;foo *= 42;foo /= 42;foo %= 42;foo += 42;foo -= 42;foo <<= 0b101010;foo >>= 0b101010;foo >>>= 0b101010;foo &= 0b101010;foo ^= 0b101010;foo |= 0b101010;// Unary `--` and `++`:--foo;++foo;// Postfix `--` and `++`:foo--;foo++;
由此可见,ES6的const做到的是保证变量分配上的不可变,而非值上的不可变(译者:尽管对于基本数据类型的变量来讲这俩好像是一回事)。
那么,如何让一个变量内部的值不可变?
对于基本数据类型(number, string, boolean, symbol, null, undefined)来讲它们内部的值是不可变的,不管你怎么定义它们:
var foo = 27;foo.bar = 42;console.log(foo.bar);// → `undefined`
如果你想让一个对象的值不可变,可以使用Object.freeze()。这个方法从ES5开始被支持,到现在已经被广泛使用。
const foo = Object.freeze({
'bar': 27});foo.bar = 42; // strict mode下会抛出TypeError异常;// sloppy mode下不会报错,但赋值也不会成功;console.log(foo.bar);// → 27
值得注意的是,Object.freeze()的效果并不保证整个对象完全的、绝对的无法改变:一个已经freeze了的对象的值仍然是可以发生变化,比如嵌套对象,例:
obj1 = {
internal: {}};Object.freeze(obj1);obj1.internal.a = 'aValue';console.log(obj1.internal.a)// 'aValue'
如果想要保证整个变量是彻底的不可变,你可以参考这个链接(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)里面deepFreeze()方法的实现来达到这个目的(译者:这个deepFreeze简单来讲就是用递归的方法遍历内部所有的对象并给他们加上freeze)。
另外要说的是,Object.freeze()只适用于property-value pairs(属性值对)这种形式,所以你没办法让Date、Map或是Set这种对象完全不可变。
补充一点:现在有个提议就是关于在未来的ECMAScript标准中增加一种完全不可变的数据结构。
const vs. let
const和let唯一的区别是,const让rebinding(重新绑定)不能发生。
本文写到这里都是基于事实的内容,接下来我说点主观的东西。基于上面我们所了解的,我认为使用const让代码更易于阅读,一个const变量总是指向同一个object(译者:而且你可以改变对象内的值),let则完全不能保证这一点(译者:即无法保证总是指向同一object)。所以我认为,在ES6代码中使用let还是const基于以下原则是合理的:
1、没有特殊情况都用const
2、只有需要rebinding的时候(译者:如基本数据类型变量的改变)才使用let
3、不管你怎么用let或者const,都不要在ES6代码里用var
不知道你同意吗?同意或反对的理由是什么?我对那些更习惯使用let的开发者的意见很感兴趣(即便他们将let使用在那些从来不会变化的变量上),为什么当你定义那些不会重新bind的变量时第一个会想到使用let?是因为“const定义的是常量”这种概念上的错误理解,还是别的什么原因?欢迎你们留言写下自己的理由。