跳到主要内容

原型

在 JavaScript 中,每个函数都有一个prototype属性,它指向函数的原型对象。通过函数创建的对象会继承原型对象上的属性和方法。

下面是一个使用原型的示例:

function Person(name = 'zhangsan', age = 18) {
this.name = name;
this.age = age;
}

Person.prototype.say = function () {
console.log(`my name is ${this.name}, my age is ${this.age}`);
};

new Person('lisi', 19).say();

let person = new Person('wangwu', 20);

// 原型上的属性,全等于这个构造器
console.log(Object.getPrototypeOf(person).constructor === Person); // true
console.log(Person.prototype.constructor === Person); // true

在这个示例中,我们定义了一个Person构造函数,并在其原型对象上添加了一个say方法。通过new关键字创建的Person实例会继承原型对象上的say方法。

我们可以通过Object.getPrototypeOf()或者Person.prototype来访问原型对象,它们的constructor属性指向构造函数本身。

class

ES6 引入了class关键字,提供了一种更加面向对象的写法来定义类。使用class定义的类有以下特点:

  1. 类内部定义的方法是不可枚举的,而传统的构造函数上的方法是可枚举的。
  2. 类中有默认的constructor构造函数,如果不显式定义,会自动添加一个空的constructor

下面是一个使用class定义类的示例:

class Person {
// 构造函数
constructor(name = 'zhangsan', age = '18') {
// 实例化的属性:私有属性
this.name = name;
this.age = age;
}

// 构造函数原型上的方法:公有属性
// 公有属性不可枚举
say() {
console.log(`my name is ${this.name}, my age is ${this.age}`);
}
}

console.log(new Person());
console.log(typeof Person); // function

在这个示例中,我们使用class关键字定义了一个Person类,包含一个constructor构造函数和一个say方法。通过new关键字创建的实例会自动调用constructor构造函数进行初始化。

函数表达式

类也可以使用函数表达式的形式定义,如下所示:

let Person = class {
say() {
console.log(1);
}
};

new Person().say(); // 1

暂时性死区

类的声明不会提升,存在暂时性死区(Temporal Dead Zone,TDZ)。在类声明之前尝试访问类会抛出一个ReferenceError错误。

console.log(new Person()); // Uncaught ReferenceError: Cannot access 'Person' before initialization
class Person {}

公/私有属性

在类中,我们可以定义公有属性和私有属性。公有属性是在原型对象上定义的方法,可以被实例访问。私有属性是在构造函数中定义的属性,只能在类的内部访问。

// 独一无二值
const eat = Symbol();

class Person {
// 私有属性
a = 1;

// 公有属性
say() {
console.log(1);
}

// 私有方法
[eat]() {
console.log(2);
}
}

console.log(new Person().say()); // 1
console.log(new Person().eat()); // undefined

在这个示例中,a是私有属性,say是公有属性,[eat]是私有方法。私有属性和私有方法只能在类的内部访问,外部无法直接访问。

静态属性

静态属性是直接定义在类本身上的属性,而不是定义在原型对象上。静态属性可以使用static关键字来定义。

class Person {
static a() {
console.log(1);
}
}

console.log(Person.a()); // 1

在这个示例中,aPerson类的静态方法,可以直接通过类名来访问,而不需要创建实例。

extends:继承

extends关键字用于创建一个类作为另一个类的子类。子类会继承父类的公有属性和私有属性,但不会继承静态属性。

class Parent {
constructor(name = 'zs') {
this.name = name;
}
}

// 子类继承父类
class Child extends Parent {}

console.log(new Child()); // Child { name: 'zs' }

在这个示例中,Child类通过extends关键字继承了Parent类,Child的实例会继承Parent类的name属性。

super

super关键字用于调用父类的构造函数和方法。

在子类的构造函数中,必须先调用super()方法才能使用this关键字。super()方法会执行父类的构造函数,并将参数传递给父类的构造函数。

// 父类
class Parent {
constructor(name = 'zs') {
this.name = name;
}
}

// 子类
class Child extends Parent {
constructor(name = 'ls', age) {
// 调用父类的构造函数
super(name);
this.age = age;
this.type = '儿子';
}
}

console.log(new Child()); // Child { name: 'ls', age: undefined, type: '儿子' }

在这个示例中,Child类的构造函数中通过super(name)调用了父类Parent的构造函数,并传递了name参数。这样,Child类的实例就会继承父类的name属性,并且可以添加自己的agetype属性。

super关键字还可以用于调用父类的方法。在子类中,可以通过super.method()来调用父类的方法。