《红宝书》第8章——类

531 阅读4分钟

刚开始学到“类”的时候还是有点懵懵懂懂,一直在思考,类到底是什么呢?原来,类其实是ECMAScript中新的基础性语法糖结构,实际上使用的都是一些原型和构造函数的概念。接下来开始对类的总结~

1. 认识类

定义有两种主要的方式:类生命和类表达式。类表达式在声明的时候不会被提升,而函数在定义的时候是非被提升的,且类受块级作用域限制。类名的首字母一般要大写

在把类表达式赋值给变量后,可以通过name属性取得类表达式的名称字符串。但是由于类受块级作用域不能在类表达式作用域外部访问这个标识符(类名)。

let Person = class PersonName {
}
let p = new Person();
console.log(Person.name);    // PersonName
console.log(PersonName);   // 报错

2. 类构造函数

constructor关键字用于在类定义块内部创建类的构造函数。其实刚开始看到这个我就在想类构造函数构造函数里面的construcor有什么区别呢?这个在后面会一一总结。在类构造函数中,方法名constructor会告诉解释器在使用new操作创建类的新实例时应该调用这个函数。构造函数如果没有定义的话,就相当于将构造函数定义为空函数。

a. 实例化

使用new调用类的构造函数会执行如下操作:

  1. 在内存中创建一个新对象
  2. 在这个新对象内部的[[Prototype]]指针被赋值为构造函数的prototype属性
  3. 构造函数内部的this被赋值为这个新对象(即this指向新对象)
  4. 执行构造函数内部的代码(给对象添加属性)
  5. 如果构造函数返回非空对象,则返回该对象;否则返回刚创建的新对象。 重要

使用new操作符实例化Person的操作等于new调用其构造函数。如上面的第五点,此时构造函数相当于被定义为空函数,返回的就是刚创建的新对象,因此p1===p2的值为真。

class Person { }
let p1 = new Person();
let p2 = new Person().constructor;
console.log(p1 === p1);   //true

console.log(p1);打印结果如下:实例.png

默认情况下,类构造函数会在执行之后返回this对象。构造函数返回的对象会被用作实例化的对象,如果没有引用新创建的this对象,那么这个对象会被销毁。不过,如果返回的不是this对象而是其他对象,那么这个对象不能通过instanceof操作符检测出跟类有关联,因为这个对象的原型指针没有被修改。

我是通过下面这个例子理解这段话的。p1在实例化的时候,没有返回值,默认返回刚创建的新对象。而对于p2,由于传了一个true的参数,返回值发生了改变,不再是this对象,所以此时p2 instanceof Person的结果为false。

class Person {
    constructor(override) {
        this.foo = 'foo';
        if (override) {
            return {
                bar: 'bar'
            }
        }
    }
}
let p1 = new Person();
let p2 = new Person(true);
console.log(p1 instanceof Person);  // true
console.log(p2 instanceof Person);  // false

类构造函数与构造函数的主要区别就是调用类构造函数必须使用new操作符。普通构造函数如果不使用new调用,那么就会以全局的this(通常是window)作为内部对象。而类构造函数如果没有使用new的话会报错。

function Person {}
class Ball {}
let p = Person();  // 把window作为this来构建实例
let b = Ball();    // 没有new,报错
// 使用对类构造函数的引用创建一个新实例
let b1 = new Ball();  let b2 = new b1.constuctor;

类构造函数实例化之后会成为普通的实例方法(当仍然需要new创建)。因此,在实例化之后可以在实例上引用它。

b. 把类当成特殊函数

类中定义的constructor方法不会被当成构造函数(此时Person.constructor是一个普通函数),在对它使用instanceof操作符时会返回false。但是,如果在创建实例时直接将类构造函数当成普通构造函数来使用。instanceof操作符的返回值会反转:

class Person {}
let p1 = new Person();
console.log(p1.constructor === Person);         // true 
console.log(p1 instanceof Person);              // true
console.log(p1 instanceof Person.constructor);  // false 这个constructor不会被当成构造函数
let p2 = new Person.constructor();
console.log(p2.constructor === Person);         // false
console.log(p2 instanceof Person);              // false
console.log(p2 instanceof Person.constructor);  // true

对于p1

实例.png instanceof的内部机制就是检测构造函数的prototype属性是否出现在某个实例对象的原型链上。由上图对p1的打印可以看出,Person是存在于p1的原型中的,因此返回true,而Person.constructor是不存在的,所以返回false。

总结

类本质就是一个语法糖,相比之下类构造函数更符合面向对象编程的思维,与构造函数的用法是大致相同的,两者的本质是相同的,只不过在调用类构造函数的时候必须使用new操作符,而且类构造函数在写的时候比较美观。

今天做完对类的笔记,感觉对构造函数的理解又深了一点。如果有什么问题请大佬们多多指教!