ES6的class出现之前,js都是通过原型链来实现继承的。
显式原型和隐式原型
function Person(name,age){
this.name = name;
this.age = age
}
Person.prototype.sayHello = function() {
console.log('hi'+ this.name)
}
let p1 = new Person('jack',20)
p1.sayHello() // 'hi jack'
上述代码是最简单的关于原型继承的例子。
此时涉及两个名词:显式原型和隐式原型
- Person是构造函数,p1是实例对象
- 每一个函数都有prototype属性,称为
显式原型 - 每一个对象都有__proto__属性,称为
隐式原型(隐式意味着不能直接操作它) 显式原型(Person.prototype)是在函数声明时添加的,默认指向一个空对象A隐式原型(p1.__proto__)是在对象创建时添加的,同样指向对象A
原型链
以上例子,在实例对象p1上调用sayHello方法是如何成功的呢?
这里就涉及到原型和原型链的联动。
前置准备知识
栈和堆都用来存放什么?
简单来说,栈用来存放基础类型的变量和值、引用类型变量和其内存指向
堆用来存放引用对象
上面这幅手画的图是查找sayHello方法的整个过程。
- 首先在p1的构造函数中查找
- 然后查找
p1.__proto__,即沿着隐性原型链查找 - 发现原型对象上有这个方法,就取到了
假如我要查找p1.toString(),是如何找到该方法的呢?
- 首先在p1的构造函数中查找
- 然后查找
p1.__proto__,即沿着隐性原型链查找,没找到 - 再查找
p1.__proto__.__proto__,也就是js自带的Object对象 - 找到了该方法,返回
原型链的尽头是null对象,没找到就返回undefined。
ES6 class
上面的方式是ES5中,用来生产相似对象的方法。
ES6中,为了更加符合面向对象编程的特点,引入了class关键字,用来作为产生对象的模版。
class只是一种语法糖,以上代码完全相等于:
class Person {
// 简写实例属性
a=1
// 相当于构造函数
constructor(name,age){
this.name = name;
this.age = age
}
// 原型上的方法
sayHello(){
console.log('hi'+ this.name)
}
}
let p1 = new Person('jack',20)
p1.sayHello() // 'hi jack'
- constructor是默认添加的函数,会在new创建实例的时候自动执行
- constructor会自动返回一个对象实例,即this
- 声明实例属性可以声明变量,而不用非写在constructor中
- 可以使用static关键字,声明静态方法和属性,即在类本身调用的属性和方法
ES6本身没有实现private、piblic等关键字,这些关键字只能在typescript中使用。
关于继承
ES5 继承
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log('hi',this.name)
}
function Student(grade){
this.grade = grade;
}
// 实现对象继承
Student.prototype = new Person('jack',22);
let p1 = new Student(89);
console.log(p1.grade,p1.name);
p1.sayHello()
可以发现:Student.prototype = new Person('jack',22);
这句话实现了两个对象的继承关系。
Student的实例,既可以访问Person的实例属性和方法,又可以访问Person的原型对象。
就让student的原型对象指向一个Person的实例
以上就是ES5中的继承实现。
ES6 继承
// ES6
class Person1 {
a = 1
constructor(name,age){
this.name = name;
this.age = age
}
static sayHi(){
console.log('hi')
}
sayHello(){
console.log('hello')
}
}
class Student1 extends Person1 {
constructor(name,age,grade){
super(name,age)
this.grade = grade;
}
say(){
console.log(this.name,this.age,this.grade)
}
}
let p2 = new Student1('jack',22,89);
Student1.sayHi()
p2.say()
p2.sayHello()
console.log(p2.a,p2.name)
这句话class Student1 extends Person1实现了ES6中对象的继承。
其实就是ES5中实现的语法糖。