带你翻越JS三座大山之一的原型,原型链和继承

JS和Java一样都是面向对象的语言,不同之处在于采取的实现方式不一样,

  • Java采用了基于Class的方式而被人们熟知
  • JS选用的基于原型的方式就没那么出名了

面向对象的三个基本特征是 封装,继承,多态。而其目的就是为了实现代码复用。那么基于原型的面向对象如何实现代码复用呢?

首先要知道两点:

  1. JS的函数都有一个prototype属性指向一个对象,这个对象就是原型对象
  2. JS的对象都有一个__proto__属性指向一个对象,其就是创建本JS对象的构造函数所对应的原型对象

举个例子:

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原型和原型链的基本原理了(我删减了细枝末节的东西,以免分散你的注意力),是不是非常简单。它们是实现基础,必须牢牢掌握。

知道了原型链的机制,接下来开始实现继承:

有两种很简单的想法:

  1. 通过构造函数继承
  2. 通过原型链继承

构造函数继承思路很简单:在子类构造函数中,执行父类构造函数即可

			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

以上两者组合起来,就是最常用的伪经典(组合式)继承。

通过分析原型链可以看出组合式继承存在两个问题:

  1. 父类构造函数执行了两次
  2. 父类实例属性在子类原型对象上多存在了一份(查找的时候被子类实例属性覆盖,没有问题)

想要解决上面的两个问题,就有了完美继承。

首先考虑一下完美继承应该是什么样的?有了目标才能去做改进嘛:

  1. 父类的实例属性,应该成为子类的实例属性
  2. 子类的原型对象应该是父类原型对象的一个副本

首先看一下错误操作:

直接把子类原型指向父类原型 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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值