原型与原型链与继承

embedded/2024/9/23 9:36:08/

原型、原型链与继承

构造函数

构造函数创建实例的过程

1.创建一个新对象

2.将空对象的__proto__指向构造函数的原型

3.修改构造函数中this指向,将构造函数中的this指向实例对象,执行构造函数中的代码,给这个新对象添加属性和方法(通过call/apply)

4.返回新对象(实例对象)

手写new

  function Person(name, age) {this.name = name;this.age = age;}//   const p = new Person("张三", 40);//   console.log(p);function myNew(Fn, args) {let obj = {};obj.__proto__ = Fn.prototype;Fn.apply(obj, args);return obj;}const p = myNew(Person, ["张三", 40]);console.log(p);

原型

原型与原型链都都源于对象并服务于对象,他们是js实现继承的一种模型

原型:每个构造函数都有一个prototype属性,它就是通过构造函数创建(new)的对象的原型,在他上面定义的属性和方法都可以被对象实例所共享

  function Fn(name) {this.name = name;this.speak = function () {console.log("Chinese");};}Fn.saySomething = function () {console.log("i love you");};const fn = new Fn("张三");fn.speak();    // Chinesefn.saySomething();  // 报错 saySomething is not a function// 但是如果我将saySomething放到Fn的prototype中,所有实例都可以使用这个方法function Fn(name) {this.name = name;}Fn.prototype.saySomething = function () {console.log("i love you");};const fn = new Fn("张三");const fn2 = new Fn("李四");fn.saySomething();  // i love youfn2.saySomething();  // i love you

protype对象

1>proto属性

js中,除去null外任何对象内部都会自带__proto__属性;prototype是一个对象,所以存在__proto__属性

fn.__proto__==>Fn.prototype

2>constructor属性

对象的的prototype里面有个constructor属性,指向当前对象所属的构造函数

Fn.prototype.constructor==>Fn构造函数

在这里插入图片描述

每个构造函数都有一个prototype属性,指向原型对象,原型对象上有个constructor属性指回构造函数,每个实例对象都有一个__proto__属性,指向构造函数的prototype

原型链

原型链:每个对象都有一个__proto__属性,指向他的原型,也就是构造函数的prototype;当访问一个对象的属性时,它首先会在自己身上找,如果没有找到就会往原型上面找,如果还是没找到,他会继续往上,直到找到为止,如果查找到最顶层的原型对象中也没有找到,就结束查找,返回undefined,这样就会形成一条链,就是原型链

原型链的终点是null,Object.prototype .__proto__指向null

在这里插入图片描述

继承

继承的本质是重写原型对象

原型链继承

可以继承属性和方法

      function Boy() {this.gender = "male";}function Girl() {this.gender = "female";this.color = "pink";}Girl.prototype.getColor = function () {return this.gender;};Girl.prototype.getGender = function () {return this.gender;};// 创建Girl实例,并加那个该实例赋值给Boy原型Boy.prototype = new Girl();const baby = new Boy();console.log(baby.getColor()); // pinkconsole.log(baby.getGender()); // male

缺点:

  1. 多个实例对引用类型的操作会被篡改
  2. 在创建子类型是不能向超类型的构造函数中传参
      function Boy() {this.colors = ["blue", "green"];}function Girl() {}Girl.prototype = new Boy();const baby1 = new Girl();baby1.colors.push("black");console.log(baby1.colors); //["blue", "green","black"]// baby2和baby1的构造函数一样,都到原型上找,指向一致,color是引用类型,所以baby2也跟着变了const baby2 = new Girl();console.log(baby2.colors); //["blue", "green","black"]

构造函数继承

通过call()、apply()来实现继承

call

apply

缺点:只能继承父类的实例属性和方法,无法继承原型属性、方法

      function Boy() {this.gender = "male";// 继承BoyGirl.call(this);}function Girl() {this.gender = "female";this.color = "pink";}Girl.prototype.getGender = function () {return this.gender;};const baby = new Boy();console.log(baby); // {gender:"female",color:"pink"}console.log(baby.getGender()); //报错: baby.getGender is not a function

组合继承

使用原型链实现原型属性和方法的继承,通过构造函数实现对实例属性的及继承

      function Boy(name) {this.name = name;this.colors = ["blue"];}Boy.prototype.getName = function () {return this.name;};function Girl(name, age) {// 先利用构造函数继承来继承实例对象的属性和方法Boy.call(this, name);this.age = age;}// 在利用原型继承来继承原型Girl.prototype = new Boy();const baby1 = new Girl("baby1", 0);const baby2 = new Girl("baby2", 1);console.log(baby1);console.log(baby1.getName()); // baby1console.log(baby2.getName()); // baby2baby1.colors.push("pink");// 实例自身已经有colors属性,就不会到原型上找,所以不会相互影响console.log(baby1.colors); // ['blue','pink']console.log(baby2.colors); // ['blue'] 

组合继承融合了两者的有点,避免了他们的缺陷

原型式继承

object()对传入其中的对象执行了一次浅复制,将构造函数F的原型直接指向传入的对象

      function object(obj) {function F() {}F.prototype = obj;return new F();}const person = {name: "test",colors: ["blue"],};const p = object(person);p.name = "hello";p.colors.push("pink");console.log(p.colors); // ["blue","pink"]const p2 = object(person);p2.name = "world";p2.colors.push("white");console.log(p2.colors); // ["blue","pink",'white']console.log(p.colors); // ["blue","pink",'white']

缺点:

  1. 多个实例对引用类型的操作会被篡改
  2. 在创建子类型是不能向超类型的构造函数中传参

寄生式继承

在原型的基础上,增强对象,返回构造函数

      function object(obj) {function F() {}F.prototype = obj;return new F();}function objectAnother(origin) {const clone = object(origin);clone.greet = function () {alert("hello");};return clone;}const person = {name: "test",colors: ["blue"],};const p = objectAnother(person);p.colors.push("pink");p.greet();console.log(p.colors); // ['blue',pink]const p2 = objectAnother(person);p2.colors.push("white");console.log(p2.colors); // ["blue","pink",'white']console.log(p.colors); // ["blue","pink",'white']

寄生组合式继承

function inheritPrototype(subType, superType){const prototype = Object.create(superType.prototype);  // 创建对象,创建父类原型的一个副本prototype.constructor = subType;  // 增强对象,弥补因重写原型而失去的默认的constructor 属性subType.prototype = prototype;  // 指定对象,将新创建的对象赋值给子类的原型
}// 父类初始化实例属性和原型属性
function SuperType(name){this.name = name;this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){alert(this.name);
};// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function SubType(name, age){SuperType.call(this, name);this.age = age;
}// 将父类原型指向子类
inheritPrototype(SubType, SuperType);// 新增子类原型属性
SubType.prototype.sayAge = function(){alert(this.age);
}const instance1 = new SubType("xyc", 23);
const instance2 = new SubType("lxy", 23);instance1.colors.push("2"); // ["red", "blue", "green", "2"]
instance1.colors.push("3"); // ["red", "blue", "green", "3"]

ES6类继承extends

ES6支持类的继承,它背后依旧是使用原型链

      class Person {static myStaticProp = 42; // 静态属性:class本身的属性,不是定义到实例对象(this)上面的属性constructor(name, ageNum) {this.name = name;this.age = ageNum;}getDoubleAge() {return 2 * this.age;}}const p = new Person("潘周聃", 29);console.log(p.getDoubleAge()); // 58class personalInfo extends Person {constructor(name, age, info) {super(name, age); // 不能在调用super之前引用thisthis.info = info;}getInfo() {return this.info;}}const p2 = new personalInfo("潘周聃", 29, "硕士毕业于苏黎世联邦理工大学");console.log(p2.getDoubleAge()); // 58console.log(p2.getInfo()); //"硕士毕业于苏黎世联邦理工大学"

扩展

instanceof

基本语法

返回布尔值

      const arr = [1];console.log(arr instanceof Array); // trueconsole.log(arr instanceof Object); // trueconsole.log(null instanceof Object); // false

原理

右侧的对象(构造函数)的原型对象prototype)是不是在左侧对象的原型链上

手写instanceof

      function myInstLnce(L, R) {if (typeof L !== "object" || L === null) return false;const origin = R.prototype;L = Object.getPrototypeOf(L);while (true) {if (L === origin) return true;L = Object.getPrototypeOf(L);}}

apply call bind

call、apply、bind的区别

都可以改变this的指向

1》call 和 apply 改变this指向的同时,会调用函数,bind改变函数的this指向,不会调用

2》call 和apply 的传参不同,第一个参数都是this指向的执行上文,后面的参数都是作为改变this指向的函数的参数;call 第二个参数开始,可以接收任意个参数。每个参数会映射到相应位置的 Function 的参数上,apply第二个参数必须是数组或者类数组,它们会被转换成类数组,传入 Function 中,并且会被映射到 Function 对应的参数上

      function fn(uname, age) {this.name = uname;this.age = age;}const obj = {name: "张三",age: 20,};fn.call(obj, "test", 22);console.log(obj);fn.apply(obj, ["测试", 33]);console.log(obj);

3》在使用上的区别:

call:对象的继承,在子构造函数这种调用父构造函数,但是改变this指向,就可以继承父的属性

function superClass () { this.a = 1; this.print = function () {  console.log(this.a);  
}}
function subClass () {  superClass.call(this);   this.print();
}
subClass(); // 1

apply的应用场景: Math.max,获取数组中最大、最小的一项

const max = Math.max.apply(null, array)  // 和Math.max(...array)效果一样

第一个参数,是一个对象。 函数的调用者,将会指向这个对象。如果不传,则默认为全局对象 window

注意点:

多次 bind 时只认第一次 bind 的值

箭头函数中this不会被改变


http://www.ppmy.cn/embedded/97449.html

相关文章

【Hot100】LeetCode—56. 合并区间

目录 1- 思路贪心 2- 实现⭐56. 合并区间——题解思路 3- ACM 实现 原题连接:56. 合并区间 1- 思路 贪心 1- 先对数组根据首元素排序2- 遍历数组,根据 left 和 right 维护一个左右区间。判断是否重叠 不重叠:收集结果重叠:更新 r…

Linux VSFTP 部署与配置

一、VSFTP 简介与应用 VSFTP(Very Secure FTP Daemon)是一款功能强大、安全可靠的FTP服务器软件,广泛应用于Linux/Unix系统中。它提供了高效的文件传输服务,并具备诸多安全特性,如用户认证、权限控制、SSL/TLS加密等。…

HarmonyOS布局详解:掌握线性布局中的Row与Column

概述 线性布局(LinearLayout)是开发中最常用的布局,通过线性容器 Row 和 Column 构建。线性布局是其他布局的基础,其子元素在主轴方向(水平方向或垂直方向)依次排列。根据排列方向的不同,开发者…

什么是BERT?工程快速入门

基本介绍 全称是Bidirectional Encoder Representations from Transformers。BERT翻译成中文通常被称为“双向编码器表征法”或简单地称为“双向变换器模型” Bidirectional:是双向神经网络,这个在学习 RNN 时候我们就了解到如何使用双向 RNN 让每一个…

ffmpeg实现mvk到mp4的格式转换

本文记录在使用ffmpeg转换mkv格式的文件到mp4时的一些问题. 像素格式问题 常规的转换命令可能如下 ffmpeg -i input.mkv -c:v libx264 -c:a aac output.mp4这里用libx264进行视频编码,aac进行音频编码,按道理是有很好的通用性的,但是笔者多次在转换后…

html+css网页设计 淘宝登录页面

htmlcss网页设计 淘宝登录页面 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源码 1,访…

机器人等方向学习和研究的目标

核心目标类似: 学习一个知识点用时越来越短,研究一个系统效率越来越高。 目标 没有目标是常态,十分普遍。 但其实,目标也可以很宽泛。 感谢朋友们一直以来的鼓励帮助,倍感荣幸,非常感谢。-CSDN blink-…

LeetCode 205 同构字符串

题目 给定两个字符串 s 和 t ,判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。 每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上&#xff0c…