【无标题】

对象继承的集中方式

1.原型链继承

将父类的实例作为子类的原型

// 父类
function Person(name,friend) {
    this.name = name;
    this.friend = friend
    this.say = function () {
        console.log(`My name is ${this.name}.`);
    };
}
Person.prototype.listen = function () {
    console.log('I am listening...');
}

function Student(no) {
    this.no = no;
}
Student.prototype = new Person(); // 将父类的实例作为子类的原型
Student.prototype.constructor = Student; // 修正
Student.prototype.sayNo = function () {
        console.log(`My No. is ${no}`);
    }

const stu = new Student(1);
stu.sayNo(); // My No. is 1
stu.say(); // My name is undefined.
stu.listen(); // I am listening...

console.log(stu instanceof Student); // true
console.log(stu instanceof Person); // true

缺点:

  • 从父类中继承的属性没有添加到子类实例自身上,而是需要通过原型链访问,打印stu对象, 继承的属性是看不到的
  • 在子类实例上,直接修改对象上继承的属性, 是给本对象添加了一个新属性(不论是基本类型还是引用数据类型)
  • 在子类实例上,访问继承的属性,如果是引用数据类型,通过引用修改该引用数据,则多个子类实例之间会互相影响
var stu1 = new Student(1)
var stu2 = new Student(2)

// 1.直接修改继承的属性,是给本对象添加了一个新属性
stu1.name = "kobe"
console.log(stu1) // {no: 1, name: 'kobe'}
console.log(stu2) // {no: 2}

// 2.访问继承的引用数据类型,获取引用, 修改引用中的值, 会相互影响
stu1.friends.push("kobe")
console.log(stu1.friends) // ['kobe']
console.log(stu2.friends) // ['kobe']
  • 无法实现多继承(即一个子类继承多个父类的属性或方法)?
  • 创建子类实例时,不能向父类构造函数传参数,因为这个对象是一次性创建的没有办法定制化

2.借助构造函数继承

在子类型构造函数的内部调用父类型构造函数,使用父类的构造函数来增强子类 实例,等同于复制父类的实例给子类(不使用原型)。
  • 因为函数可以在任意的时刻被调用;
  • 因此通过apply()和call()方法让父类构造函数内部的this指向子类实例;
function Person(age) {
  this.name= name;
  this.age = age
}
Person.prototype.say = function(){
console.log('啦啦啦~')
}

function Student(no, name, age) {
    Person.call(this, name); 
    this.no = no;
}
const stu = new Student(233, 'Ben', 18);
stu.say();// 无法继承到父类原型的方法

console.log(stu2 instanceof Student2); // true
console.log(stu2 instanceof Person); // false

缺点:

  • 只能继承父类实例上的属性,不能继承原型上的属性

3.组合式继承

原型链继承借用构造函数继承方法的结合,用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。

// 父类: 公共属性和方法
function Person(name, age, friends) {
  this.name = name
  this.age = age
  this.friends = friends
}

Person.prototype.eating = function() {
  console.log(this.name + " eating~")
}

// 子类: 特有属性和方法
function Student(name, age, friends, sno) {
  Person.call(this, name, age, friends) //1.借用构造函数
  this.sno = 111
}

var p = new Person()
Student.prototype = p  //2.原型链继承
p.constructor = Person

Student.prototype.studying = function() {
  console.log(this.name + " studying~")
}

const stu = new Student("why", 18, ["kobe"], 111)
console.log(stu) // {age:18,name:'why',sno:111,firends:['kobe']}

缺点:

1.无论在什么情况下,都会调用 两次父类构造函数
  •  一次在创建子类原型的时候
  •  另一次在子类构造函数内部(也就是每次创建子类实例的时候)
2.所有的子类实例事实上会 拥有两份父类的属性
  • 一份在当前的实例自己里面(也就是person本身的),
  • 另一份在子类对应的原型对象中(也就是 person.__proto__里面);
当然,这两份属性我们无需担心访问出现问题,因为默认一定是访问实例本身这一部分的;

4.原型式继承

为了理解这种方式,我们先再次回顾一下JavaScript想实现继承的目的:重复利用另外一个对象的属性和方法.
  • 这种继承方法不是通过构造函数来实现的,而是通过一个原型式继承函数实现。
  • 最终的目的:student对象的原型指向了person对象。
// 原型式继承函数1
function createObject1(o) {
  var newObj = {}
  Object.setPrototypeOf(newObj, o)
  return newObj
}

// 原型式继承函数2
function createObject2(o) {
  function Fn() {}
  Fn.prototype = o
  var newObj = new Fn()
  return newObj
}

// 原型式继承3
// Object.create可以替代上述两个函数的功能
var obj = {
  name: "why",
  age: 18
}
var info = Object.create(obj)
console.log(info)
console.log(info.__proto__===obj) //true

优点:

创建一个新的对象(子类实例),这个对象的原型是传入的对象(父类构造函数的实例),子类实例就可以通过原型链访问到父类实例的属性。

缺点:

  • 继承的属性和方法只能通过原型链访问,并没有在子类实例上添加属性和方法,
  • 具有和原型链继承一样的缺点,继承的引用数据类型,修改了之后会影响其他实例

5.寄生式继承

  • 寄生式(Parasitic)继承是与原型式继承紧密相关的一种思想
  • 寄生式继承的思路是结合原型式继承和工厂模式的一种方式
  • 即创建一个封装继承过程的函数, 该函数在内部以某种方式来增强对象,最后再将这个对象返回

6.寄生组合式继承 

7.混入方式寄生多个对象

8.ES6类继承

你可能感兴趣的:(javascript,前端,开发语言)