原型
在 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
定义的类有以下特点:
- 类内部定义的方法是不可枚举的,而传统的构造函数上的方法是可枚举的。
- 类中有默认的
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
在这个示例中,a
是Person
类的静态方法,可以直接通过类名来访问,而不需要创建实例。
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
属性,并且可以添加自己的age
和type
属性。
super
关键字还可以用于调用父类的方法。在子类中,可以通过super.method()
来调用父类的方法。