乐趣区

关于javascript:const关键字终于拥有真正的常量声明语句

本文首发于集体网站:const 关键字:终于领有真正的常量申明语句

你好,明天大叔想和你唠扯唠扯 ES6 新增的关键字 —— const。在说 const 关键字之前,大叔先和你唠唠大叔本人对 const 的感触 —— JavaScript 尼玛终于能够申明真正的常量啦!大叔为啥会收回这样滴感叹?切实是“天下苦秦久矣”呀~

话说 long long ago,当初大叔从 Java 技术栈转向前端技术栈,首先要搞定的就是 JavaScript。尽管都说 JavaScript 和 Java 语言有很多的中央是类似滴,但你晓得直到大叔发现这货不能申明常量时候的感触吗?!那真是欲哭无泪啊~ 一个堂堂滴编程语言,竟然尼玛不能申明常量?!也好意思说本人是个编程语言?!

申明常量

好吧~ 大叔不吐槽了,还是说正事儿吧。其实,在 ES5 外面也不是不能申明常量,就是有那么一捏捏的顺当。你晓得 Object 有个叫做 defineProperty() 的办法吧?它是用来给某个对象定义属性的。在 ES5 外面就能够通过这个办法间接地来申明常量。

比方说,你当初想在全局作用域外面申明一个常量,其实就能够看作是给顶级对象增加一个属性嘛。带入个场景比拟好整明确,假如是一个 HTML 页面外面申明一个全局常量,就能够像上面这样整:

Object.defineProperty(window, 'a', {
  value: 'a',     // 设置该属性的值
  writable: false // 设置该属性的值不能写
})

这段代码的意思就是给 window 对象减少一个名叫 a 的属性。writeable 的作用就是这个属性能不能写,值为 false 就是只能读不能写的意思。所以,这时的属性 a 就只能访问不能批改。不信咱能够试试:

console.log(a)

这时打印的后果比较简单,就是 a。接下来咱们再批改一下试试看是啥后果:

a = 'b'
console.log(a)

打印的后果还是 a,并没有把 a 的值改成 b。

其实,这样的用法曾经很靠近常量的用法了。然而,可然而,但可是~ 你不感觉顺当吗?!为啥?大叔给你每每:

  • 尼玛我当初要申明的是一个常量,你当初用的是定义属性的语法。
  • 这个示例还好,因为是申明全局常量,能够加到 window 这种顶级对象上。要是在一个函数作用域外面咋办,我找不准上下文对象咋办?!
  • 即便就用这个方法能解决这个问题,也的确不能改值。然而,啥提醒没有,你不好受吗?!
  • 常量就是常量,属性就是属性。至多从概念上就别尼玛混同在一起,你说是不是?!

所以,在 ES6 的新个性外面,大叔终于看到心愿 —— const。这货才是真正用来定义常量滴!说到常量,大叔得先给你科普一下到底怎么样才算是常量。

所谓常量,简略来说就是不能变动的值。其实,不仅不能值变动,还不能反复赋值,也不能反复申明,这才算是对滴。

那接下来,大叔就给你看看 const 这货能不能做到这一点:

const a = 'a'
console.log(a)
a = 'b'
console.log(a)

这段代码不难看出就是应用 const 关键字申明了一个常量,两处打印的后果别离为:

  • 第一处打印的后果是 a
  • 第二处打印的后果是报错,报错的内容是:TypeError: Assignment to constant variable.,大略的意思就是你把常量当变量赋值了。

看到了吧?!这才是常量。不仅不能扭转值,而且会进行报错,通知你扭转值这种行为是不对滴。

申明常量的注意事项

当然了,ES6 新增了这样的语法,附带的也有一些须要你留神的事儿 —— 就是在申明一个常量的时候,是必须把常量进行初始化的。不能像申明变量一样,申明和初始化能够分成两个步骤实现。比方说像上面这样婶儿做:

const a
a = 'a'

这段代码运行之后的后果就是给你报个大错,报错的内容是:SyntaxError: Missing initializer in const declaration,大略的意思就是说你在申明常量的时候没给人家初始化。

你看看,整得多明确!钉儿是钉儿,卯儿是卯儿的。

块级作用域

再有就是,const 不仅提供一个真正能够申明变量的形式,还提供了块级作用域。啥?你还不晓得呢?!那你就去看看大叔写的《let 关键字:ES6 新增的 var 关键字加强版》这篇文章外面对于块级作用域的内容吧。

在这儿呢,大叔想再次重申一下块级作用域的益处 —— 就是会把之前裸露在全局作用域的一些变量全副限度在一个具体的块级作用域外面。比方说像上面这样婶儿的代码:

if (true) {const a = 'a'}
console.log(a)

这段代码运行后打印的后果是报错,报错的内容是:ReferenceError: f is not defined。也就是说,你在一个块级作用域外面申明一个常量,在这个块级作用域之外是没方法拜访到这个常量的。

暂时性死区(TDZ)

既然唠到 const 申明的常量是具备块级作用域的,那就不能不说一下暂时性死区的问题了。啥意思呢?就是说应用 const 申明的常量也同样存在暂时性死区的,不信你来看段代码:

if (true) {console.log(a)
  const a = 'a'
}

这段代码运行后的后果是报错,报错的内容还是暂时性死区的谬误:ReferenceError: Cannot access 'a' before initialization

存在了暂时性死区了,就阐明 const 申明常量的时候也就不存在啥申明提前的事儿了。这两件事儿吧,其实说的是一个意思,你可得记明确了。

申明常量对象或者数组

唠到这儿吧,其实基本上对于 const 都唠完了。这货除了能够真正申明一个常量之外,其实没啥可唠的啦。然而,但可是,可然而~ 嘿嘿~

大叔想问你的问题:如果咱们用 const 申明一个对象或者数组,那这个对象的属性或者数组外面的元素能不能批改呢?

寻思是寻思不明确的,咱们间接整代码吧,用事实来看看到是个啥状况。比方说咱们先申明一个这样婶儿的对象:

const obj = {
  name: "不想成熟的大叔",
  age: 37
}

大叔往年都 37 啦,然而年老的心还是有滴。所以,大叔想把 age 这个属性的值改成 18,就像这样婶儿的:

obj.age = 18
console.log(obj)

后果~ 咱们运行代码之后失去的后果就是这样婶儿的:

{
    name: "不想成熟的大叔",
  age: 18
}

留神啊~ 留神啊~ age 属性的值被胜利滴改成了 18!不对吧?const 申明的不是常量吗?常量不是不能扭转值吗?这尼玛后果也不对啊?

灵魂三连问也没用,事实摆在眼吧前儿,咱就得认!然而,为啥会这样涅?别急,且听大叔给你缓缓道来~

想整明确这个事儿吧,咱们就得先唠扯唠扯 JavaScript 的存储构造了。JavaScript 的存储构造有俩儿,一个叫做“堆内存”,一个叫做“栈内存”。个别状况下吧,咱们定义的变量或者常量都是存储在堆内存外面的。然而吧,对象和数组算是 JavaScript 外面比较复杂的一种数据,所以实际上对象或者数组的存储模式是这样婶儿的:

晓得了这个事儿,你大略就能整明确为啥下面的代码运行的后果是这样婶儿的了吧?!说白了,const 申明的对象的值的确不能扭转,然而对象的值存储的是一个援用地址,而具体的属性其实是存储在这个援用地址外面,const 是没方法限度的。

整到这儿,你是不是想问那这样婶儿的问题要怎么解决?嗯~ 也能解决的。你还记得 Object 提供了一个办法叫做 freeze() 吗?这个办法是用来解冻某个对象的。解冻之后不能向这个对象增加新的属性,不能删除已有属性,不能批改该对象已有属性的可枚举性、可配置性、可写性,以及不能批改已有属性的值。所以,就能够利用这个办法来解决下面提到的问题:

const obj = {
  name: "不想成熟的大叔",
  age: 37
}
Object.freeze(obj)
obj.age = 18
console.log(obj)

这样解决之后,咱们再来看看打印后的后果吧:

{
    name: "不想成熟的大叔",
  age: 37
}

问题被完满滴解决了!等一下,真的是这样婶儿的吗?大叔想持续再操作一下下的,比方说像这样婶儿的:

const obj = {
  name: "不想成熟的大叔",
  age: 37,
  skill: {
    name: "coding",
    year: 15
  }
}

啥意思?就是说咱们在申明一个对象的时候,这个对象的属性也同样是一个对象或者数组,那当初的问题就是你利用 Object.freeze() 办法还能胜利地解冻不?咱们就来试试吧:

Object.freeze(obj)
obj.skill.year = 20
console.log(obj)

咱们失去的后果实际上是这样婶儿的:

{
    name: "不想成熟的大叔",
  age: 37,
  skill: {
      name: "coding",
    year: 20
  }
}

发现还是被批改了~ 这又是咋回事儿呢?这就阐明 Object.freeze() 办法只能解冻以后对象的属性,然而如果某个属性的值还是一个对象或者数组的话,那就阐明这个属性存储的还是一个援用地址,理论的数据是存储在这个援用地址中的。

想要解决这个问题其实也不算难,就是持续利用 Object.freeze() 办法来解冻这个值为对象或者数组的属性就行了。就像这样婶儿操作:

Object.freeze(obj.skill)
obj.skill.year = 20
console.log(obj)

这样咱们失去的后果就是:

{
    name: "不想成熟的大叔",
  age: 37,
  skill: {
      name: "coding",
    year: 15
  }
}

也就是说,对于这个问题咱们想一劳永逸地解决掉,能够定义一个函数,把对象作为参数传递进去。而后,这个函数次要利用递归形式把对象中所有值为对象或者数组的属性别离进行解冻,穷尽为止就能够啦!

写在最初的话

好了,整到这儿,ES6 新增的 const 关键字所有大叔想和你唠扯的内容都唠扯完了,也心愿能对你有所帮忙。最初再说一句:我是不想成熟的大叔,为前端学习不再干燥、艰难和迷茫而致力。你感觉这样学习前端技术乏味吗?有什么感触、想法,和好的倡议能够在上面给大叔留言哦~

退出移动版