ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptの定数定義の詳しい説明

JavaScriptの定数定義の詳しい説明

黄舟
黄舟オリジナル
2017-03-03 14:40:171438ブラウズ

このタイトルを見た生徒たちは混乱したと思います。 JSで定数を定義できるのでしょうか?からかわないでね?正確に言うと、確かにJSには定数は存在しません(ES6には定数定義のキーワードがあるようです)が、さらに深く掘り下げていくと、これらのプロパティをうまく活用すると、JSの未知のプロパティがたくさん見つかります。別の JS の世界を見つけてください。

まず、JS では、オブジェクトのプロパティには、実際には次のオブジェクトのような独自の暗黙的なプロパティが含まれています。

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

ここでは、オブジェクト obj と、このオブジェクトの 2 つの属性 a と b を定義します。これら 2 つの属性の値を削除するには、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() 方法可以定义对象属性的数据描述和存储描述,这里我们只讲数据描述符,不对存储描述符讲解,数据描述符有以下选项:

  • 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)!

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。