当前位置: 首页 > news >正文

原型与原型链与继承

原型、原型链与继承

构造函数

构造函数创建实例的过程

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不会被改变

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • uniapp 日常业务 随便写写 源码
  • 嵌入式软件--模电基础 DAY 2
  • 探索数字媒体产业园区的未来之路
  • 电子秤方案低功耗蓝牙芯片CST92P15
  • 结构体和类
  • PRVF-4037 : CRS is not installed on any of the nodes
  • zdpgo_gin_graceful 为zdpgo_gin框架打造的用于实现优雅退出的框架,当你需要程序优雅退出的时候可以考虑使用此框架
  • PCDN业务推荐
  • Transformer架构;Encoder-Decoder;Padding Mask;Sequence Mask;
  • 【区块链+金融服务】区块链在仓储融资的创新应用 | FISCO BCOS应用案例
  • Expo创建的React Native项目如何在Windows上进行打包
  • DVWA靶场配置相关问题解决
  • 获奖方案|趋动科技:资源池化释放AI算力价值
  • 【自动驾驶】ROS中的TF坐标变换(一):静态坐标变换
  • 花10亿裁6300人,这家网络巨头不好过
  • Android系统模拟器绘制实现概述
  • canvas绘制圆角头像
  • echarts花样作死的坑
  • es6(二):字符串的扩展
  • extjs4学习之配置
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • sessionStorage和localStorage
  • Webpack 4 学习01(基础配置)
  • 工程优化暨babel升级小记
  • 关于extract.autodesk.io的一些说明
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 前端之Sass/Scss实战笔记
  • 容器服务kubernetes弹性伸缩高级用法
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 三栏布局总结
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 译米田引理
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ‌内网穿透技术‌总结
  • # windows 运行框输入mrt提示错误:Windows 找不到文件‘mrt‘。请确定文件名是否正确后,再试一次
  • ## 基础知识
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (1)STL算法之遍历容器
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (pytorch进阶之路)扩散概率模型
  • (rabbitmq的高级特性)消息可靠性
  • (不用互三)AI绘画工具应该如何选择
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (二)linux使用docker容器运行mysql
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (力扣)循环队列的实现与详解(C语言)
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (十)c52学习之旅-定时器实验
  • (转)nsfocus-绿盟科技笔试题目
  • (转)德国人的记事本