JS和Java一样都是面向对象的语言,不同之处在于采取的实现方式不一样,
面向对象的三个基本特征是 封装,继承,多态。而其目的就是为了实现代码复用。那么基于原型的面向对象如何实现代码复用呢?
首先要知道两点:
举个例子:
function Parent() {}
const parent = new Parent()
parent.__proto__ === Parent.prototype // true
接着就可以来看JS代码是如何实现代码复用的了,其实就是一条规则:
获取对象属性的时候,首先在对象上查找,如果找不到,再去该对象的原型对象上查找,也就是顺着__proto__上溯。
function Parent(name) {
this.name = name
}
Parent.prototype.sayHello = function() {
console.log('Hello World')
}
const parent = new Parent('papa')
console.log(parent.name) // papa
parent.sayHello() // Hello World
上面就是JS原型和原型链的基本原理了(我删减了细枝末节的东西,以免分散你的注意力),是不是非常简单。它们是实现基础,必须牢牢掌握。
知道了原型链的机制,接下来开始实现继承:
有两种很简单的想法:
构造函数继承思路很简单:在子类构造函数中,执行父类构造函数即可
function Parent(name) {
this.name = name
}
Parent.prototype.sayHello = function() {
console.log('Hello World')
}
const parent = new Parent('papa')
console.log(parent.name) // papa
parent.sayHello() // Hello World
function Son(name, age) {
Parent.call(this, name)
this.age = age
}
const son = new Son('son', 18)
console.log(son.name, son.age) // son 18
son.sayHello() // say Hello isnot a function
这种方式,不能继承Parent.prototype上的属性
原型链继承:son.prototype = new Parent()
function Parent(name) {
this.name = name
}
Parent.prototype.sayHello = function() {
console.log('Hello World')
}
const parent = new Parent('papa')
console.log(parent.name) // papa
parent.sayHello() // Hello World
function Son(name, age) {
Parent.call(this, name)
this.age = age
}
Son.prototype = new Parent()
const son = new Son('son', 18)
console.log(son.name, son.age) // son 18
son.sayHello() // Hello World
以上两者组合起来,就是最常用的伪经典(组合式)继承。
通过分析原型链可以看出组合式继承存在两个问题:
想要解决上面的两个问题,就有了完美继承。
首先考虑一下完美继承应该是什么样的?有了目标才能去做改进嘛:
首先看一下错误操作:
直接把子类原型指向父类原型 Son.prototype = Parent.prototype,由于不是指向副本,所以当子类修改了子类原型的时候,父类原型也会跟着变化,比如说,子类原型上添加了一个方法,那么直接就影响到了所有父类的对象。
接下来看正确操作:
又得解决一个新问题,如何创建一个父类原型的副本,也就是说如何创建Parent.prototype的副本(有点像深拷贝哦,不过并不是一个东西,不多说)?
很简单,直接new 一个就可以了。不过new的时候,要把实例属性置空
function create (o) {
function F() = {}
F.prototype = obj
return new F()
}
差不多就是上面的原理,Object.create()就是这样实现的原型是继承:
下面是实现原型链完美继承的三行代码,第一行创建父类原型副本,第三行修改子类原型为父类原型副本,第二行是JS函数的另一个小规则(因为会分散大家的注意力,就没讲,请自行了解)
const sonProtytype = Object.create(Parent.prototype)
sonProtytype.constructor = Son
Son.prototype = sonProtytype
最后附上完美继承代码:
function Parent(name) {
this.name = name
}
Parent.prototype.sayHello = function() {
console.log('Hello World')
}
function Son(name, age) {
Parent.call(this, name)
this.age = age
}
const sonProtytype = Object.create(Parent.prototype)
sonProtytype.constructor = Son
Son.prototype = sonProtytype