类和对象

JS 中是支持类的,可以使用 class 关键字来新建一个类:

class MyClass {
    // ...
}

通常我们约定类名为大写开头,之后每个单词首字母大写。

然后来看看类的内容。

this

this 关键字表示一个类对象本身,如果在类内需要使用自身的一些函数什么的,就可以通过 this 来调用。

构造函数

类可以拥有自己的构造函数,这个函数的名字只能是 constructor ,可以自定义参数。

class MyClass {
    constructor() {
        // ···
    }
}

构造函数的作用就是在创建一个类时执行的动作。

静态成员

静态成员表示整个类的所有对象都共享的值,可以使用关键字 static类内定义,也可以使用[类名].成员定义。

比如这样:

class MyClass {
    static inner = 1;
}

MyClass.outer = 2;

这两种方法的效果是一样的,但通常推荐第一种。

函数

类中可以定义函数,这时可以省略关键字 function

class MyClass {
    // 普通函数
    func() {
        console.log('function');
    }

    // 静态函数
    static funcStatic() {
        console.log('static function');
    }
}

类字段

在类中想声明一个字段的话,可以在类中的任意地方声明,并且在其他地方使用(当然声明还是要在使用之前,如果在声明之前就调用的话,会是一个undefined)。

使用 this.[字段名] 就可以声明一个类字段了。通常,我们会在构造函数里定义一写需要在这个类里使用的字段。当然在其他地方定义也是可以的。

class MyClass {
    // 在类中定义 name 字段
    constructor() {
        this.name = 'myclass';
    }

    // 第一次调用的时候,this.something是个undefined,所以就进入else
    // else 里会定义this.something
    sayName() {
        if (this.something) {
            console.log(this.something);
        }
        else {
            this.something = 123;
            console.log(this.name);
        }
    }
}

get 和 set 访问器

如果想设置一个只读或者只写的字段,就可以使用这个特性。只要在一个函数前,加上 get 或者 set 就好了。

class MyClass {
    get give() {
        return 5;
    }

    get value() {
        return this.someFiledWithALooooooooooooooogName;
    }

    set value(newValue) {
        this.someFiledWithALooooooooooooooogName = newValue;
    }
}

使用的时候依旧可以像一个普通变量一样使用他们。

let obj = new MyClass();

console.log(obj.five); // 5
obj.value = 123;
console.log(obj.value); // 5

继承类们

类也是可以继承的,使用 extend 关键字就好。在子类的构造函数里,使用 super 函数就可以调用父类的构造函数。

class MyClass {
    constructor(init) {
        this.value = init;
    }
}

class NewClass extend MyClass {
    constructor(initValue, elseInitValue) {
        super(initValue);

        this.init = elseInitValue;
    }
}

对象(Object)

类定义了怎么组织数据,然后在使用的时候需要使用关键字 new 来声明一个该类的实例,这个实例就是一个类对象。

创建指定类的对象

let obj = new MyClass();

来一个匿名类的对象

有的时候,我们会需要组织一些数据,但只用在一个地方,这时候再写类的话就会比较麻烦。所以我们可以直接用一个花括号来创建一个对象。

let obj = {
    name: 'obj',
    value: 'lalala',
    sayHello: function(name) {
        console.log('Hello, ' + name);
    },
    sayHi: (name) => {
        console.log('Hi! ' + name);
    }
};

在花括号内写的是一个个的“键-值”对(Key-Value Pair),之后就可以和普通对象一样使用了。在定义函数时,可以使用关键字 function ,也可以使用箭头函数,两个的效果一样。

需要注意的是,在每一个键-值对直接需要用逗号分隔。

对象是浅复制的

浅复制的意思就是很浅的复制(呸)。

好吧其实就是说,看上去像复制了,但实际上只是给之前的变量创建了一个别名,来看个栗子就好理解了。

let obj1 = { name: 'obj' };
let obj2 = obj1;
obj2.name = 'new name';

console.log(obj2.name); // 'new name'
console.log(obj1.name); // 'new name'

但如果你将拷贝后的对象完全改成另一个对象,原先的对象是不会跟着改变的。

let obj1 = { name: 'obj' }
let obj2 = obj1;
let obj3 = obj1;

obj2 = 123;
console.log(obj1); // { name: 'obj' }
obj1 = 456;
console.log(obj3); // { name: 'obj' }

如果想深度拷贝的话,需要用到下一节的方法。

一个看上去很厉害的词:解构

这一节的内容暂时无法在 node 中使用

JS 提供了一种语法,能够很方便的获取一个对象中的部分字段,这种语法叫做解构。

let obj = { name: 'obj', age: 12, isAlive: true };
let { name, age } = obj;

console.log(name); // 'obj'
console.log(age); // 12

这样,就不用在每次调用前加个 obj. 了。

解构还有一种语法,可以将整个对象展开,这样就能对一个对象进行深度拷贝了。用法很简单,就是在一个对象前加 ... 符号就好。

let obj = { name: 'obj', age: 12, isAlive: true };
let newObj = { ...obj };
obj.name = '???';

console.log(newObj.name); // 'obj'

其实对象是个字典(Dictionary)

什么是字典呢,就是有一个键,通过键可以查到对应的值。

对象里的字段名就是一个键,这个键的类型是一个字符串;字段的值就是一个键对应的值,所以你可以通过将键作为下标,来获取一个字段的值。

let obj = { name: 'obj' };
console.log(obj['name']); // 'obj'

这和用 . 调用没什么区别。

然后 JS 提供了一种叫 for...in 的方式,可以遍历一个对象内的所有字段。

let obj = { name: 'obj', age: 12, isAlive: true };
for (o in obj) {
    console.log(o + ': ' + obj[o]);
}

// 输出 'obj', 12, true