详解JS原型与原型链

2023-11-13

目录

1.构造函数原型prototype

2.对象原型__proto__

3.constructor构造函数

4.原型链

5.原型对象中的this指向

6.扩展内置对象(原型对象的应用)


在ES6之前,我们面向对象是通过构造函数实现的。我们把对象的公共属性和方法放在构造函数里

像这样:

function student(uname,age) {
    this.uname = uname;
    this.age = age;
    this.school = function() {
        console.log('深圳理工大学');
    }
}
var stu1 = new student('小红',18);
var stu2 = new student('小紫',20);

但是构造函数方法虽然好用,可他存在浪费内存的问题。比如在上例中在构造函数里我们有一个函数,函数属于复杂数据类型,他会单独在内存中开辟一个空间。在这里我们创建了两个实例化对象,那么就会开辟两个内存空间来存放同一个函数,那就造成了内存浪费的问题。

1.构造函数原型prototype

构造函数通过原型分配的函数是所有对象所共享的
JavaScript规定,每一个构造函数都有一个prototype属性, 指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
所以在上例中我们就可以把公共函数放在原型对象的里面,这样就不会造成内存浪费

function student(uname,age) {
    this.uname = uname;
    this.age = age;
}
student.prototype.school = function() {
    console.log('深圳理工大学');
}
var stu1 = new student('小红',18);
var stu2 = new student('小紫',20);

那么我们就明白了原型是什么,他就是一个对象,我们称prototype为原型对象

原型的作用是什么呢?四个字,共享方法

2.对象原型__proto__

对象都会有一个属性__ proto__指向构造函数的 prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有_ proto_ 原型的存在。
在对象身上系统自己添加了一个__proto__指向我们构造函数的原型对象,所以__proto__对象原型和原型对象prototype是等价的。

我们验证一下,看看会输出什么:

console.log(stu1.__proto__ === student.prototype);

 最后输出是true证明对象原型__proto__和原型对象protptype是等价的

所以方法的查找规则就是首先看stu1对象身上是否有school方法,如果有就执行这个对象上的school,如果没有school这个方法, 因为有__proto__的存在,就去构造函数原型对象prototype身上去查找school这个方法

如果觉得还是不太懂,一图搞懂你的疑问:

3.constructor构造函数

对象原型(__proto__) 构造函数( prototype )原型对象里面都有一个属性 constructor属性, constructor我们称为构造函数,因为它指回构造函数本身。

我们打印一下student.prototype和stu1.__type__看看是否存在constructor属性:

console.log(student.prototype);
console.log(stu1.__proto__);

 可以看到这里面都有constructor,那constructor有什么作用呢?

constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数

我们知道在原型对象中我们可以定义那些公共的方法,但是如果公共的方法很多呢,我们通过对象的形式存储就方便多了:

student.prototype = {
    school:function() {},
    location:function() {}
}

然后我们输出一下原型对象的constructor看看有没有变化:

console.log(student.prototype.constructor);
console.log(stu1.__proto__.constructor);

最后的输出结果是这样:

 这是为什么呢,constructor不应该指向我们的student构造函数吗

因为我们通过student.prototype.school这样属于往原型对象里添加方法,但是我们刚才属于赋值,这样就把原先prototype里的东西都覆盖掉了,这样student.prototype就没有constructor了,没有constructor自然就指回不了student构造函数了

这个时候我们就需要手动的利用constructor这个属性指回原来的构造函数

student.prototype = {
    constructor:student,
    school:function() {},
    location:function() {}
}

我们再输出一下原型对象的constructor:

 这样我们就知道这个对象到底是通过哪个构造函数创建出来的了

现在我们更新一下构造函数、实例、原型对象三者关系图:

4.原型链

因为student原型对象也是一个对象,我们之前说了只要是对象就有对象原型的存在

那我们打印一下原型对象看看里面是否有原型:

function student(uname,age) {
    this.uname = uname;
    this.age = age;
}
student.prototype.school = function() {
    console.log('深圳理工大学');
}
var stu1 = new student('小红',18);
console.log(student.prototype);

输出结果:

可以看到原型对象里也有一个原型,又因为原型指向的是原型对象,那么我们这个student.prototype里面的__proto__指向的是谁呢?

我们打印一下:

console.log(student.prototype.__proto__);

可以看到指向的是这个constructor指向的是Object原型对象

Object原型对象是由谁创建出来的呢,毫无疑问是Object构造函数创建出来的。那么我们继续,Object原型对象也是一个对象,那它也有一个原型,这个原型指向的又是谁呢?

我们输出一下:

console.log(Object.prototype.__proto__);

最后的结果为空:

这样就到了最顶层了,这样我们把这些串起来就能得到一个原型链:

5.原型对象中的this指向

我们知道在构造函数中的this指向的是对象实例,那么原型对象里的函数,这个函数里的this指向的是谁呢?

 我们声明一个全局变量that,把原型对象里的this赋给that,看看这个that指向的是不是实例对象:

function student(uname,age) {
    this.uname = uname;
    this.age = age;
}
var that;
student.prototype.school = function() {
    that = this;
    console.log('深圳理工大学');
}
var stu1 = new student('小红',18);
stu1.school();
console.log(that === stu1);

输出结果:

所以原型对象函数里面的this指向的也是实例对象stu1 

6.扩展内置对象(原型对象的应用)

我们可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求和的功能

我们输出一下数组的原型对象,看看里面有什么方法:

console.log(Array.prototype);

这里没有给数组自定义求和的函数,那么我们往数组的原型对象里添加这个方法:

Array.prototype.sum = function() {
    var sum = 0;
    for(var i = 0;i<this.length;i++)
    {
        sum += this[i];
    }
    return sum;
}
var ss = new Array(4,5,3,6);
console.log(ss.sum());

我们自定义的sum方法里,this.length指的就是调用这个方法的数组的长度,因为在上一节中我们知道原型对象函数里面的this指向的也是实例对象

我们通过new方法创建一个数组实例对象,我们向数组的原型对象添加sum方法,那么我们的实例对象就可以调用它。

输出结果:

我们再打印一下数组的原型对象,看看里面有没有sum方法:

可以看到sum成功的添加到数组的原型对象里了,这样我们继续用到数组求和时,就可以直接调用sum方法了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

详解JS原型与原型链 的相关文章

随机推荐

  • Java字符串的加密解密

    为了保证程序的安全 经常采用数据加密的方法 Java 中提供了专门用于加密运算的类和接口 除了使用加密类和接口外 还可以通过多种方式实现字符串的加密 其中常用的就是获取字符串的字节数组 之后对字节数组中的每个字节都进行运算 得到新的内容 这
  • 程序的组成部分

    程序的组成部分 如上图所示 程序由 code 到 stack 地址由小到大 text 之前有一段不可读的区域 text rodata data bss heap stack 的大小是在程序编译阶段确定的 text 段 存放程序代码的区域 r
  • 深入AMS源码(二)—— ActivityManagerService对Activity的调度管理

    1 概述 在上一篇深入AMS源码 一 ActivityManagerService的基础知识文章介绍了AMS的基础信息 主要包括AMS中主要的数据结构 主要功能类和属性以及AMS中对生命周期的调度方式 本篇主要从源码的角度分析下AMS对Ac
  • 利用mysql实现上传和下载_文件的上传和下载功能

    1 文件上传下载展示和思路 1 图片的上传 是把图片数据以二进制的方式放入数据库 还是把图片上传到服务器的某个目录下 数据库记录这个目录 哪种方式好 数据库记录比较好 2 图片的上传下载在java中有哪些jar包支持 commons fil
  • android 通话蓝牙耳机,通话质量/降噪,安卓真无线耳机选哪个?Sony vs. 华为

    通话质量 降噪 安卓真无线耳机选哪个 Sony vs 华为 2021 02 14 22 07 12 15点赞 16收藏 31评论 作者用安卓手机 想选一个安卓手机用得好的降噪耳机 由于之前用朋友的airpods pro试过 在安卓手机上通话
  • [激光原理与应用-38]:《光电检测技术-5》- 光学测量基础 - 光调制

    目录 一 光调制概述 1 1 什么是光调制 1 2 激光的光调制方法 1 3 光调制的调制 二 直接调制法 三 腔内调制法 3 1 被动调制 3 2 主动调制 四 腔外调制法 五 新型光调制 5 1 基于强度调制 5 2 基于相位调制 5
  • 什么是前端(js)路由器

    概述 路由器是WebApp应用程序切换页面的枢纽 早起由于浏览器处理JS脚本的能力较弱 大部分逻辑和交互都在服务端完成 因此路由器这个词最早出现服务端 服务端通过客户端请求的URL解析出需要返回给客户端的HTML文档的路径 最终返回相对应H
  • 使用恒源云训练k210和v831模型

    使用恒源云训练k210和v831模型 因为在训练本地模型时 环境搭建容易出各种bug 费时费力 而恒源云上有大佬搭建浩的环境可以直接使用 所以记录一下我使用的流程 这是大佬视频链接 非常牛批 1 注册恒源云账号 首先直接在浏览器上搜索恒源云
  • Linux如何运行.Applmage文件

    1 什么是 Applmage文件 AppImage是新型的打包软件 它可以解决Linux上面的依赖问题 在使用上面相比其他的软件使用极为简单 所谓的 Applmage文件就是使用该打包软件打包出来的文件格式 2 怎么运行 Applmage文
  • html边框渐变颜色代码,css中如何实现border边框颜色渐变代码详解

    在css中 如果一条边框线有n像素的宽度 那么就可以对其设置n种不同的颜色 在代码中 我们可以定义一条宽为7px的实线 border 7px solid C8C8C8 不要在意此处设置的颜色 在firefox中后面的代码中会被新设置的颜色覆
  • C#中委托和事件

    目 录 1 1 理解委托 2 1 1 1 将方法作为方法的参数 2 1 1 2 将方法绑定到委托 4 1 2 事件的由来 6 1 2 1 更好的封装性 6 1 2 2 限制类型能力 9 1 3 委托的编译代码 10 1 4 NET 框架中的
  • python json.load与json.loads区别

    文章目录 json loads 与json load区别 json load示例 json loads示例 json loads 与json load区别 stackoverflow上的一个提问 In Python what is the
  • Windows下载安装redis

    Windows 安装Redis 64位下载地址 https github com microsoftarchive redis releases tag win 3 2 100 下载64位解压 启动redis 命令行 redis serve
  • MATLAB 的循环语句

    1 MATLAB while循环语法 在MATLAB 中 while循环的语法如下 while
  • 星星之火-24: 3G CDMA中为什么要把1转换成+1,0转换成-1才进行扩频与码分多址运算?+1,-1转换的本质是什么?

    扩频运算 宏观上看 是把一个比特的0或1 扩展成8比特 起到了扩频的效果 从效果上理解扩频 没有多大的问题 但为什么要把预先把1转换成 1 0转换成 1才能运算 1 1转换的本质是什么 不要小看这个不起眼的转换 它涉及到数字无线通信中的一个
  • 测试点击屏幕次数的软件_最新5G测试软件FAT详细介绍(包含锁频锁网锁PCI、信令/事件查看等功能)...

    推荐一款适用于高通芯片的4 5G网优测试APP FAT 推荐原因 界面简洁 操作简单 方便快捷 而且可以锁网锁频段和查看信令 1 打点测试 除Speedtest外 Attach Volte Ping FtpUp和FtpDown都可以打点测试
  • mongodb安装

    MongoDB安装 MongoDB下载 下载链接 https www mongodb com download center community 打开下载得到的文件 一直next 接下来一路next 等待安装完成 安装完成
  • ElementUI DatePicker直接赋值回显后,无法更改与删除问题

    前言 使用vue ElementUI 开发项目时 使用到e date picker组件选择日期范围dateRange 当默认dateRange直接赋值后 导致组件内回显的值无法删除且也无法修改 解决方案 setTemp 直接使用下列方式直接
  • Java中BigDecimal详解

    引言 Effective Java 一书中提到 float和double类型主要是为了科学计算和工程计算而设计的 它们执行二进制浮点数运算是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的 然而 它们并没有提供完全精确的结果 先
  • 详解JS原型与原型链

    目录 1 构造函数原型prototype 2 对象原型 proto 3 constructor构造函数 4 原型链 5 原型对象中的this指向 6 扩展内置对象 原型对象的应用 在ES6之前 我们面向对象是通过构造函数实现的 我们把对象的