【JavaScript高级】07-ES5、ES6中实现继承,原型及原型链

news2024/11/16 23:44:02

ES5、ES6实现继承,原型及原型链理解

  • ES5实现继承
    • 对象和函数的原型
      • 对象的原型
      • 函数的原型
    • new、constructor
      • 函数原型上的属性
      • 优化通过构造函数创建对象
  • 原型链
    • 原型链实现的继承
    • 借用构造函数继承
    • 寄生组合实现继承

ES5实现继承

对象和函数的原型

对象的原型

JavaScript当中每个对象都有一个特殊的内置属性[[prototype]],这个特殊的对象可以指向另外一个对象。

  • 当我们通过引用对象的属性key来获取一个value时,它会触发[[Get]]的操作;
  • 这个操作会首先检查该对象是否有对应的属性,如果有的话就使用它;
  • 如果对象中没有改属性,那么会访问对象[[prototype]内置属性指向的对象上的属性;

那么如果通过字面量直接创建一个对象,这个对象也会有这样的属性吗?如果有,应该如何获取这个属性呢?

答案是有的,只要是对象都会有这样的一个内置属性;

获取的方式有两种:

  • 方式一:通过对象的_proto_属性可以获取到(但是这个是早期浏览器自己添加的,存在一定的兼容性问题);
  • 方式二:通过Object.getPrototypeOf方法可以获取到;
<script>
var obj={name: "kkk",
age:19
}
console.log(obj)

//获取对象的原型
console.log(obj.name,obj.age)console.log(obj._proto_)
console.log(object.getPrototypeOf(obj))
console.log(obj_proto__=== Object.getPrototypeof(obj))// true
//这个原型有什么用呢?
//当我们通过[[get]]方式获取一个属性对应的value时
//1>它会优先在自己的对象中查找,如果找到直接返回
//2>如果没有找到,那么会在原型对象中查找
console.log(obj.name)
console.log(obj.message)//null 

//测试自身对象中没有时是否在原型中寻找
obj._proto_.message ="Hello-world"
console.log(obj.message)//Hello-world
</script>

函数的原型

  • 所有的函数都有一个prototype的属性,(不是_proto_);对象中是没有这个prototype这个属性的,注意不要和上文中的[[prototype]]混淆

在这里插入图片描述

  • 当然,把函数看成是一个普通的对象时,它是具备_proto_(隐式原型),
  • 可使用函数名.prototype获取
    在这里插入图片描述
    那么函数的原型有什么作用呢?继续往下看就知道了

new、constructor

之前的笔记中写过new关键字相关知识(点击回顾)

  1. 在内存中创建一个新的对象(空对象);
  2. 这个对象内部的I[[prototype]]属性会被赋值为该构造函数的prototype属性;

那么也就意味着我们通过Person构造函数创建出来的所有对象的[prototype]]属性都指向Person.prototype:

在这里插入图片描述

代码验证(new操作原型赋值)

function Foo() {
    
}

var f1 = new Foo()
var f2 = new Foo()


console.log(f1.__proto__ === f2.__proto__) // true
console.log(f1.__proto__ === Foo.prototype) // true

将方法放在原型上:

function Foo() {
    
}

var f1 = new Foo()
var f2 = new Foo()


f1.__proto__.name = 'xt'
// Foo.prototype.name = 'xt'

console.log(f1.name) // xt
console.log(f2.name) // xt

在看上方的代码,当在f1.__proto__上面添加了name 属性后,f2.name也是可以打印出相同结果。

这说明了f1.proto 与 Foo.prototype与f2.__proto__他们的内存地址是相等的;而查找的规则就是先在自己里面找,找不到在沿着原型链去找。

函数原型上的属性

函数的prototype都用是有constructor属性的,用node执行时看不见的原因是enumerable默认为fasle,向这里我们改成了true,就可以看见了。

function Foo() {
    
}

Object.defineProperty(Foo.prototype, "constructor", {
    enumerable: true,
    configurable: true
})


// prototype.constructor 指向构造函数本身
console.log(Foo.prototype.constructor); // Foo;/如果foo.prototype,直接打印是空的,但是它不是空的。这里因为我们预选将可枚举的属性设置为ture所以可以看见
console.log(Foo.prototype.constructor.prototype.constructor); // Foo

(构造函数创建对象的内存表现)
在这里插入图片描述

constructor保存的是一个地址,这个地址指向构造函数本身。即函数中有prototype属性,也就是函数的原型对象,而这个原型中是有个constructor属性的,并且他的constructor属性的值是函数本身。

优化通过构造函数创建对象

通过构造函数创建对象是创建对象方法之一

function CreateObj(name, age) {
    this.name = name
    this.age = age

    this.getName = function() {
        console.log(this.name);
    }
}

var obj1 = new CreateObj('kkk', 19)
var obj2 = new CreateObj('kkk', 20)
console.log(obj1);
console.log(obj2);

但这种方法是有弊端的,比如getName 这个函数,在每次new CreateObj时他都会被创建,我们如果不想它每次被创建,就可以通过我们函数的原型来解决,如下:

function CreateObj(name, age) {
    this.name = name
    this.age = age
}

CreateObj.prototype.getName = function() {
    console.log(this.name);
}

var obj1 = new CreateObj('kkk', 18)
obj1.getName() // kkk


var obj2 = new CreateObj('kkk', 20)
obj2.getName() // kkk

原型链

var obj = {
    name: 'kkk'
}

obj.__proto__ = {}
obj.__proto__.__proto__ = {}
obj.__proto__.__proto__.__proto__ = {
    age: 18
}

console.log(obj.age); // 18

上面的代码,在obj中是没有age属性的,但是当我们在其__proto__上添加了age后他是可以找到的,说明他的查找,是一层一层的在查找。如下图:

(原型链的查找顺序)
在这里插入图片描述
我们知道了其查找规则,但是它会不会一直查找呢,答案是不会,因为它是有顶层的,为什么我们能确定他是有顶层的呢?

var obj = {
    name: 'kkk'
}

console.log(obj.age); // undefined

因为当obj 中没age属性时,我们直接打印obj.age,他是能马上给我们返回undefined的,那他的顶层又是什么呢?

var obj = {
    name: 'kkk'
}

console.log(obj.__proto__); // [Object: null prototype] {}

[Object: null prototype] {} 这个就是我们的顶层,这个是在node环境下的打印,看着不太明显,我们可以在浏览器中执行(浏览器中他为了我们方便调试,是有帮我们特殊处理的,所以我们会更容易看懂);这里的__proto__是null,其实我们对象原型的顶层,就是null

看了对象的,我们再来看函数的

function foo(params) {
        
    }

console.log(foo.prototype.__proto__.__proto__);// null

我们会发现看到这里依旧是null,同时我们也应该知道函数的prototype属性值中,也是有__proto__的。当然,我们还可以知道,这里的foo其实是继承Object的,所以原型链最顶层的原型对象就是Object的原型对象

原型链实现的继承

继承的主要目的,就是为了减少重复代码。
当我们学完了,原型链其实我们就可以通过原型链来自己实现继承。

// 父类
function Person() {
    this.name = 'kkk'
}

Person.prototype.eating = function() {
    console.log(this.name + 'eating');
}

// 子类
function Student() {
    this.sno = 1
}

// 通过原型继承
Student.prototype = new Person()

Student.prototype.studying = function() {
    console.log(this.name + 'studying');
}

var stu = new Student()
console.log(stu.name); // kkk

通过这种方法实现继承的弊端:

  • 当我们打印stu的时候(node中),我们是看不到name这个属性的,因为name在person中。
  • 如果是引用类型,可能会有传值引用问题。
  • 不能给Person传递参数

解决方法:借用构造函数继承

借用构造函数继承

function Person(name) {
    this.name = 'xt'
}
...
Student.prototype.studying = function() {
	Person.call(this, name)
    console.log(this.name + 'studying');
}
...

通过构造函数继承,我们前面的弊端解决了,但同时又产生了其他弊端,比如:

  • Person至少要被调两次
  • stu原型对象会多出一些属性,但是这些属性时没有存在的必要的

解决方法:寄生组合继承

寄生组合实现继承

// 父类
function Person(name) {
    this.name = name
}

Person.prototype.eating = function() {
    console.log(this.name + 'eating');
}

// 子类
function Student(name, sno) {
    Person.call(this, name)
    this.sno = sno
}

// 寄生式核心代码
function inheritPrototype(subType, superType) {
    subType.prototype = Object.create(superType.prototype)
    subType.prototype.constructor = subType
}

inheritPrototype(Student, Person)

Student.prototype.studying = function() {
    console.log(this.name + 'studying');
}


var stu = new Student('kkk', 1)
console.log(stu); // Student { name: 'kkk', sno: 1 }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/70786.html

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

相关文章

C#实现发送钉钉工作通知消息

一、实现效果 实现在钉钉的工作中心里面发送消息(比如发送【文本消息】、【markdown消息】等不同类型的内容),实现效果如下: 二、实现思路 2.1、了解钉钉学习路径图 钉钉开放文档 (dingtalk.com)https://open.dingtalk.com/document/org-roadmap 2.2、学习了解钉钉的接入…

cubeIDE开发, stm32的RS485/232串口通信开发要点

一、stm32串口通信 stm32串口通信一般是指通过UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;通用异步收发传输器传输数据&#xff0c;UART 作为异步串口通信协议的一种&#xff0c;工作原理是将传输数据的每个字符一位接一位地传输&#xff0c;其在…

数据库01_内存分页管理_分段管理_设备管理_IO处理_索引文件结构_文件目录_位示图---软考高级系统架构师008

可以看到数据库方面的考点.在架构里面考4,5分左右 这里只说比较重要的标红的考点. 然后我们来看ER图: 1.首先看一下实体的概念:实体是指的客观存在并相互区别的事物,可以举一个例子,比如一家超市,那么超市经理,员工,部门经理,业务员等等,这都是名词,都是属于实体. 2.然后…

知识图谱-KGE-语义匹配-双线性模型(打分函数用到了双线性函数)-2013:NTN(Neural Tensor Network)

【paper】 Reasoning With Neural Tensor Networks for Knowledge Base Completion 【简介】 本文是斯坦福大学陈丹琦所在团队 2013 年的工作&#xff0c;好像是发表在一个期刊上的。文章提出了用于知识库补全的神经网络框架 NTN&#xff08;Neural Tensor Network&#xff09…

Golang代码质量检查工具GolangCI-Lint(学习笔记)

Golang代码质量检查工具GolangCI-Lint 直接用下面go get的方式会出现报错 go get github.com/golangci/golangci-lint/cmd/golangci-lint解决方法 直接去 https://github.com/golangci/golangci-lint/releases 下载对应版本 go < 1.9 isn’t supported go1.9 is officia…

机器学习之数据分离与混淆矩阵

文章目录[TOC](文章目录)前言数据分离混淆矩阵實戰總結前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&#xff0c;本文就介绍了机器学习的基础内容之数据分离与混淆矩阵。 数据分离 前面我们…

多线程间的同步控制和通信

用多线程并发处理&#xff0c;目的是为了让程序更充分地利用CPU &#xff0c;好能加快程序的处理速度和用户体验。如果每个线程各自处理的部分互不相干&#xff0c;那真是极好的&#xff0c;我们在程序主线程要做的同步控制最多也就是等待几个工作线程的执行完毕&#xff0c;如…

weston input 概述

weston input 概述 零、前言 本文描述了有关于 weston (基于 wayland 协议一种显示服务器的实现) 中有关于输入设备管理的部分;为了聚焦于此,本文不会对 weston 整体或 wayland 协议进行过多的阐述. 考虑到读者可能存在不同的需求,采用分层次的描述方式,主要面向以下两类人群…

Android Studio 导入opencv异常报错紧急救援

Download OpenCV from SourceForge.net 1、下载Android demo之后导入Android Studio 如下图所示 报错信息如下 A problem occurred configuring root project opencv_samples. > Could not resolve all artifacts for configuration :classpath.> Could not find org.j…

校园失物招领毕业设计,学生失物招领系统设计与实现,毕业设计怎么写论文源码开题报告需求分析怎么做

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于web网页的失物招领网站系统&#xff0c;整个网站项目使用了B/S架构&#xff0c;基于java的springboot框架下开发&#xff1b;管理员通过后台录入信息、管理信息&#xff0c;设置网站信息&#xff0c;管理…

8_4、Java基本语法之线程的通信

一、问题的引入 使用两个线程打印 1-100。线程1, 线程2交替打印&#xff1f; 二、解决问题涉及的方法 涉及到的三个方法&#xff1a; 1.wait():一旦执行此方法&#xff0c;那么调用此方法的线程就会进入阻塞状态&#xff0c;并释放同步监视器。 2.notify():一个线程…

如何使用htmlq提取html文件内容

htmlq能够对 HTML 数据进行 sed 或 grep 操作。我们可以使用 htmlq 搜索、切片和过滤 HTML 数据。让我们看看如何在 Linux 或 Unix 上安装和使用这个方便的工具并处理 HTML 数据。 什么是htmlq&#xff1f; htmlq类似于 jq&#xff0c;但用于 HTML。使用 CSS 选择器从 HTML 文…

[安装] HIVE搭建环境

一、生产环境hive集群架构 参考&#xff1a; hive2.3.7安装记录 hive基础入门与环境的搭建 基础篇七 Hive-2.3.9安装与配置 大数据之Hive 集群搭建 完整使用 数仓&#xff08;十&#xff09;hive的Metastore机制 二、前言快读 Hive安装分类 主要是metastore的服务搭建方…

[rsync] 基于rsync的同步

环境 Linux&#xff1a;CentOs7.5 rsync: 3.1.2 rsync安装 一般安装系统时会自带rsync&#xff0c;可通过如下命令查看已经安装的版本信息 rsync --version如果系统未安装&#xff0c;可通过如下方式安装 yum安装【建议】 使用root用户执行yum安装 yum install -y rsync安…

代码随想录刷题记录day37 0-1背包+分割等和子集

代码随想录刷题记录day37 0-1背包分割等和子集 0-1背包 问题&#xff1a;有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 例题&#xf…

操作系统实验五 进程间通信-管道通信

实验目的 1.掌握利用管道机制实现进程间的通信的方法 2.了解利用消息缓冲队列机制实现进程间的通信的方法 3..了解利用共享存储区机制实现进程间的通信的方法 五个题目如下 1. 函数int pipe(int fd[2])创建一个管道&#xff0c;管道两端可分别用描述字fd[0]以及fd[1]来描述。需…

多元宇宙算法求解电力系统多目标优化问题(Matlab实现)【电气期刊论文复现与创新】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f389;作者研究&#xff1a;&#x1f3c5;&#x1f3c5;&#x1f3c5;本科计算机专业&#xff0c;研究生电气学硕…

SpringCloud项目使用Nacos进行服务的注册

本篇介绍Spring cloud项目使用Nacos作为注册中心来进行服务注册及服务发现&#xff0c;并进行简单的测试来验证。 一、简介 nacos是一个集服务发现、服务配置、服务元数据以及流量管理于一体的管理中心&#xff0c;能帮助我们更好的发现、配置和管理微服务。 注意&#xff1…

家政公司网站毕业设计,家政服务系统设计与实现,毕业设计论文源码开题报告需求分析

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于web网页的家政服务预约系统&#xff0c;整个网站项目使用了B/S架构&#xff0c;基于java的springboot框架下开发&#xff1b;管理员通过后台录入信息、管理信息&#xff0c;设置网站信息&#xff0c;管理…

JS项目打包之ROLLUP.JS入门

一、目的 Rollup是一个用于JavaScript的模块打包器&#xff0c;它将小块代码编译成更大、更复杂的东西&#xff0c;例如库或应用程序。它为JavaScript ES6版本中包含的代码模块使用了新的标准化格式&#xff0c;而不是以前的特殊解决方案&#xff0c;如CommonJS和AMD。ES模块可…