视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
JavaScript常量定义详解
2020-11-27 20:24:32 责编:小采
文档

注意,当我们用常规方法定义属性的时候,其除 value 以外的数据描述符默认均为 true ,当我们用 Object.defineProperty() 定义属性的时候,默认为 false。

也就是说,当我们把 writable 设置为 false 的时候,该属性是只读的,也就满足了常量了性质,我们把常量封装在CONST命名空间里面:

var CONST = {};
Object.defineProperty(CONST, "A", {
 value: 1,
 writable: false, //设置属性只读
 configurable: true,
 enumerable: true
});
console.log(CONST.A); //1
CONST.A = 2; //在严格模式下会抛错,在非严格模式下静默失败,修改无效。

但是这样定义的常量不是绝对的,因为我们依然可以通过修改属性的数据描述符来修改属性值:

var CONST = {};
Object.defineProperty(CONST, "A", {
 value: 1,
 writable: false,
 configurable: true,
 enumerable: true
});
Object.defineProperty(CONST, "A", {
 value: 2,
 writable: true, //恢复属性的可写状态
 configurable: true,
 enumerable: true
})
console.log(CONST.A); //2
CONST.A = 3;
console.log(CONST.A); //3

想要做到真正的常量,还需要将属性设置为不可配置:

var CONST = {};
Object.defineProperty(CONST, "A", {
 value: 1,
 writable: false, //设置属性只读
 configurable: false, //设置属性不可配置
 enumerable: true
});
console.log(CONST.A); //1
CONST.A = 2; //错误!属性只读
Object.defineProperty(CONST, "A", {
 value: 2,
 writable: true, 
 configurable: true,
 enumerable: true
}); //错误!属性不可配置

但是如果只设置属性为不可配置状态,依然可以对属性值进行修改:

var CONST = {};
Object.defineProperty(CONST, "A", {
 value: 1,
 writable: true, //设置可写
 configurable: false, //设置属性不可配置
 enumerable: true
});
console.log(CONST.A); //1
CONST.A = 2;
console.log(CONST.A); //2

进而我们可以推断出,configurable 描述符仅冻结属性的描述符,不会对属性值产生影响,也就是说该描述符会冻结 writable、configurable、enumerable 的状态,不会对属性值加以:

var CONST = {};
Object.defineProperty(CONST, "A", {
 value: 1,
 writable: false, //设置不可写
 configurable: false, //设置属性不可配置
 enumerable: false //设置不可枚举
});
Object.defineProperty(CONST, "A", {
 value: 2, //该属性本身不受 configurable 的影响,但由于属性不可写,受 writable 的
 writable: true, //错误!属性不可配置
 configurable: true, //错误!属性不可配置
 enumerable: true //错误!属性不可配置
});

但是 configurable 的有一个特例,就是 writable 可以由 true 改为 false,不能由 false 改为 true:

var CONST = {};
Object.defineProperty(CONST, "A", {
 value: 1,
 writable: true, //设置可写
 configurable: false, //设置属性不可配置
 enumerable: false //设置不可枚举
});
Object.defineProperty(CONST, "A", {
 value: 2, //该属性本身不受 configurable 的影响,由于属性可写,修改成功
 writable: false, 
 configurable: false, 
 enumerable: false 
});
console.log(CONST.A); //2
CONST.A = 3; //错误!属性只读

可枚举描述符用于配置属性是否可以枚举,也就是是否会出现在 for … in … 语句中:

var CONST = {};
Object.defineProperty(CONST, "A", {
 value: 1,
 writable: false,
 configurable: false,
 enumerable: true //可枚举
});
Object.defineProperty(CONST, "B", {
 value: 2,
 writable: false,
 configurable: false,
 enumerable: false //不可枚举
});
for (var key in CONST) {
 console.log(CONST[key]); //1
};

有了以上的基础,我们也就学会一种定义常量的方法,使用属性的数据描述符,下次我们需要用到常量的时候,就可以定义一个 CONST 命名空间,将常量封装在该命名空间里面,由于属性描述符默认为 false,所以我们也可以这样定义:

var CONST = {};
Object.defineProperty(CONST, "A", {
 value: 1,
 enumerable: true
});
Object.defineProperty(CONST, "B", {
 value: 2,
 enumerable: true
});

以上方法是从属性的角度的去定义一组常量,不过我们还可以用另外一种方法,从对象的角度去配置一个对象包括它的所有属性,Object.preventExtensions() 方法可以让一个对象不可扩展,该对象无法再添加新的属性,但是可以删除现有属性:

var CONST = {};
CONST.A = 1;
CONST.B = 2;
Object.preventExtensions(CONST);
delete CONST.B;
console.log(CONST); //CONST: { A: 1}
CONST.C = 3; //错误!对象不可扩展

在该方法的基础之上,我们可以使用 Object.seal() 来对一个对象密封,该方法会阻止对象扩展,并将该对象的所有属性设置为不可配置,但是可写:

var CONST = {};
CONST.A = 1;
CONST.B = 2;
Object.seal(CONST);
CONST.A = 3;
console.log(CONST.A); //3
Object.defineProperty(CONST, "B", {
 value: 2,
 writable: true, 
 configurable: true, //错误!属性不可配置
 enumerable: false, //错误!属性不可配置
}) 
CONST.C = 3; //错误!对象不可扩展

也就是说 Object.seal() 方法相当于帮助我们批量的将属性的可配置描述符设置为 false ,所以说在代码实现层面相当于:

Object.seal = function (obj) {
 Object.preventExtensions(obj);
 for (var key in obj) {
 Object.defineProperty(obj, key, {
 value: obj[key],
 writable: true,
 configurable: false,
 enumerable: true
 })
 };
 return obj;
}

在以上两个方法基础上,我们可以 Object.freeze() 来对一个对象进行冻结,实现常量的需求,该方法会阻止对象扩展,并冻结对象,将其所有属性设置为只读和不可配置:

var CONST = {};
CONST.A = 1;
CONST.B = 2;
Object.freeze(CONST);
CONST.A = 3; //错误!属性只读
Object.defineProperty(CONST, "B", {
 value: 3, //错误!属性只读
 writable: true, //错误!属性不可配置
 configurable: true, //错误!属性不可配置
 enumerable: false, //错误!属性不可配置
}) 
CONST.C = 3; //错误!对象不可扩展

从代码实现层面上相当于:

Object.freeze = function (obj) {
 Object.preventExtensions(obj);
 for (var key in obj) {
 Object.defineProperty(obj, key, {
 value: obj[key],
 writable: false,
 configurable: false,
 enumerable: true
 })
 };
 return obj;
}

最后我们在来看一下这三个方法的兼容性:

Object.preventExtensions()

FeatureFirefox (Gecko)ChromeInternet ExplorerOperaSafari
Basic support4 (2.0)69未实现5.1

Object.seal()

FeatureFirefox (Gecko)ChromeInternet ExplorerOperaSafari
Basic support4 (2.0)69未实现5.1

Object.freeze()

FeatureFirefox (Gecko)ChromeInternet ExplorerOperaSafari
Basic support4.0 (2)69125.1

到底还是万恶的IE,均不兼容IE8

现在,我们也就有了两种方法在JS中定义常量,第一种方法是从属性层面上来实现,在命名空间上可以继续添加多个常量,而第二种方法是从对象层面上来实现,对冻结对象所有属性以及对象本身:

//第一种方法:属性层面,对象可扩展
var CONST = {};
Object.defineProperty(CONST, "A", {
 value: 1,
 enumerable: true
});

//第二种方法:对象层面,对象不可扩展
var CONST = {};
CONST.A = 1;
Object.freeze(CONST);

关于JS常量的问题就讲到这里了,许多书籍在介绍JS基础的时候都会提到JS当中没有常量,导致许多JS开发者在一开始就默认了JS是没有常量的这一说法。从严格语法意义上来讲,JS确实是没有常量的,但是我们可以通过对知识的深入和创造力来构建我们自己的常量,知识是死的,人是活的,只要我们不停的探索,满怀着创造力,就会发现其中不一样的世界

下载本文
显示全文
专题