js对象继承的六种方式的相关学习

一、理解一些基本概念

(1)理清构造函数、对象实例、原型对象prototype和对象原型__proto__的关

  1. 构造函数:用于建立对象的函数,经过new关键字生成对象实例。函数名通常首字母大写的。
  2. 对象实例:new关键字生成对象实例。
  3. 原型对象prototype:每一个函数都有一个prototype属性,它是一个指向原型对象的指针(原型对象在定义函数时同时被建立)。
  4. 对象原型:构造函数创建的每个实例对象都有一个__proto__属性,用来指向构造函数的原型对象prototype。可以理解为两者是等价的。

(2)原始值、引用值、值传递

  1. 原始值:表示单一的数据,保存原始值的变量是按值访问,操作存储在变量内存中的实际值。ES6有undefined,Null,Boolean,Number,String,Symbol等六种原始值
  2. 引用值:引用值是指由多个值构成的对象。实际操作对象时,访问的是保存对象的内存地址,即该对象的引用。引用值(对象)可以随时添加,修改和删除其属性的方法(动态属性)。
  3. 值传递:即将值复制给变量的过程。与原始值一样都是将变量中保存的信息赋值给另一个变量。

二、继承的六种方式

1.原型链继承(prototype继承)

function Person() {
    
    
  this.color = ['red', 'green']
}
Person.prototype.getColor = function() {
    
    
  return this.color
}
function My(name) {
    
    
  this.name = name
}
// 通过创建My的实例继承了Person
My.prototype = new Person()
var m1 = new My('ljc')
var m2 = new My('xlt')
m1.color.push('black')
console.log(m1.color, m2.color)

在这里插入图片描述

小结:原型对象的所有属性(引用值)会被所有实例共享,这会导致对⼀个实例的修改会影响另⼀个实例。

2. 构造函数继承(借助 call或apply)

function Person() {
    
    
  this.color = ['red', 'green']
}
Person.prototype.getColor = function() {
    
    
  return this.color
}
function My(name) {
    
    
  Person.call(this)
  this.name = name
}
var m1 = new My('ljc')
var m2 = new My('ljc')
m1.color.push('black')
console.log(m1.color)
console.log(m2.color)
console.log(m1.getColor())
call和apply传参的区别,前者是列表形式,而后者是以数组形式

在这里插入图片描述
在这里插入图片描述

小结:解决了原型链继承的缺点(原型对象的所有属性((引用值))共享),不能继承原型(prototype)属性或者方法。

3. 组合继承(结合原型链继承+构造函数继承)

 function Person(name) {
    
    
   this.name = name
   this.color = ['red', 'green']
}
Person.prototype.getColor = function() {
    
    
  return this.color
}
function My(name) {
    
    
   Person.call(this, name)
   // Person.apply(this, [name])
}
My.prototype = new Person()
// 手动挂上构造器,指向自己的构造函数
My.prototype.constructor = My
var m1 = new My('ljc')
var m2 = new My('xlt')
m1.color.push('black')
console.log(m1.color)
console.log(m2.color)
console.log(m1.getColor())
console.log(m2.getColor())

在这里插入图片描述

小结:解决了前两种的缺点,但无论在什么情况下,都会调用两次父类(Person)的构造函数,一次是在创建子类型原型的时候(My.prototype = new Person()),一次是在子类型构造函数的内部(Person.call(this, name)),存在效率问题。

4. 原型式继承

ES5之后使用Object.create()方法,这个方法接收两个参数:一是用作新对象原型的对象、二是为新对象定义额外属性的对象(可选参数)。

 const obj = {
    
    
   color: ['red', 'green', 'blue'],
   name: 'obj'
 }
 const t1 = Object.create(obj)
 const t2 = Object.create(obj)
 t1.color.push('balck')
 t1.name = 'ljc'
 console.log(t1.color, t2.color)
 console.log(t1.name, t2.name)

在这里插入图片描述

小结:对一个对象进行浅克隆创建另一个对象,同时继承该对象的原型属性,于是浅克隆,所以实例共享的对象属性如果是引用值,会受污染。

5. 寄生式继承

const obj = {
    
    
  color: ['red', 'green', 'blue'],
  name: 'obj',
  getName: function() {
    
    
    return this.name
  }
}
function clone(obj) {
    
    
  const newObj = Object.create(obj)
  newObj.getColor = function() {
    
    
    return this.color
  }
  return newObj
}
 const t1 = clone(obj)
 const t2 = clone(obj)
 t1.color.push('balck')
 t1.name = 'ljc'
 console.log(t1.color, t2.color)
 console.log(t1.name, t2.name)
 console.log(t1.getName(), t2.getName())
 console.log(t1.getColor(), t2.getColor())

在这里插入图片描述

小结:根据一个对象克隆创建另一个对象,并增强对象。通过寄生式继承给对象添加函数会导致函数难以复用,与构造函数模式类似。

6. 寄生式组合继承(构造函数+原型式继承)

function Person(name) {
    
    
  this.name = name
  this.color = ['green', 'red']
}
Person.prototype.getName = function() {
    
    
  return this.name
}
function My(name, age) {
    
    
  Person.call(this, name)
  this.age = age
}
function clone(Sup, Sub) {
    
    
  Sub.prototype = Object.create(Sup.prototype)
  Sub.prototype.constructor = Sub
}
clone(Person, My)
My.prototype.getAge = function() {
    
    
  return this.age
}
const m1 = new My('ljc', 24)
const m2 = new My('xlt', 23)
m1.color.push('black')
m2.color.push('white')
console.log(m1.color, m2.color)
console.log(m1.getName(), m1.getAge())

在这里插入图片描述

小结:基本可以解决前几种继承方式的缺点,只调用了一次父类的构造函数(Person.call(this, name)),提高效率。

三、ES6的extends关键字实现逻辑

class Person {
    
    
  constructor(name) {
    
    
    this.name = name
  }
  getName() {
    
    
    return this.name
  }
}
class My extends Person {
    
    
  constructor(name, age) {
    
    
    super(name)
    this.age = age
  }
}
const my = new My('ljc', 23)
console.log(my.getName(), my.age)
小结:this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。子类的构造函数必须执行一次super函数。

猜你喜欢

转载自blog.csdn.net/qq_45616003/article/details/124930686