学习js必然绕不开对象,今天我们就来比较全面地了解一下js中对象。
定义对象的语法
对象可以通过两种形式定义:声明(文字)形式和构造形式。
文字形式:
var myObj = {
key: value
// ...
};
构造形式:
var myObj = new Object();
myObj.key = value;
构造形式和文字形式生成的对象是一样的。唯一的区别是,在文字声明中你可以添加多个 键 / 值对,但是在构造形式中你必须逐个添加属性。
js中的内置对象
js 中还有一些对象子类型,通常被称为内置对象,主要有以下几种:
- Number
- Boolean
- Object
- Function
- Array
- Date
- RegExp
- Error
这些内置对象从表现形式来说很像其他语言中的类型(type)或者类(class),比如 Java 中的 String 类,可以通过new关键字来调用(实例化)。
观察如下代码:
var strPrimitive = "I am a string";
typeof strPrimitive; // "string"
strPrimitive instanceof String; // false
var strObject = new String( "I am a string" );
typeof strObject; // "object"
strObject instanceof String; // true
上述代码中,原始值 “I am a string” 并不是一个对象,它只是一个字面量,所以strPrimitive instanceof String输出false。并且它是一个不可变的值,如果要在这个字面量上执行一些操作,比如获取长度、访问其中某个字符等,那需要将其转换为 String 对象。
看到上述加粗的文字描述,我们心中可能会有些疑惑,因为我们常常使用到如下代码,而且能正确获得长度,这是为什么呢?
var strPrimitive = "I am a string";
var len = strPrimitive.length;
console.log(len); //13
上述代码能正常执行是因为js自动把字符串字面量转换成一个 String 对象,也就是说我们并不需要显式创建一个String对象,对于其他对象来说也是如此,比如我们可以通过代码 var re = /\d+/; 来声明一个正则表达式re。
对象的属性
访问对象属性的方法
观察如下代码:
var myObject = {
a: 2
};
myObject.a; // 2
myObject["a"]; // 2
我们可以通过 . 操作符或者 [] 操作符来访问属性。
.a 语法通常被称为“属性访问”,[“a”] 语法通常被称为“键访问”,相信大家比较常用的是属性访问。
这里有个我们需要注意的点,即在对象中(es5),属性名永远都是字符串。如果我们使用 string(字面量)以外的其他值作为属性名,那它首先会被转换为一个字符串,即使是数字也不例外。
数据描述符和属性描述符
观察如下代码:
var myObject = {};
Object.defineProperty( myObject, "a", {
value: 2,
writable: true,
configurable: true,
enumerable: true
} );
myObject.a; // 2
上述代码中,value就是属性a的数据描述符,因为它只保存一个数值,而writable(可写)、 enumerable(可枚举)和 configurable(可配置)就是属性a的属性描述符,定义了属性a的行为。
下面我们来分析一下属性描述符:
writable
writable 决定是否可以修改属性的值,如果把writable 设置为false,那么就不能修改属性的值,语句myObject.a = 3;会静默失效,a的值还是原来的2,如果在严格模式下,该语句会报错。
configurable
configurable决定属性是否可以配置,只要属性是可配置的,就可以使用 defineProperty() 方法来修改属性描述符,
这里要注意的是把configurable 修改成 false 是单向操作,无法撤销!即我们通过defineProperty() 方法把configurable的值设置成 false,那么将无法再次使用defineProperty() 方法把configurable的值设置成 true。
把属性的configurable的值设置成 false会禁止删除这个属性,即语句delete myObject.a会静默失效。
enumerable
这个描述符控制的是属性是否会出现在对象的属性枚举中,比如说 for…in 循环。如果把 enumerable 设置成 false,这个属性就不会出现在枚举中,虽然仍然可以正常访问它(myObject.a),但是使用for…in 循环遍历myObject时,属性a不会被遍历到。
属性的不变性
有时候我们会需要维护一个不变的对象,即对象中属性不可进行修改、删除等操作,那么我们可以通过以下·的方法来实现这个需求。
-
对象常量
结合 writable:false 和 configurable:false 就可以创建一个真正的常量 属性(不可修改、 重定义或者删除)。 -
禁止扩展
如果我们想禁止一个对象添加新属性并且保留已有属性,可以使用 Object.preventExtensions()方法。 -
密封
Object.seal()方法会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用 Object.preventExtensions() 并把所有现有属性标记为 configurable:false。所以,密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(可以修改属性的值,因为writable不为false)。
-
冻结
Object.freeze()方法会创建一个冻结对象,这个方法实际上会在一个现有对象上调用 Object.seal() 并把所有“数据访问”属性标记为 writable:false,这样就无法修改它们的值。
属性的存在性
怎样判断一个属性是否存在于当前对象呢?
观察如下代码:
var myObject = {
a:2
};
("a" in myObject); // true
("b" in myObject); // false
myObject.hasOwnProperty( "a" ); // true
myObject.hasOwnProperty( "b" ); // false
由上述代码,我们可以看出:可以通过in操作符和hasOwnProperty()方法来判断属性的存在性。
它们的区别是:in 操作符会检查属性是否在对象及其 [[Prototype]] 原型链中。相比之下,hasOwnProperty() 只会检查属性是否在 myObject 对象中,不会检查 [[Prototype]] 链。关于原型链,我会在下一篇博客中进行介绍。