创建一个对象有两种形式:声明(文字)形式
和构造形式
。
例如:
// 声明形式
var myObject = {
name: 'cherry',
age: 20,
};
// 构造形式
var myObject = new Object();
myObject.name = 'cherry';
myObject.age = 20;
一般我们在开发的时候,在创建一个对象的时更喜欢用声明(文字)形式,这种形式可以简化创建包含有大量属性的对象的过程。
Object
是JavaScript的基础,它也是JavaScript语言类型中的一种。JavaScript中的语言类型包含:
除去object
之外,其他的类型都被称为简单基础类型
,在使用typeof
来测试null
的时候,会返回‘object’
,但是null
并不是object
类型。造成这种情况是由于不同对象在底层会被表示为二进制,当二进制的前三位都是0
的时候会被认为是object类型,而null的二进制表示为全0,因此,前三位自然是0,所以在使用typeof
的时候,会返回’object‘
。Object类型有多衍生的子类型,函数就是Object的一个子类型,数组也是。这些子类型通常被称为JavaScript的内置对象
。有些内置对象的名字看起来和简单基础类型一样。
这些内置对象在JavaScript中实际上是内置函数
,这些内置函数可以作为构造函数
来使用,从而可以构建一个对应子类型的新对象。
例如:
var str = 'I am a string';
typeof str ; // string
str instanceof String; // false
var strObj = new String('I am a string');
typeof strObj ; // object
strObj instanceof String; // true
原始值’I am a string’并不是一个对象,它只是一个字面量,并且是一个不可改变的值。当我们需要在字符串上执行一些操作的时候,比如:获取具体的对应位置的字符,获取字符串的长度…,需要将其转换为String对象,幸好,在必要的时候语言会自动将字符串字面量转换为String对象。也就是数,你不需要自己显式的创建一个对象。
var str = 'I am a string';
console.log(str.length); // 13
console.log(str.charAt(3)); // 'm'
对象中的内容是由一些存储在特定命名位置的(任何类型)的值组成。虽然这么说,但是对象并不是拥有这些内容,在引擎内部,这些值的存储方式各种各样,一般并不会存储在对象容器中,存储在对象容器中的是这些属性的名字,这些名字就像指针一样,指向这些值真正的存储位置。
访问对象中的内容有两种方式:.操作符(属性访问)
和[]操作符(键访问)
。
例如:
var myObj = {
a: 2,
};
myObj.a ; // 2
myObj['a']; // 2
这两种访问方式访问的都是同一个位置,拿到的也是同一个值,在日常开发中,我们最常用的是属性访问。这两种访问方式的区别在于,.操作符
要求属性名必须符合标识符的命名规范,而['...']操作符
则可以接受任意的UTF-8/Unicode字符串。例如,如果访问名称为’super-name’的属性,就不能使用.操作符
,只能使用['super-name']
来访问。
从ES5开始,所有的属性都具有属描述符,属性描述符用来描述该属性是否只读,是否可配置,以及是否可遍历。所以属性描述符是针对一个属性而言的,并不是针对整个对象。
以上就是JavaScript中的属性描述符。writable决定了是否可以修改属性的值,configurable决定了是否可以配置属性的属性描述符,enumerable决定了属性是否可遍历。在我们创建一个属性的时候,这三个属性描述符都是默认值。
var myObj = {
a: 2,
};
Object.getOwnPropertyDescription(myObj, 'a');
// {
// value: 2,
// writable: true,
// configurable: true,
// enumerable: true,
// }
我们可以使用Object.defineProperty(…)来添加一个新属性或修改一个已有属性,并对属性操作符进行设置。
var myObj = {};
Object,defineProperty(myObj, 'a', {
value: 2,
wirtable: true,
configurable: true,
enumerable: true,
});
一般来说,你并不会使用这种方式来为对象添加属性,除非你想改变属性的属性描述符。
ES5中的getter
和setter
用来改写默认操作,但是只能应用于单个属性,不能应用于整个对象上。getter
是一个隐藏函数,在调用对象属性的时候调用,setter
也是一个隐藏函数,在给对象属性赋值的时候调用。
通常来说getter
和setter
是成对出现的,当你给一个属性定义了getter
和setter
时,这个属性会被定义为‘访问描述符’
,对于访问描述符,JavaScript会忽略它的value
和writable
属性,取而代之的是只关心它的get
和set
以及configurable
和enumerable
特性。
var myObj = {
get a() {
return this.a;
}
set a(val) {
this.a = val * 2;
}
}
myObj.a = 2;
myObj.a ; // 4
之前我们提到过,如果引用一个对象中不存在的属性时,会返回undefined,但是undefined有可能是对象中的属性存储的值,那么如何来区分者两种情况?
var myObj = {
a: undefined,
}
myObj.a; // undefined
myObj.b; // undefined;
在JavaScript中用来判定一个属性是否存在的方法有两个:
继续上面的例子:
('a' in myObj); // true
('b' in myObj); // false
myObj.hasOwnProperty('a'); // true
myObj.hasOwnProperty('b'); // false
这两种方法的区别是。in操作符
不但会在当前对象中进行查找,也会查找该对象的原型链,而hasOwnProperty(...)
只会在当前对象中进行查找,不会查找该对象的原型链。
之前介绍的enumerable描述符用来描述一个属性是否可枚举,而可枚举就相当于一个属性是否会出现在对象属性的遍历中。例如:
var myObject = {
a: 2,
};
Object.definePropertyDescription(myObject, 'b', {
value: 4,
enumerbale: false, // 让属性b不可枚举
});
myObject.b; // 3
("b" in myObject); // true
myObject.hasOwnProperty( "b" ); // true
for (var k in myObject) {
console.log( k, myObject[k] );
}
// "a" 2
可以看到的是,'b'
确实可以访问,并且存在于myObject当中,但是使用for...in
进行遍历的时候,确不会出现在循环中,原因就是属性b是一个不可枚举属性
。
我们还可以使用其他的方式进行检测一个属性是否可枚举,接着上面的例子:
myObjec.propertyIsEnumerable('a'); true
myObjec.propertyIsEnumerable('b'); false
Object.keys(myObject); // ['a']
Object.getOwnPropertyNames( myObject ); ['a', 'b']
propertyIsEnumerable(...)
方法可以检测一个属性是否可以枚举,Object.keys(..)
返回的是一个数组,包含一个对象中所有可枚举的属性名,Object.getOwnPropertyNames(...)
返回的也是一个数组,包含的是一个对象所有的属性名,无论它们是否可枚举。且这两个方法都只会返回这个对象包含的直接属性,并不会涉及到对象的原型链。