原型与原型链与继承

news2024/11/24 16:17:41

原型、原型链与继承

构造函数

构造函数创建实例的过程

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();    // Chinese
  fn.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 you
  fn2.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()); // pink
      console.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";
        // 继承Boy
        Girl.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()); // baby1
      console.log(baby2.getName()); // baby2
      baby1.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()); // 58
      class personalInfo extends Person {
        constructor(name, age, info) {
          super(name, age); // 不能在调用super之前引用this
          this.info = info;
        }
        getInfo() {
          return this.info;
        }
      }
      const p2 = new personalInfo("潘周聃", 29, "硕士毕业于苏黎世联邦理工大学");
      console.log(p2.getDoubleAge()); // 58
      console.log(p2.getInfo()); //"硕士毕业于苏黎世联邦理工大学"

扩展

instanceof

基本语法

返回布尔值

      const arr = [1];
      console.log(arr instanceof Array); // true
      console.log(arr instanceof Object); // true
      console.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.coloradmin.cn/o/2045887.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习笔记:注意力机制中多头注意力的实现

目录 介绍 模型 代码实现 引入库 单个注意力头 多个注意力头的实现 测试 思考 介绍 在注意力机制中,单个注意力学到的东西有限,可以通过对不同的注意力进行组合,学到不同的知识,以达到想要的目的。因此采用”多头注意力…

windows 安装 Mysql

一、安装Mysql 下载完成后直接双击进行安装 安装一路默认 如下图所示,在MySQL Servers/MySQL Server/MySQL Server 5.7的下方找到MySQL Server 5.7.41 - X64,然后选中它,点击两框之间的第一个箭头,将其移到右边的框中 点击Exe…

接口基础知识8_详解response header(响应头)

课程大纲 一、定义 HTTP响应头(HTTP Response Header):在HTTP协议中用于描述服务器响应的元数据。 它是服务器在响应客户端请求时,发送给客户端的一部分响应信息,包含了服务器的相关配置和响应内容的描述。 二、常见…

[机器学习]--KNN算法(K邻近算法)

KNN (K-Nearest Neihbor,KNN)K近邻是机器学习算法中理论最简单,最好理解的算法,是一个 非常适合入门的算法,拥有如下特性: 思想极度简单,应用数学知识少(近乎为零),对于很多不擅长数学的小伙伴十分友好虽然算法简单,但效果也不错 KNN算法原理 上图是每一个点都是一个肿瘤病例…

【C++深度探索】unordered_set、unordered_map封装

🔥 个人主页:大耳朵土土垚 🔥 所属专栏:C从入门至进阶 这里将会不定期更新有关C/C的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉 文章目录…

CSS继承、盒子模型、float浮动、定位、diaplay

一、CSS继承 1.文字相关的样式会被子元素继承。 2.布局样式相关的不会被子元素继承。(用inherit可以强行继承) 实现效果: 二、盒子模型 每个标签都有一个盒子模型,有内容区、内边距、边框、外边距。 从内到外:cont…

基于 Android studio 实现停车场管理系统--原创

目录 一、项目演示 二、开发环境 三、项目页面 四、项目详情 五、项目完整源码 一、项目演示 二、开发环境 三、项目详情 1.启动页 这段代码是一个简单的Android应用程序启动活动(Activity),具体功能如下: 1. **延迟进入登…

计算机网络三级笔记--原创 风远 恒风远博

典型设备中间设备数据单元网络协议物理层中继器、集线器中继器、集线器数据位(bit) binary digit二进 制数据的缩写HUB使用了光纤、 同轴电缆、双绞 线.数据链路层网卡、网桥、交换机网桥、交换机数据帧(Frame)STP、ARQ、 SW、CSMA/CD、 PPP(点对点)、 HDLC、ATM网络层路由器、…

SQL注入(cookie、base64、dnslog外带、搜索型注入)

目录 COOKIE注入 BASE64注入 DNSLOG注入—注入判断 什么是泛解析? UNC路径 网上邻居 LOAD_FILE函数 搜索型注入—注入判断 本文所使用的sql注入靶场为sqli-labs-master,靶场资源文件已上传,如有需要请前往主页或以下链接下载 信安必备…

【漫谈C语言和嵌入式002】嵌入式中的大小端

在计算机科学中,"端序"(Endianness)是指多字节数据类型(如整数或浮点数)在内存中的存储方式。主要分为两种:大端模式(Big-Endian)和小端模式(Little-Endian&am…

星戈瑞FITC-DXMS荧光素标记地塞米松不同方向的应用

FITC-DXMS,全称异硫氰基荧光素-地塞米松,是一种创新的科研试剂。他是由FITC-NH2的(-NH2)氨基与地塞米松的-OH(羟基)结合。它结合了地塞米松的特性和荧光素的高灵敏度标记技术,为医药研究、生物医…

栈与括号匹配——20、636、591、32(简中难难)

20. 有效的括号(简单) 给定一个只包括 (,),{,},[,] 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭…

springboot的学习(二):常用配置

简介 springboot的各种常用的配置。 springboot 项目是要打成jar包放到服务器上运行的。 打包 idea上使用maven打包的时候,会执行自动测试,可能会对数据库中的数据有影响,先点跳过测试,在点package。 运行 Windows上运行的…

新闻资讯小程序的设计

管理员账户功能包括:系统首页,个人中心,新闻类别管理,新闻信息管理,用户管理,管理员管理,系统管理 微信端账号功能包括:系统首页,新闻信息,我的 开发系统&a…

极市平台 | 如何通俗理解扩散模型?

本文来源公众号“极市平台”,仅用于学术分享,侵权删,干货满满。 原文链接:如何通俗理解扩散模型? 极市导读 还有谁没有看过diffusion的工作,席卷AI圈的diffusion到底是什么?本文作者用尽量通…

tcpdump快速入门及实践手册

tcpdump快速入门及实践手册 1. 快速入门 [1]. 基本用法 基本用法: tcpdump [选项 参数] [过滤器 参数] [rootkysrv1 pwe]# tcpdump -h tcpdump version 4.9.3 libpcap version 1.9.1 (with TPACKET_V3) OpenSSL 1.1.1f 31 Mar 2020 Usage: tcpdump [-aAbdDefhH…

Python爬虫使用实例

IDE:大部分是在PyCharm上面写的 解释器装的多 → 环境错乱 → error:没有配置,no model 爬虫可以做什么? 下载数据【文本/二进制数据(视频、音频、图片)】、自动化脚本【自动抢票、答题、采数据、评论、点…

3.2 实体-关系模型(ER模型)

欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: 工💗重💗hao💗:野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

Keycloak中授权的实现-转载

在Keycloak中实现授权,首先需要了解与授权相关的一些概念。授权,简单地说就是某个(些)用户或者某个(些)用户组(Policy),是否具有对某个资源(Resource&#xf…

基于SpringBoot的餐饮订单系统-计算机毕业设计源码39867

摘 要 随着现代生活节奏的加快和人们对便捷餐饮服务的需求不断增长,基于Spring Boot的餐饮订单系统的设计与实现成为当前研究的关键课题。本研究旨在开发一款包括首页、通知公告、餐饮资讯、餐饮菜单、商城管理等功能模块的系统,旨在提供便捷高效的餐饮订…