JS 对象基本用法

45 阅读7分钟

概述

对象(object)是 JavaScript 语言的核心概念,也是最重要的数据类型。什么是对象?简单说,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。

var obj = {
  foo: 'Hello',
  bar: 'World'
};

上面代码中,大括号就定义了一个对象,它被赋值给变量 obj,所以变量 obj 就指向一个对象。该对象内部包含两个键值对(又称为两个“成员”),第一个键值对是 foo: 'Hello',其中 foo 是“键名”(成员的名称),字符串 Hello 是“键值”(成员的值)。键名与键值之间用冒号分隔。第二个键值对是 bar: 'World',bar 是键名,World 是键值。两个键值对之间用逗号分隔。

对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名),所以加不加引号都可以。上面的代码也可以写成下面这样。

var obj = {
  'foo': 'Hello',
  'bar': 'World'
};

如果键名是数值,会被自动转为字符串。如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错。

// 报错
var obj = {
  1p: 'Hello World'
};

// 不报错
var obj = {
  '1p': 'Hello World',
  'h w': 'Hello World',
  'p+q': 'Hello World'
};

上面对象的三个键名,都不符合标识名的条件,所以必须加上引号。

对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用。

var obj = {
  p: function (x) {
    return 2 * x;
  }
};

obj.p(1) // 2

上面代码中,对象 obj 的属性 p,就指向一个函数。

创建新对象

JavaScript 拥有一系列预定义的对象。另外,你可以创建你自己的对象。从 JavaScript 1.2 之后,你可以通过对象初始化器(Object Initializer)创建对象。或者你可以创建一个构造函数并使用该函数和 new 操作符初始化对象。

  1. 使用对象初始化器,通过对象初始化器创建对象的语法如下:

    var obj = { property_1:   value_1,   // property_# 可以是一个标识符...
                 2:            value_2,   // 或一个数字...
                 ["property" +3]: value_3,  //  或一个可计算的key名...
                 // ...,
                 "property n": value_n }; // 或一个字符串
    

    这里 obj 是新对象的名称,每一个 property_i 是一个标识符(可以是一个名称、数字或字符串字面量),并且每个 value_i 是一个其值将被赋予 property_i 的表达式。obj 与赋值是可选的;如果你不需要在其他地方引用对象,你就不需要将它赋给一个变量。

    下例创建了有三个属性的 myHonda 对象。注意它的 engine 属性也是一个拥有自己属性的对象。

    var myHonda = {color: "red", wheels: 4, engine: {cylinders: 4, size: 2.2}};
    
  2. 使用构造函数 作为另一种方式,你可以通过两步来创建对象:

    • 通过创建一个构造函数来定义对象的类型。首字母大写是非常普遍而且很恰当的惯用法。
    • 通过 new 创建对象实例。

    现在你可以象这样创建一个 mycar 对象:

    function Person(name, age){
    this.name = name;
    this.age = age;
    }
    
    var obj1 = new Person("ys", 12);
    

    也可以直接使用 Object 来创建一个新对象

    let obj = new Object({'name': 'ab'});
    
  3. Object.create()

     // Object.create() 方式创建
     var a = { rep: 'apple' }
     var b = Object.create(a)
     console.log(b)  // {}
     console.log(b.__proto__) // {rep: "apple"}
     console.log(b.rep) // {rep: "apple"}
    

    Object.create()方法创建的对象时,属性是在原型下面的。

属性的读取

读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。

var obj = {
  p: 'Hello World'
};

obj.p // "Hello World"
obj['p'] // "Hello World"```

请注意,如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理。

var foo = 'bar';

var obj = {
  foo: 1,
  bar: 2
};

obj.foo  // 1
obj[foo]  // 2

上面代码中,引用对象 obj 的 foo 属性时,如果使用点运算符,foo 就是字符串;如果使用方括号运算符,但是不使用引号,那么 foo 就是一个变量,指向字符串 bar。

数字键可以不加引号,因为会自动转成字符串。

var obj = {
  0.7: 'Hello World'
};

obj['0.7'] // "Hello World"
obj[0.7] // "Hello World"

注意,数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。

var obj = {
  123: 'hello world'
};

obj.123 // 报错
obj[123] // "hello world"

修改或增加对象的属性

点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。

var obj = {};

obj.foo = 'Hello';
obj['bar'] = 'World';

JavaScript 允许属性的“后绑定”,也就是说,你可以在任意时刻新增属性,没必要在定义对象的时候,就定义好属性。

var obj = { p: 1 };

// 等价于
var obj = {};
obj.p = 1;

删除对象的属性

delete 命令用于删除对象的属性,删除成功后返回 true。

var obj = { p: 1 };
Object.keys(obj) // ["p"]

delete obj.p // true
obj.p // undefined
Object.keys(obj) // []

上面代码中,delete 命令删除对象 obj 的 p 属性。删除后,再读取 p 属性就会返回 undefined,而且 Object.keys 方法的返回值也不再包括该属性。

注意,删除一个不存在的属性,delete 不报错,而且返回 true。

var obj = {};
delete obj.p // true

上面代码中,对象 obj 并没有 p 属性,但是 delete 命令照样返回 true。因此,不能根据 delete 命令的结果,认定某个属性是存在的。

只有一种情况,delete 命令会返回 false,那就是该属性存在,且不得删除。

var obj = Object.defineProperty({}, 'p', {
  value: 123,
  configurable: false
});

obj.p // 123
delete obj.p // false

上面代码之中,对象 obj 的 p 属性是不能删除的,所以 delete 命令返回 false。另外,需要注意的是,delete 命令只能删除对象本身的属性,无法删除继承的属性。

var obj = {};
delete obj.toString // true
obj.toString // function toString() { [native code] }

上面代码中,toString 是对象 obj 继承的属性,虽然 delete 命令返回 true,但该属性并没有被删除,依然存在。这个例子还说明,即使 delete 返回 true,该属性依然可能读取到值。

查看对象的属性

in 运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回 true,否则返回 false。它的左边是一个字符串,表示属性名,右边是一个对象。

var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true

in 运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。就像上面代码中,对象 obj 本身并没有 toString 属性,但是 in 运算符会返回 true,因为这个属性是继承的。

这时,可以使用对象的 hasOwnProperty 方法判断一下,是否为对象自身的属性。

var obj = {};
if ('toString' in obj) {
  console.log(obj.hasOwnProperty('toString')) // false
}

属性的遍历:for...in 循环

var obj = {a: 1, b: 2, c: 3};

for (var i in obj) {
  console.log('键名:', i);
  console.log('键值:', obj[i]);
}
// 键名: a
// 键值: 1
// 键名: b
// 键值: 2
// 键名: c
// 键值: 3

for...in 循环有两个使用注意点。

  • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
  • 它不仅遍历对象自身的属性,还遍历继承的属性。

举例来说,对象都继承了 toString 属性,但是 for...in 循环不会遍历到这个属性。

如果继承的属性是可遍历的,那么就会被 for...in 循环遍历到。但是,一般情况下,都是只想遍历对象自身的属性,所以使用 for...in 的时候,应该结合使用 hasOwnProperty 方法,在循环内部判断一下,某个属性是否为对象自身的属性。

var person = { name: '老张' };

for (var key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key);
  }
}
// name