说透javascript的继承

说透javascript的继承,第1张

1. 什么是继承?

继承出现在两个构造函数之间,如果A构造函数的属性或者方法被B构造函数使用了,那么我们说B继承A构造函数

2. 什么是构造函数?
function Person(name, age) {
	this.name = name
	this.age = age
}
Person.prototype.sayhi = () => {
	console.log('hello world')	
}
let xiaoming = new Person('小明', 18)
console.log(xiaoming) // {}
xiaoming.sayhi() // hello wordl

上图的Person就是一个构造函数, 构造函数可以通过new创建**实例对象,**实例对象可以使用构造函数上的属性和方法

3. 扩展一下,new关键字的原理及其实现
// new关键字原理
1. 创建一个空对象
2. 将对象的__proto__指向构造函数的prototype
3. 将构造函数的this指向将要要被创建的对象
4. 返回实例对象

// 实现new关键字
function myNew(fn, ...args) {
	let obj = {} // 创建一个空对象
	obj.__proto__ = fn.prototype // 将对象的__proto__指向构造函数的prototype
	let res = fn.apply(obj, args) // 将构造函数的this指向将来要被创建的对象
	return typeof obj === 'object' ? res : obj // 返回对象
}

// 下面我们来验证一下我写的new
let Person() {
	this.name = name
	this.age = age
}
Person.prototype.sayhi = () => {
	console.log('hello world')	
}
let xiaoming = new Person('小明') 
console.log(xiaoming)// Son {name: '小明', age: 18}
let xiaoming1 = new myNew(Person,'小明',18) 
console.log(xiaoming1)// Son {name: '小明', age: 18}
4. 下面说一说继承的几种实现方式及优缺点 4.1. 原型链继承
// 实现: 子类.prototye = 父类的实例
// 优点: 可以继承父类的属性和方法
// 缺点: 继承的属性不在自己身上,需要通过子类.prototype 向上寻找
function Parent(name) {
	this.name = name
}
Parent.prototype.sayhi = () => {
	console.log('hello 原型链继承')	
}
let p = new Parent('xiaoming')

function Son(age) {
	this.age = age
}

Son.prototype = new Parent('潘晨阳') // 注意这里

let s = new Son(18)

console.log(s.name) // '潘晨阳'
s.sayhi() // hello 原型链继承
4.2. 借用构造函数继承
// 实现: 父类.call(this, 参数)
// 优点: 继承的属性在自己身上不用向上找
// 缺点: 不能继承父类方法
function Parent(name) {
	this.name = name
}
Parent.prototype.sayhi = () => {
	console.log('hello 借用构造函数继承')	
}
let p = new Parent('xiaoming')

function Son(name, age) {
	this.age = age
	Parent.call(this, name) // 注意这里
}
let s = new Son('pcy',18)
console.log(s.name) // 'pcy'
s.sayhi() // 报错 c.say is not a function
4.3. 组合继承
// 实现: 用原型链继承 继承方法 + 借用构造函数继承 继承属性
// 优点: 可以继承父类的属性方法,且属性在自己身上
// 缺点: 会调用两次父类构造函数
function Parent(name) {
	this.name = name
}
Parent.prototype.sayhi = () => {
	console.log('hello 组合继承')	
}
let p = new Parent('xiaoming')

function Son(name, age) {
	this.age = age
	Parent.call(this, name) // 注意这里 借用构造函数继承属性  调用一次父类
}
Son.prototype = new Parent() // 注意这里 原型继承继承方法  有调用一次父类

let s = new Son('pcy',18)
console.log(s.name) // 'pcy'
s.sayhi() // hello 组合继承
4.4. 原型式继承
// 实现: 创建一个过渡构造函数,将构造函数的prototype指向父对象, 返回过渡构造函数的实例化对象出去 即下面object函数的实现
// 优点: 减少内存开销, 直接在父类内部创建对象, 根据已有对象衍生新对象
// 缺点: 由于是浅克隆,所以实例共享的对象属性如果是引用值,会受污染
function object(obj) {
	function F () {}
	F.prototype = obj
	return new F()
}

function Parent(name) {
	this.name = name
}
Parent.prototype.sayhi = () => {
	console.log('hello 原型式继承')	
}
let p = new Parent('xiaoming')

let son = object(p) // 注意这里
console.log(son.name) // xiaoming
son.sayhi() // hello 原型式继承
4.5. 寄生继承
// 实现: 使用原型式继承创建对象 => 增强对象 => 返回对象
// 优点: 根据一个对象克隆创建另一个对象,并增强对象
// 缺点: 无法复用, 每个实例上都要创建一遍
 function object(obj) {
   // 1. 创建一个过渡构造函数
   function F () {}
   // 2. 将构造函数的prototype指向父对象
   F.prototype = obj
   // 3. 返回过渡构造函数的实例化对象
   return new F()
 }
 function enhance_object(obj) {
   // 1. 创建新对象
   let new_obj = object(obj)
   // 2. 增强
   new_obj.age = 18
   new_obj.enhance_methods = () => {
     console.log('增强的方法')
   }
   // 3. 返回对象
   return new_obj
 }

 // 父构造函数
 function Parent(name) {
   this.name = name
 }
 Parent.prototype.sayhi = () => {
   console.log('hello 寄生继承')	
 }
 let p = new Parent('xiaoming')

 let son = enhance_object(p) // 注意这里

 console.log(son.name) // xiaoming    父的属性
 console.log(son.age) // 18           增强的属性
 son.sayhi() // hello 寄生继承         父的方法
 son.enhance_methods() // 增强的方法   增强的方法

所以寄生继承就是给原型式继承穿了个马甲,看起来更像继承了(上面介绍的原型式继承更像是对象复制), 创建对象 ==> 增强对象 ==> 返回对象,这么一个过程,可以理解为寄生继承

4.6. 寄生组合继承(最理想的一种继承方式)

关于Object.create这个方法不了解的,点击这里查看

// 实现:原型式继承 / Object.create + 借用父类的构造函数
// Object.create也可以理解为原型式继承, 只不过原型式继承是es5的实现方式, es6推出新方法Object.create,其实就是对原型式继承的实现进行了封装 加强
// let back_obj = Object.create('传入的对象') 需要传入一个对象,将传入对象的_proto_提供给新创建的对象, 最后返回一个对象
// 优点: 只调用一次父类构造函数 即可继承父类属性和方法
function Parent(name) {
	this.name = name
}
Parent.prototype.sayhi = () => {
	console.log('hello 寄生组合继承')	
}
let p = new Parent('xiaoming')

function Son(name, age) {
	this.age = age
	Parent.call(this, name) // 注意这里 借用构造函数继承属性  只调用一次父类
}
Son.prototype = Object.create(Parent.prototype) // 将父类的prototype 给 子类的prototype

let s = new Son('pcy',18)
console.log(s.name) // 'pcy'
s.sayhi() // hello 寄生组合继承
4.7. ES6继承
// 实现: extends + super() 
// super的作用; 借用父类的构造函数,实例自身属性
class Parent{
  constructor(name) {
    this.name = name
  }
  say() {
    console.log('hello es6继承');
  }
}
let p = new Parent('潘晨阳')
console.log('父类实例对象', p);

class Son extends Parent{  // 相当于原型继承 子类.prototype = 父类实例
  constructor(name, age) {
    super(name)  // 相当于借用构造函数 Parent.call(this, name)  
    this.age = age
  }
}
let s = new Son('潘晨阳', 18)
console.log(s.name);// 潘晨阳
s.say() // hello es6继承
5. 结语

如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。
文章如有错误之处,希望在评论区指正🙏🙏。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/web/1297436.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存