手写js——继承

news/2024/12/5 4:47:22/

原型链继承

所谓 函数 也就是 函数 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);


http://www.ppmy.cn/news/3030.html

相关文章

教育的本质——采用不同学习方式,学习者在两周后还能记住的内容有多少

目录 一、学习金字塔模型 二、学习曲线 三、左右脑交替学习法 一、学习金字塔模型 “学习金字塔模型”,人们学习的效率一共分为七个层次: 第一层 ~ 第四层:这是我们最熟悉不过的形式,在学生时代,老师在上面讲课、…

冯诺依曼体系和操作系统概念

Ⅰ. 冯诺依曼体系结构 我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。 截至目前,我们所认识的计算机,都是有一个个的硬件组件组成 输入单元:包括键盘, 鼠标&#xf…

redis 主从复制(读写分离)集群搭建(含错误处理)

目录 1.概述 2.搭建 2.1.安装配置 2.2.认主 2.3.错误处理 3.原理 1.概述 当面临大流量时,redis可以采取集群的方式进行扩容,将压力分散到集群中的多个结点上去防止redis被打挂。redis的扩容方式有两种: 垂直扩容,即读写分离…

MATLAB | 一起来感受数学之美,第一届迷你黑客大赛回顾

Hey真的是好久不见,最近确实是比较忙更新频率也下来了,过段时间应该能恢复正常更新速度,之前给大家解说过今年举办的math is beautiful迷你黑客大赛,但这其实是第二届大赛,本期推送带大家回顾一下第一期大赛&#xff0…

大一作业HTML个人网页作业(宠物狗)

🎉精彩专栏推荐 💭文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (10…

第四部分 书写命令

目录一、显示命令二、命令执行三、命令出错四、嵌套执行 make五、定义命令包每条规则中的命令和操作系统 shell 的命令行是一致的。make 会按顺序一条一条地执行命令,每条命令的开头必须以[Tab] 键开头,除非,命令是紧跟在依赖规则后面的分号后…

jmeter接口测试之大家都来我家领豆子

一、测试目的: 2万用户不停请求云豆领取接口时,查看服务器内存占用情况,从而确认服务器内存占用异常的情况是否得到修复。 二、测试策略: 用2万个账号,以每2秒100次请求的速度向服务器发出请求,观察内存…

Latent Class Modeling lca

潜类别模型(Latent Class Modeling) 潜在类别分析(LCA)数据分析流程(详细版) - 简书 (jianshu.com) R数据分析:用R语言做潜类别分析LCA - 知乎 (zhihu.com) About Latent Class Modeling -…