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

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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容