JavaScript 常量定义
引言
相信同学们在看见这个标题的时候就一脸懵逼了,什么?JS能常量定义?别逗我好吗?确切的说,JS当中确实没有常量(ES6中好像有了常量定义的关键字),但是深入一下我们可以发现JS很多不为人知的性质,好好利用这些性质,就会发现一个不一样的JS世界。
一、设置对象属性
首先,在JS当中,对象的属性其实还含有自己的隐含性质,比如下面对象:
var obj = {};
obj.a = 1;
obj.b = 2;
在这里我们定义了一个对象 obj ,并且定义了这个对象的两个属性 a 、 b ,我们可以修改这两个属性的值,可以用 delete 关键字删除这两个属性,也可以用 for ... in ... 语句枚举 obj 对象的所有属性,以上的这些操作叫做对象属性的性质,在我们平常编写代码的时候我们会不知不觉的默认了这些性质,把他们认作为JS应有的性质,殊不知这些性质其实是可以修改的。我通常的定义的属性的方法,默认了属性的性质,不过我们也可以在定义属性的时候修改属性的性质,比如:
var obj = {};
obj.a = 1;
obj.b = 2;
//等价于
var obj = {
a: 1,
b: 2
}
//等价于
var obj = {};
Object.defineProperty(obj, "a", {
value: 1, //初始值
writable: true, //可写
configurable: true, //可配置
enumerable: true //可枚举
});
Object.defineProperty(obj, "b", {
value: 2, //初始值
writable: true, //可写
configurable: true, //可配置
enumerable: true //可枚举
});
这里涉及到了一个方法,Object.defineProperty(),该方法是ES5规范中的,该方法的作用是在对象上定义一个新属性,或者修改对象的一个现有属性,并对该属性加以描述,返回这个对象,我们来看一下浏览器兼容性:
| 特性 | Firefox (Gecko) | Chrome | Internet Explorer | Opera | Safari |
|---|---|---|---|---|---|
| 基本支持 | 4.0 (2) | 5 | 9 [1] | 11.60 | 5.1 [2] |
还是天煞的IE8,如果你的项目要求兼容IE8,那么这个方法也就不适用了,不过IE8也对该方法进行了实现,只能在DOM对象上适用,而且有一些独特的地方,在这里就不讲解了。
二、认识 Object.defineProperty() 方法
Object.defineProperty() 方法可以定义对象属性的数据描述和存储描述,这里我们只讲数据描述符,不对存储描述符讲解,数据描述符有以下选项:
configurable- 当且仅当该属性的 configurable 为 true 时,该属性
描述符才能够被改变,也能够被删除。默认为false。enumerable当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为false。
value- 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为
undefined。writable当且仅当该属性的 writable 为 true 时,该属性才能被赋值运算符改变。默认为false。
注意,当我们用常规方法定义属性的时候,其除 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); //
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); //
CONST.A = 3;
console.log(CONST.A); //
想要做到真正的常量,还需要将属性设置为不可配置:
var CONST = {};
Object.defineProperty(CONST, "A", {
value: 1,
writable: false, //设置属性只读
configurable: false, //设置属性不可配置
enumerable: true
});
console.log(CONST.A); //
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); //
CONST.A = 2;
console.log(CONST.A); //
进而我们可以推断出,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); //
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]); //
};
有了以上的基础,我们也就学会一种定义常量的方法,使用属性的数据描述符,下次我们需要用到常量的时候,就可以定义一个 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); //
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()
| Feature | Firefox (Gecko) | Chrome | Internet Explorer | Opera | Safari |
|---|---|---|---|---|---|
| Basic support | 4 (2.0) | 6 | 9 | 未实现 | 5.1 |
Object.seal()
| Feature | Firefox (Gecko) | Chrome | Internet Explorer | Opera | Safari |
|---|---|---|---|---|---|
| Basic support | 4 (2.0) | 6 | 9 | 未实现 | 5.1 |
Object.freeze()
| Feature | Firefox (Gecko) | Chrome | Internet Explorer | Opera | Safari |
|---|---|---|---|---|---|
| Basic support | 4.0 (2) | 6 | 9 | 12 | 5.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确实是没有常量的,但是我们可以通过对知识的深入和创造力来构建我们自己的常量,知识是死的,人是活的,只要我们不停的探索,满怀着创造力,就会发现其中不一样的世界。
JavaScript 常量定义的更多相关文章
- JavaScript基础——JavaScript常量和变量(笔记)
JavaScript常量和变量(笔记) Javascript代码严格区分大小写. javascript暂不支持constant关键字,不允许用户自定义常量. javascript使用var关键字声明变 ...
- c# - 常量定义与赋值
1.前言 c#与Java很相似,但是不一样,又与js(JavaScript)相似,但是也不一样,所以我认为c#是Java和 js的孩子. 2.常量定义 字符串: const string = &quo ...
- C#与Java对比学习:类型判断、类与接口继承、代码规范与编码习惯、常量定义
类型判断符号: C#:object a; if(a is int) { } 用 is 符号判断 Java:object a; if(a instanceof Integer) { } 用 inst ...
- JavaScript基础——定义变量
在JavaScript中使用变量来临时存储和访问来自JavaScript文件的数据.变量既可以指向简单的数据类型,如数字或者字符串:也可以指向更复杂的数据类型,比如对象. 在JavaScript中定义 ...
- (转载)JavaScript中定义变量
(转载)http://blog.163.com/xuxiaoqianhz@126/blog/static/165190577201061594421870/ JavaScript中定义变量有两种方式: ...
- javascript从定义到执行 js引擎 闭包
javascript从定义到执行,JS引擎在实现层做了很多初始化工作,因此在学习JS引擎工作机制之前,我们需要引入几个相关的概念:执行环境 栈.全局对象.执行环境.变量对象.活动对象.作用域和作用域链 ...
- php 常量定义
php常量定义及取值 常量在定义时赋值: 不能变 :不能销毁: 具有超全局作用于:常量只能储存标量数据(字符 整型 浮点 ): <?php define("hello", ...
- 点评阿里JAVA手册之编程规约(命名风格、常量定义、代码风格、控制语句、注释规约)
下载原版阿里JAVA开发手册 [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文难度系数为一星(★) 码出高效.码出质量. 代码的字里行间流淌的是 ...
- PHP常量定义define与const
一.const PHP5.3以前,const只能在类内部声明变量,5.3+允许在外部声明变量,但还不能使用常量计算! const ONE = 1; const WORD = 'hello world' ...
随机推荐
- In-Memory:内存数据库
在逝去的2016后半年,由于项目需要支持数据的快速更新和多用户的高并发负载,我试水SQL Server 2016的In-Memory OLTP,创建内存数据库实现项目的负载需求,现在项目接近尾声,系统 ...
- 【小程序分享篇 二 】web在线踢人小程序,维持用户只能在一个台电脑持登录状态
最近离职了, 突然记起来还一个小功能没做, 想想也挺简单,留下代码和思路给同事做个参考. 换工作心里挺忐忑, 对未来也充满了憧憬与担忧.(虽然已是老人, 换了N次工作了,但每次心里都和忐忑). 写写代 ...
- 使用webstorm+webpack构建简单入门级“HelloWorld”的应用&&引用jquery来实现alert
使用webstorm+webpack构建简单入门级"HelloWorld"的应用&&构建使用jquery来实现 1.首先你自己把webstorm安装完成. 请参考这 ...
- 尝试asp.net mvc 基于controller action 方式权限控制方案可行性
微软在推出mvc框架不久,短短几年里,版本更新之快,真是大快人心,微软在这种优秀的框架上做了大量的精力投入,是值得赞同的,毕竟程序员驾驭在这种框架上,能够强力的精化代码,代码层次也更加优雅,扩展较为方 ...
- [原] KVM 虚拟化原理探究(4)— 内存虚拟化
KVM 虚拟化原理探究(4)- 内存虚拟化 标签(空格分隔): KVM 内存虚拟化简介 前一章介绍了CPU虚拟化的内容,这一章介绍一下KVM的内存虚拟化原理.可以说内存是除了CPU外最重要的组件,Gu ...
- Java之多态(二)
package test05;import test06.Car1;public class DuoTai_Test02 { /**多个对象,一个形态 * Tiger.Lion.Snake → Ani ...
- PHP设计模式(五)建造者模式(Builder For PHP)
建造者模式:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示的设计模式. 设计场景: 有一个用户的UserInfo类,创建这个类,需要创建用户的姓名,年龄,爱好等信息,才能获得用 ...
- 用angular怎么缓存父页面数据
angular做单页面应用是一个比较好的框架,但是它有一定的入门难度,对于新手来说可能会碰到很多坑,也有许多难题,大部分仔细看文档,找社区是能解决的. 但有些问题也许资料比较少,最近遇到过一个要缓存父 ...
- 移动BPM解决方案分享
畅通开放 无边界的渠道 效率倍增 更高效的处理方式 即时共享 更强大的决策能力 各种终端应用 帮您实现:新任务通知.任务预警.催办.任务审批.任何数据汇总提醒消息通知...... 短信 客户端: ...
- [AlwaysOn Availability Groups]健康模型 Part 1——概述
健康模型概述 在成功部署AG之后,跟踪和维护健康状况是很重要的. 1.AG健康模型概述 AG的健康模型是基于策略管理(Policy Based Management PBM)的.如果不熟悉这个特性,可 ...