手写js——继承
创始人
2024-04-21 18:40:52
0

原型链继承

所谓 函数 也就是 函数 Father其本身,也叫作构造函数 ,当一个函数被创建的同时,也会为其创建一个 prototype 属性,而这个属性,就是用来指向 函数原型,的我们可以把 prototype 理解为 Father的一个属性,保存着函数原型的引用。

  • 当访问一个对象的属性(包括方法时),首先查找这个对象自身有没有该属性。  
  • 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
  •  如果还没有就查找原型对象的原型(Object的原型对象)。依此类推一直找到为null为止。
  • __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
     
//一、原型链继承
function Father(){}
Father.prototype.name="father"
// Father.prototype.sex="man"
function Son(){}
Son.prototype.name="son"
Son.prototype.sex="woman"
var f=new Father()
Son.prototype=new Father()
Son.prototype.constructor=f;
var s=new Son();
console.log(s.name,"=============>son's name") //undefined
console.log(s.sex)

运行上述代码 会发现 s.name 结果为undefined 原因是我们在后面重新 改变了 Father.prototype的指向  所以找不到之前定义的name,所以在原型链定义属性要在 继承之后定义属性

function Father(){}
Father.prototype.name="father"
// Father.prototype.sex="man"
function Son(){}var f=new Father()
Son.prototype=new Father()
Son.prototype.constructor=f;
var s=new Son();
Son.prototype.name="son"
Son.prototype.sex="woman"
console.log(s.name,"=============>son's name") //undefined
console.log(s.sex)

 

优点:

父类方法可以复用

缺点 :

  • 父类的引用属性会被所有子类实例共享

  • 子类的构建实例不能像父类构造函数传参 

构造函数继承

 实现 new函数

当使用new关键字调用构造函数生成一个对象实例时,js做出的关键处理如下:

  • 1.创建一个新的对象,将构造函数内this指针指向新建对象。
  • 2.将新建对象的__proto__属性设置成构造函数的prototype属性,确保新建对象是构造函数实例。
  • 3.运行一遍构造函数,如果构造函数本身需要返回一个object或者array对象,则舍去新创建对象,返回构造函数需要返回的对象;否则返回新建对象

/*** @bug 这里假设new出来实例的都是object,不是array对象* @description 传入构造函数,以及构造函数参数,模拟构造函数返回一个新建实例* @param {function} constructor 需要传入的构造函数* @param {any} params 跟在constructor参数后面的所有参数,用来模拟传入constructor的参数* @returns 返回一个新的对象
*/
function newFn(constructor){var obj={}obj.__proto__=constructor.prototypevar params=Array.prototype.splice.call(arguments,1);constructor.apply(obj,params);return obj
}function Person(name,age){this.name=name;this.age=age
}
let Cat={name:"",age:""
}
let person=newFn(Person,"Florenza",20)
console.log(person)
console.log(person instanceof Person)
// console.log(cat)

基本思想

构造函数继承的基本思想就是利用call或者apply把父类中通过this指定的属性和方法复制(借用)到子类创建的实例中。因为this对象是在运行时基于函数的执行环境绑定的。在全局中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。call 、apply方法可以用来代替另一个对象调用一个方法。call、apply 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
new对象的时候(注意,new操作符与直接调用是不同的,以函数的方式直接调用的时候,this指向window,new创建的时候,this指向创建的这个实例),创建了一个新的实例对象,并且执行子类里面的代码,而子类里面用父类.call改变this指向,也就是说把this指向改成了指向新的实例,所以就会把SuperType里面的this相关属性和方法赋值到新的实例上,而不是赋值到SupType上面。所有实例中就拥有了父类定义的这些this的属性和方法。 

//二 构造函数继承
function Animal(){this.sex="male"
}
function Cat(){Animal.call(this)
}
console.log(new Cat().sex)

 

 优点:和原型链继承完全反过来

  • 父类的引用属性不会被共享

  • 子类构建实例时可以向父类传递参数

缺点:父类的方法不能复用,子类实例的方法每次都是单独创建的。

组合式继承

基本思想

组合继承有时候也叫伪经典继承,指的是将原型链和借用构造函数技术组合到一块,从而发挥二者之长的一种继承模式,其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它的自己的属性。

// 组合式继承
console.log("=============组合式继承=============")
function Animal2(){this.name="animal"this.sex="male"
}function Dog(){this.name="dog"Animal2.call(this);
}
Dog.prototype=new Animal2();
console.log(new Dog().sex,"dog sex")

优点:

  • 父类的方法可以被复用
  • 父类的引用属性不会被共享
  • 子类构建实例时可以向父类传递参数

缺点:

调用了两次父类的构造函数,第一次给子类的原型添加了父类的name, arr属性,第二次又给子类的构造函数添加了父类的name, arr属性,从而覆盖了子类原型中的同名参数。这种被覆盖的情况造成了性能上的浪费。

原型式继承

主要思想

原型式继承并没有使用严格意义上的构造函数,是通过借助原型基于已有的对象创建新对象,同时还不必创建自定义类型。使用原型式继承的主要思路:

核心:原型式继承的object方法本质上是对参数对象的一个浅复制

优点:

父类方法可以复用。

缺点:

  • 父类的引用属性会被所有子类实例共享

  • 子类构建实例时不能向父类传递参数

console.log("===========原型式继承===========")
var people={name:"peopel",friends:["tiger"]
}
function createObject(o){
function F(){}
F.prototype=o;
return new F();
}
let elephant=createObject(people);
elephant.friends.push("mouse")console.log(elephant.name)
console.log(elephant.friends)

 ECMAScript5新增Object.create( )方法规范了原型式继承。该方法接收了两个参数:一个用作新对象原型的对象和(可选)一个为新对象定义额外属性的对象。在传入一个参数的情况下Object.create( )和object( )方法的行为相同。如果传入两个参数,则Object.create( )的第二个参数与Object.defineProperties( )方法的第二个参数的格式相同:每个属性都是通过自己的描述符定义的。

 寄生式继承

接上面原型式继承,只在原型继承方法后面进行方法增强

即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,后再像真的是它做了所有工作一样返回对象。

console.log("===========寄生式继承==========")
//原型式继承方法
var people2={name:"peopel2",friends:["tiger"]
}
function createObject(o){
function F(){}F.prototype=o;return new F();
}
function createAnother(original){ var fn=createObject(original)fn.say=function(){console.log("another")}return fn
} 
var man=createAnother(people2)
man.say()

优缺点:

仅提供一种思路,没什么优点。

组合式寄生式继承

即使用 组合式继承( xxx.call的方式)也使用 原型式 继承

了解了组合继承和寄生继承之后就是寄生式组合继承了,它是通过借用构造函数来继承属性,通过原型链形式来继承方法,会解决2次调用父类函数以及复用率的问题。

console.log("============组合式寄生式继承===========")
function Animal3(){this.name="animal3"this.age=10;this.friends=["cat"]
}
Animal3.prototype.getSay=function(){console.log("animal-3")
}
function Pig(){Animal3.call(this)
}
function createObj(o){function fn(){}fn.prototype=o;return new fn();
}function inhertCreate(parent,child){let parentProto=createObj(parent.prototype);parentProto.constructor=child;child.prototype=parentProto;
}
// 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费
inhertCreate(Animal3,Pig)
var pig=new Pig();
console.log(pig)
pig.getSay()

 完美方式实现

ES6继承

主要思想

核心: ES6继承的结果和寄生组合继承相似,本质上,ES6继承是一种语法糖。但是,寄生组合继承是先创建子类实例this对象,然后再对其增强;而ES6先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

class A {}class B extends A {constructor() {super();}}ES6实现继承的具体原理:class A {}class B {}Object.setPrototypeOf = function (obj, proto) {obj.__proto__ = proto;return obj;}// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

相关内容

热门资讯

鸿蒙怎样还原安卓系统,系统切换... 你有没有想过,鸿蒙系统竟然能还原安卓系统?这听起来是不是有点像魔法一样神奇?没错,今天就要带你一探究...
电脑安卓转苹果系统,系统迁移攻... 你有没有想过,有一天你的安卓手机突然变成了苹果的忠实粉丝,想要跳槽到iOS的阵营呢?这可不是什么天方...
安卓xp系统下载地址,轻松获取... 你有没有想过,手机系统也能穿越时空?没错,今天我要给你揭秘的就是这样一个神奇的存在——安卓XP系统。...
安卓系统怎么清理相册,安卓系统... 手机里的相册是不是越来越臃肿了?每次打开都感觉像是在翻山越岭,找一张照片都要费老鼻子劲。别急,今天就...
安卓系统安装ios转移,轻松实... 你有没有想过,手机系统之间的转换竟然也能如此神奇?没错,今天就要来聊聊安卓系统安装iOS转移这个话题...
安卓系统与ios系统的优势,系... 你有没有想过,为什么你的手机里装的是安卓系统而不是苹果的iOS系统呢?或者反过来,为什么你的朋友用的...
安卓系统游戏如何升级,轻松实现... 亲爱的安卓玩家们,你是否也和我一样,对安卓系统游戏升级这件事充满了好奇和期待呢?每次游戏更新,都仿佛...
安卓系统蛋仔派对,安卓系统下的... 你有没有发现,最近你的手机里多了一个超级好玩的游戏?没错,就是安卓系统上的“蛋仔派对”!这款游戏可是...
坚果3安卓原生系统,深度体验原... 你有没有听说过坚果3这款手机?它可是最近在数码圈里火得一塌糊涂呢!今天,我就要来给你详细介绍一下这款...
安卓子系统点不开,排查与解决指... 最近是不是你也遇到了安卓子系统点不开的烦恼?这可真是让人头疼啊!别急,今天就来给你详细解析一下这个问...
安卓系统经常误删文件,如何有效... 你有没有遇到过这种情况?手机里的文件突然不见了,找来找去,怎么也找不到。别急,这可能是安卓系统的小调...
安卓51系统如何破解,轻松解锁... 安卓51系统如何破解——探索未知的技术边界在数字化时代,手机已经成为我们生活中不可或缺的一部分。而安...
安卓系统怎么换回主题,安卓系统... 亲爱的手机控们,你是不是也和我一样,对安卓系统的主题换换换乐此不疲呢?不过,有时候换着换着,突然发现...
黑莓安卓系统 太垃圾,令人失望... 你有没有用过黑莓的安卓系统?别告诉我你没有,因为现在这个系统真的是太垃圾了!是的,你没听错,就是那个...
修改安卓系统权限代码,安卓系统... 你有没有想过,你的安卓手机里那些神秘的系统权限代码?它们就像隐藏在手机里的秘密通道,有时候让你觉得既...
虚拟大师安卓系统教程,教程详解... 你有没有想过,手机里的世界可以变得更加神奇?今天,就让我带你一起探索虚拟大师安卓系统的奥秘吧!想象你...
基于安卓系统个人博客,轻松构建... 你有没有想过,在这个信息爆炸的时代,拥有一片属于自己的网络小天地是多么酷的事情啊!想象每天都能在这里...
安卓怎么传到苹果系统,从安卓到... 你是不是也有过这样的烦恼:手机里存了好多好用的安卓应用,可是一换到苹果系统,就发现这些宝贝们都不见了...
安卓改系统字体app,安卓系统... 你有没有想过,手机上的字体也能变得个性十足?没错,就是那个安卓改系统字体app,它可是让手机界面焕然...
安卓系统重启密码错误,破解与预... 手机突然重启了,屏幕上竟然出现了密码输入的界面!这可怎么办?别急,让我来帮你一步步解决这个安卓系统重...