Home  >  Article  >  Web Front-end  >  Detailed explanation of JavaScript constant definitions

Detailed explanation of JavaScript constant definitions

黄舟
黄舟Original
2017-03-03 14:40:171446browse

I believe the students were confused when they saw this title. What? Can constants be defined in JS? Don't tease me, okay? To be precise, there are indeed no constants in JS (there seems to be keywords for constant definitions in ES6), but if we dig deeper, we can find many unknown properties of JS. If you make good use of these properties, you will find a different world of JS. .

First of all, in JS, the properties of objects actually contain their own implicit properties, such as the following objects:

var obj = {};
obj.a = 1;
obj.b = 2;

Here we define an object obj and define the properties of this object We can modify the values ​​of the two attributes a and b. We can use the delete keyword to delete these two attributes. We can also use the for... in... statement to enumerate all the attributes of the obj object. The above operations are called objects. The properties of attributes, when we usually write code, we will unknowingly default to these properties and regard them as the properties that JS should have. However, we do not know that these properties can actually be modified. My usual method of defining properties defaults to the properties of the properties, but we can also modify the properties of the properties when defining them, for example:

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       //可枚举
});

This involves a method, Object.defineProperty(), This method is in the ES5 specification. The function of this method is to define a new attribute on the object, or modify an existing attribute of the object, describe the attribute, and return the object. Let's take a look at browser compatibility:

Features Firefox (Gecko) Chrome Internet Explorer Opera Safari
Basic support 4.0 (2) 5 9 [1] 11.60 5.1 [2]

还是天煞的IE8,如果你的项目要求兼容IE8,那么这个方法也就不适用了,不过IE8也对该方法进行了实现,只能在DOM对象上适用,而且有一些独特的地方,在这里就不讲解了。

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);  //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()

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 常量定义详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn