美文网首页
JS 继承的几种方式-(对象、原型、继承关系)

JS 继承的几种方式-(对象、原型、继承关系)

作者: 咸鱼不咸_123 | 来源:发表于2022-04-30 21:24 被阅读0次

1.继承的方案

能不能直接让子类的原型对象=父类的原型对象

  • 不要这么做,因为这么做就意味着以后修改了子类型的原型对象的某个引用类型的时候,父类原型对象的引用类型也会被修改
// * 父类 :放公共的属性和方法
function Person(name,age,friend){
  this.name=name;
  this.age=age;
  this.friend=friend;
}
Person.prototype.eating=function(){
  console.log(this.name+"在吃东西");
}

function Student(name,age,friend,sno){
  Person.call(this,name,age,friend)
  this.sno=111;
}

Student.prototype=Person.prototype;
Student.prototype.study=function(){
  console.log(this.name+"正在学习。");
}
var stu=new Student("wjy",20,['hyz'],111)
console.log(stu);
stu.eating()
stu.study()
var p=new Person("hyz",20,['zmj','lt']);
p.study();//* 由于Student是在原型上添加,study应该是Student类特有的,但是Person也会跟着有了 

1.1原型式继承函数

原型链继承的渊源

  • 这种模式要从道格拉斯 克罗克福德(Douglas Crockford,著名的前端大师,JSON的创立者)在2006年些的一篇文章说起:Prototypal Inheritance in JavaScript
  • 在这篇文章中,它介绍了一种继承方法,而且这种继承方法不是通过构造函数类实现的
  • 为了理解这种方式,我们先再次回顾一下JavaScript想实现继承的目的:重复利用另外一个对象的属性和方法

真实开发中,不建议以下这种方式:

  var newObj={};
  newObj.__proto__=o;
var obj={
  name:"wjy",
  age:18
}
//*  原型式继承函数
function createObject1(o){
  var newObj={};
  Object.setPrototypeOf(newObj,o);//设置 newObj的原型为o
  return newObj;
}
// * 道格拉斯
function createObject2(o){
  function Fn(){};
  Fn.prototype=o
  var newObj= new Fn()
  return newObj;
}
var info=createObject1(obj)
console.log(info);
console.log(info.__proto__);
var info2=createObject2(obj);
console.log(info2);
console.log(info2.__proto__);

但是后来Object.create实现的功能跟createObject1和createObject2是一样的。

// * Object.create实现的功能跟createObject1和createObject2是一样的
var info3=Object.create(obj);
console.log(info3);
console.log(info3.__proto__);

但是以上都是基于对象与对象之间的继承,最后我们是实现函数与函数之间的继承。

1.2寄生式工厂继承函数

寄生式(Parasitic)继承

  • 寄生式继承是与原型式继承紧密相关的一种思想,并且同样由道格拉斯 克罗克福德提出和推广的
  • 寄生式继承的思路是结合原型式继承工厂模式的一种方式
  • 即创建一个封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再将则这个对象返回

未使用寄生式继承之前

var personObj={
  running:function(){
    console.log("running");
  }
}

var stuObj=Object.create(personObj)
stuObj.name="wjy";
stuObj.study=function(){
  console.log(this.name+"在学习");
}

stuObj2.name="wjy2";
stuObj2.study=function(){
  console.log(this.name+"在学习");
}

stuObj3.name="wjy3";
stuObj3.study=function(){
  console.log(this.name+"在学习");
}

使用寄生式函数之后:

var personObj={
  running:function(){
    console.log("running");
  }
}
function createStudent(name){
  var stu=Object.create(personObj);
  stu.name=name
  stu.study=function(){
    console.log(this.name+"在学习");
  }
}

 

var stuObj=createStudent("wjy")
var stuObj2=createStudent("hyz")
var stuObj3=createStudent("zmj")
40.png
1.4 缺点
  • 不能明确对象的具体类型
  • study函数在每个对象中都会有一份重复,

1.43寄生组合式继承

现在我们来回顾一下之前提出的比较理想的组合继承。

  • 组合继承是比较理想的继承方式。但是存在两个问题
  • 问题1:构造函数会被调用两次:一次在创建子类原型对象的时候,一次是创建子类实例的时候
  • 问题2:父类型中的属性会有两份:一份在原型对象中,一份在实例中

事实上,我们现在可以利用寄生式继承将这两个问题给解决掉

  • 你需要先明确一点:当我们在子类型的构造函数中调用父类型.call(this,参数)这个函数的时候,就会将父类型中属性复制一份到子类中,所以父类型本身里面的内容,我们不再需要
  • 这个时候,我们还需要获取到一份父类型的原型对象中的属性和方法
 function Person(name,age,friend){
   this.name=name;
   this.age=age;
   this.friend=friend
 }

 Person.prototype.running=function(){
   console.log("running");
 }

 Person.prototype.eating=function(){
  console.log("eating");
}

function Student(name,age,friend,sno,score){ //* 这个只是暂时实现了属性的继承
  Person.call(this,name,age,friend);
  this.sno=sno;
  this.score=score;

}

Student.prototype=Object.create(Person.prototype);//* 实现了方法的继承
Object.defineProperty(Student.prototype,"constructor",{ //* 使用Object.defineProperty精确某个对象的属性
  value:Student, 
  configurable:true,
  enumerable:false,
  writable:true
})
// Student.prototype.constructor=Student

Student.prototype.studying=function(){
  console.log("studying");
}

var stu=new Student("wjy",20,["hyz"],1,90)

//* 打印的时候,会去打印stu.constructor.name
console.log(stu);//* Person { name: 'wjy', age: 20, friend: [ 'hyz' ], sno: 1, score: 90 }
stu.eating()
stu.running()
stu.studying()

console.log(stu.constructor.name);//* Person

41.png

这一步非常的重要:但是如果有很多函数都需要使用继承,这里我们实现一个工具类

 function inheritPrototype(subType,superType){
   subType.prototype=Object.create(superType);
   Object.defineProperty(subType.prototype,"constructor",{
     value:subType,
     configurable:true,
     enumerable:false,
     writable:true
   })
 }

整体实现变成了下面这种形式:

 function inheritPrototype(subType,superType){
   subType.prototype=Object.create(superType.prototype);
   Object.defineProperty(subType.prototype,"constructor",{
     value:subType,
     configurable:true,
     enumerable:false,
     writable:true
   })
 }
 function Person(name,age,friend){
   this.name=name;
   this.age=age;
   this.friend=friend
 }

 Person.prototype.running=function(){
   console.log("running");
 }

 Person.prototype.eating=function(){
  console.log("eating");
}

function Student(name,age,friend,sno,score){ //* 这个只是暂时实现了属性的继承
  Person.call(this,name,age,friend);
  this.sno=sno;
  this.score=score;

}

inheritPrototype(Student,Person);
Student.prototype.studying=function(){
  console.log("studying");
}

var stu=new Student("wjy",20,["hyz"],1,90)


console.log(stu);
stu.eating()
stu.running()
stu.studying()

console.log(stu.constructor.name);

2.对象的方法补充

  • hasOwnProperty

    • 对象是否有一个属于自己的属性(不是原型上的属性)
  • in操作符 for in操作符

    • 判断某个属性是否在某个对象或对象的原型上
  • instanceof

    • 用于检测构造函数的prototype,是否出现在某个实例对象的原型链
  • isPrototypeOf

    • 用于检测某个对象,是否出现在某个实例对象的原型链
var obj={
  name:"why",
  age:18
}
var info=Object.create(obj,{
  address:{
    value:"北京市",
    enumerable:true
  }
})
console.log(info);//* { address: '北京市' }
console.log(info.__proto__);//* { name: 'why', age: 18 }

//* hasOwnProperty:对象上是否有一份自己的属性,不是(原型上的属性)
console.log(info.hasOwnProperty("address"));//true
console.log(info.hasOwnProperty("name"));//false
console.log(info.hasOwnProperty("age"));//false

// * in 操作符 :不管是在当前对象上返回还是原型对象上返回 都是true
console.log("address" in info); //true
console.log("name" in info); //true
console.log("age" in info);//true

for(var key in info){
  console.log(key);
}
function inheritPrototype(subType,superType){
  subType.prototype=Object.create(superType.prototype);
  Object.defineProperty(subType.prototype,"constructor",{
    value:subType,
    configurable:true,
    enumerable:false,
    writable:true
  })
}
//* instanceof:用于检测构造函数的prototype,是否出现在某个实例对象的原型链上
function Person(){

}
function Student(){
  
}
inheritPrototype(Student,Person)

var stu=new Student();

console.log(stu instanceof Student);//true
console.log(stu instanceof Person);//true
console.log(stu instanceof Object);//true
var obj={
  name:"wjy",
  age:20
}
var info=Object.create(obj);
// * isPrototypeOf:检测某个对象,是否出现在某个实例对象的原型链
console.log(obj.isPrototypeOf(info));;//true

3.对象-函数-原型的继承关系

  • function Function

    • 其实var Function=new Function()
    • Function的[[prototype]]为Function.prototype
  • function Object

    • 其实 var Object=new Function()
    • Object的[[prototype]]为Function.prototype
  • function Foo

    • 其实var Foo=new Function()
    • Foo的[[prototype]]为Function.prototype
  • 直接通过Object创建的对象的原型都是Object.prototype

42.png

4.总结

继承、函数-对象-原型的关系.png

相关文章

  • JS 继承的几种方式-(对象、原型、继承关系)

    1.继承的方案 能不能直接让子类的原型对象=父类的原型对象 不要这么做,因为这么做就意味着以后修改了子类型的原型对...

  • JS对象(2)- 常见的继承方式

    本文归纳js中常见的几种继承方式。通过如下的对象作为示例的父对象: 1. 原型链继承 子类的原型指向父类的实例来实...

  • js实现继承的几种方式

    js实现继承有几种方式,这里我们主要探讨 原型链继承 构造继承 组合继承(原型链和构造继承组合到一块,使用原型链实...

  • 继承和函数进阶

    一. 继承的几种方式 1.继承 对象拷贝:for……in :父对象的属性拷贝给子对象。 2.原型继承 3.构造函数...

  • #js继承

    js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承)类式继承(构造函数间的继承) 类式继承是...

  • JS继承

    JS继承的几种实现方式 继承是指子类继承父类的属性和方法,要实现继承,首先我们需要有一个父类 原型链继承 原型链继...

  • JS高级 03

    JS实现继承的方式 1.属性拷贝 2.原型式继承 3.原型链继承 4.借用构造函数继承|伪对象继承|经典点继承 5...

  • JavaScript 10

    js继承的概念 1.通过原型链方式实现继承(对象间的继承) 2.类式继承(构造函数间的继承) 由于js不像Java...

  • js基础之实现继承的几种方式

    js 实现继承的方式有: 原型链继承; 构造函数继承; 组合继承(原型链继承 + 构造函数继承)(最常用);(原型...

  • 彻底弄清js继承的几种实现方式

    js有几种经典的继承方式。比如原型链继承、构造函数继承、组合继承、寄生组合继承、ES6继承。让我们一一分析并实现。...

网友评论

      本文标题:JS 继承的几种方式-(对象、原型、继承关系)

      本文链接:https://www.haomeiwen.com/subject/ipgpyrtx.html