JS基础(六)对象(上)

欢迎阅读,本篇主要介绍JS中的对象。

前言

我们工作中常听到用的对象,但你真的了解它吗?希望本篇对你学习对象有所帮助。

1. 语法

对象可以通过两种形式定义:声明(文字)形式和构造形式。

1
2
3
4
5
 // 对象的文字语法:
var myObj = {
key: value,
...
};
1
2
3
// 对象的构造形式:
var myObj = new Object();
myObj.key = value;

两种方式声明的对象是一样的。唯一的区别是:在文字声明中你可以添加多个键值对,但在构造形式中你必须逐个添加属性。一般来说,我们使用文字声明创建对象。

2. 类型

  1. 类型
    在JS中一共有种基本类型(语言类型):
    string number boolean null undefined object
    简单基本类型(string、number、boolean、null、undefined)本身并不是对象,null有时会被当作一种对象类型,对null执行typeof null时会返回字符串“object”,但这其实只是语言的一个bug:不同的对象在底层都表示为二进制,在JS中二进制前三位都为0的话,会被判定为object类型,null的二进制表示是全0,自然前三位是0,所以执行typeof时会返回“object”。
    另外,还有一个错误的说法:“JS中万物皆对象”。实际上,JS中有许多特殊的对象子类型,我们称之为复杂基本类型。
    函数就是对象的一个子类型,从技术角度来说就是“可调用的对象”,函数本质上和普通对象一样,只是可调用,所以可以像操作其他对象一样操作函数,比如把函数当作另一个函数的参数。
    数组也是对象的一种类型,具备一些额外的行为,数组中内容的组织方式比一般对象要稍微复杂些。

  2. 内置对象
    JS中还有一些对象子类型,通常被称为内置对象。有些内置对象的名字和简单基础类型一样,不过实际上,他们的关系更复杂

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    String
    Number
    Boolean
    Object
    Function
    Array
    Date
    RegExp(正则表达式)
    Error
    ```
    在JS中,这些内置对象,实际上是一些内置函数,可以当作构造函数来使用,从而可以构造一个对应子类型的新对象:
    ``` bash
    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

    // 检查sub-type对象
    Object.prototype.toString.call(strObject); // [object, String]

    我们可以认为子类型在内部借用了Object中的toString()方法,strObject是由String构造函数创建的一个对象。原始值“I am a string”并不是一个对象,只是一个字面量,并且是一个不可变的值,如果要在这个字面量上执行一些操作,比如获取长度,访问其中某个字符等,那就需要将其转换为String对象。幸好,在必要时语言会自动把字符串字面量转换成一个String对象,并不需要我们显示创建一个对象,能使用文字形式时就不要使用构造形式。

    1
    2
    3
    var StrPrimitive = "I am a string";
    console.log(StrPrimitive.length); // 13
    console.log(StrPrimitive.chatAt(3)); // "m"

    上述例子中,我们可以直接在字符串字面量上访问属性或方法,是因为引擎自动把字面量转换成了String对象。
    同样的事情也会发生在数值字面量上:

    1
    42.359.toFixed(2);

    引擎会自动把42转换成new Number(42)。对于布尔字面量来说也是如此。
    null和undefined没有对应的构造形式,它们只有文字形式。相反,Date只有构造形式,没有文字形式。
    对于Object、Array、Function、RegExp来说,无论文字形式还是构造形式,他们都是对象,不是字面量。在某些情况下,相比用文字形式创建对象,构造形式可以提供额外的选项。由于这两种形式都可以创建对象,我们首选更简单的文字形式。建议只在需要那些额外选项时使用构造形式。
    Error对象很少在代码中显示创建,一般是在抛出异常时被自动创建,当然也可以用new Error(…)来创建,一般用不到。

3. 内容

对象的内容是由一些存储在特定命名位置的值组成的,我们称之为属性。
需要注意:“内容”并不是指这些值实际上被存储在对象内部,在引擎内部,这些值的存储方式多种多样,一般不会直接存在对象容器内部。存储在对象容器内部的是这些属性的名字,它们就像指针(引用),指向这些值真正存储的位置。

1
2
3
4
5
6
var myObject = {
a: 2
}

myObject.a; // 2
myObject["a"]; // 2

如果要访问myObjecta位置上的值,我们需要用.操作符或者[]操作符。.a语法通常被称为“属性访问”,[‘a’]语法通常被称为“键访问”。实际上它们访问的是同一个位置,并且会返回相同的值2,所以这两种是可以互换的。我们常见的是“属性访问”。
这两种方式区别:操作符要求属性名满足标识符命名规范,而[“…”]语法可以接受任意UTF-8/Unicode字符串作为属性名,如果你要引用名称为“Super-Fun”的属性,就必须使用[“Super-Fun”]语法访问,因为“Super-Fun”并不是一个有效的标识符属性名。

另外需要注意的是:在对象中属性名永远是字符串,如果你使用string(字面量)以外的其他值作为属性名,它首先也会被转成字符串。即使是数字也不例外,虽然在数组中使用的的确是数字,但是在对象属性名中数字会被转成字符串。

1
2
3
4
5
6
7
8
var myObject = {};
myObject[true] = "foo";
myObject[3] = "bar";
myObject[myObject] = "baz";

myObject["true"]; // "foo"
myObject["3"]; // "bar"
myObject["[object Object]"]; // "baz"

总结

本篇主要学习对象概念内容,下篇学习对象属性方法、数组、对象的复制、遍历等。