js进阶之原型和原型链

132 阅读3分钟

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方法是如何成功的呢?

这里就涉及到原型和原型链的联动。

前置准备知识

栈和堆都用来存放什么?

简单来说,栈用来存放基础类型的变量和值、引用类型变量和其内存指向

堆用来存放引用对象

b0953fba914895cb.jpg

上面这幅手画的图是查找sayHello方法的整个过程。

  1. 首先在p1的构造函数中查找
  2. 然后查找p1.__proto__,即沿着隐性原型链查找
  3. 发现原型对象上有这个方法,就取到了

假如我要查找p1.toString(),是如何找到该方法的呢?

  1. 首先在p1的构造函数中查找
  2. 然后查找p1.__proto__,即沿着隐性原型链查找,没找到
  3. 再查找p1.__proto__.__proto__,也就是js自带的Object对象
  4. 找到了该方法,返回

原型链的尽头是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'
  1. constructor是默认添加的函数,会在new创建实例的时候自动执行
  2. constructor会自动返回一个对象实例,即this
  3. 声明实例属性可以声明变量,而不用非写在constructor中
  4. 可以使用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中实现的语法糖。