JS中声明常量

288 阅读2分钟

前言

本文将介绍JS中声明常量的方法,并介绍如何“冻结”一个对象

const

我们都知道,const 是ES6新增的关键字,用于声明常量,声明时必须赋值,且不可重复声明

const num = 0
num = 1 // TypeError: Assignment to constant variable
const n // SyntaxError: Missing initializer in const declaration
const num = 1 // SyntaxError: Identifier 'num' has already been declared

然而,使用 const 声明的常量是一个对象的时候,仅保证常量的引用地址指向是不变的,对象属性允许改变

const obj = {
  a: {
    b: 1,
  },
  c: 2,
}
obj.a = 3
obj.c = 4
console.log(obj) // {a: 3, c: 4}
obj = {} // TypeError: Assignment to constant variable

defineProperty

如果,我们想要一个对象属性也并不可改变的常量,我的第一想法是 Object.defineProperty 定义不可写的属性,或者在存值器中直接抛出异常

const obj = {
  a: 1,
}
Object.defineProperty(obj, 'b', {
  value: 2,
  writable: false,
})
Object.defineProperty(obj, 'c', {
  get() {
    return 3
  },
  set() {
    throw TypeError('Assignment to constant variable')
  },
})
obj.a++
obj.b++
console.log(obj) // {a: 2, b: 2, c: (...)}
obj.c++ // TypeError: Assignment to constant variable

freeze

其实,JS提供了更方便的冻结对象的方法 Object.freeze,该方法可以将传入的对象冻结,阻止之后的所有修改、删除、配置操作

const obj = {
  a: 1,
  b: 2,
}
Object.freeze(obj)
obj.a++
obj.b++
console.log(obj) // {a: 1, b: 2}

深层冻结

Object.freeze 是浅层的,如果想要冻结深层的所有属性,需要递归处理

浅层冻结

const obj = {
  a: 1,
  b: {
    c: 2,
  },
}
Object.freeze(obj)
obj.a++
obj.b.c++
console.log(obj) // {a: 1, b: {c: 3}}

深层冻结

const obj = {
  a: 1,
  b: {
    c: 2,
  },
}
fun(obj)
obj.a++
obj.b.c++
console.log(obj) // {a: 1, b: {c: 2}}
function fun(obj) {
  if (!Object.isFrozen(obj)) {
    Object.freeze(obj)
    Object.keys(obj).forEach((key) => {
      fun(obj[key])
    })
  }
}

通过 Object.isFrozen 判断对象是否已冻结,如果未冻结,则冻结对象并递归处理所有属性

所有基础数据类型会被判定为已冻结

结语

以上,就是JS声明常量与冻结对象相关的内容了

Object.freeze 在平时用的很少,大多数人应该像我一样会首先想到 defineProperty 的实现方式

两个都应该答出来,前者可以方便的冻结对象,后者可以选择性的设置部分属性

freeze 类似的还有两个方法,seal(阻止配置) 和 preventExtensions(阻止扩展),感兴趣的掘友可自行了解。