这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

引子

对于初学者学习原型链,还是有很大的困难。一方面是函数与对象分不太清楚;另一方面,不懂原型链的继承等。本人曾今也深受困惑,并且把疑惑的地方都记录下来,为大家做出解释。明明CSDN搜索关于原型链的文章一大堆,为啥我还要写?个人觉得,写下这篇原型链文章,不仅表达我对原型链理解的程度,也算是总结自己了自己的学习心得;更多的,此篇文章,或多或少会为大家理解原型链,又多出了一些新的感悟。同时,在讲解原型链之前,要补充关于函数和对象的一些知识,加强大家的理解。总体围绕原型链展开,其它内容为辅。希望大家能够静下心来,认真阅读,最终能够有所收获!

对象与函数的区别

  • 对象包含函数,函数是特别的对象,也就是说对象的范围更大,而函数的范围更加小。举个简单例子,985,211等知名大学是一定是本科,而本科不一定是985,211,本科可能是普通一本,甚至是普通二本,三本等。
  • 对象特别拥有*** --proto–(因为使用Markdown语法写文章,下划线的_会转译成别的作用,所以用-代替,接下里的文章内容都将如此) *** 和 constructor 两个属性,而函数特别拥有 prototape 这个属性。这个属性因为函数属于特殊的对象,因此函数也可以有 –proto–constructor 两个属性。也就是说,对象有的,我都有,对象没有的,我还有。换句话说,你的就是我的,我的还是我的。
  • Object函数和Function函数是已经内置好了的,除此之外,还有Array,Date等等。Object这个函数,也是由 Function 构造的。并且,所有函数都是 Function的实例。换句话说,我们声明的普通函数 *function name(){} *, 更加像是由new Function实例生成的,如
var ff = new Function("a", "b", "console.log(a,b); return a+b;");
就相当于function ff(a,b){
console.log(a+b);
return a+b;
}
值得注意的是,Function最后一个""中,是函数的主体,在它之前的都是参数。
并且,由Function实例生成的,都是函数。这是与其它普通函数实例出来的
最大不同,普通函数new生成的,是一个对象,而不是函数。

通过 函数名.–proto– 可以得到 Function.prototape。通过 函数名.constructor这个属性,可以得到它的构造函数是Function。比较巧的是,Function的constructor就是它自身,Function的–proto–也是它的 Function.prototape。但是规定,一切函数的prototape的–proto– 指向 Object.prototape,即函数名.prototape.–proto–等于Object.prototape

对象的创建

  1. 通过new +构造函数,比如var obj=new Object()
  2. 通过字面量的形式,比如var obj={},这个是JOSN对象的简写。JOSN是由Object构造的,也就是说 JOSN不是函数,也就不拥有prototape这个属性,JOSN.–proto–为Object.prototape,而不是Function.prototape。JOSN.constructor为Object。Math对象和JOSN一样,也是由Object实例的。那么,Math.constructor也为Object,Math.–proto–也为Object.prototape值得注意的是,JOSN和Math已经是实例对象了,不可以再new一个实例对象。也就是不能new JOSN()和new Math()等 ,这些都是错误的操作,这是很多新学者会出错的地方。

函数的创建

  1. 通过函数声明式,如function name(){}
  2. 通过函数表达式,如let name=function(){}
  3. 通过Function实例,如let name=new Function().凡事由Function实例的,都是函数

函数的静态属性和方法,以及函数的实例属性和方法

  • 静态属性和方法

    通过函数名.属性或者方法就是静态属性或者方法。

    上代码

function Son(){} ;
Son.age=18;
Son.sex="男";
Son.play=function(){
console.log("我会玩游戏");
}
Son.sing=function(){
console.log("我会唱歌");
}
要想得到age,sex属性以及play(),sing()方法,只能通过函数名.属性或者方法使用

console.log(Son.age,Son.sex)//18,男
Son.play()//我会玩游戏
Son.sing()//我会唱歌
不能通过new一个对象得到
比如
var son= new Son();
console.log(Son.age,Son.sex)//undefined,undefined
son.sing()//son.sing is not a function
  • 实例属性和方法
  • 通过在函数内部,用this.属性或者this.方法的就是实例属性或方法
  • 上代码
function Son(name,age,phone){
this.name1=name;
this.age=age;
this.phone=phone;
this.dance=function(){
console.log("我会跳舞")
}
this.getAge=function(){
return this.age;
}
}
要想得到age,sex,phone属性以及dance(),getAge()方法,只能new一个实例对象,
然后通过实例对象.属性或者方法使用

var son1=new Children("小强","18","苹果");
console.log(son1.name1);//小强
console.log(son1.age);//18
console.log(son1.phone);//苹果
console.log(son1.getAge);//18
son1.dance();//我会跳舞
不能通过函数名.属性或者方法使用

console.log(Children.name1);//undefined
Children.dance();//Uncaught TypeError: Parent.say is not a function

–proto–属性的例子

–proto–属性,指向构造它的函数的prototape属性。比如

function Son() {

    }
var son=new Son();
console.log( son.__proto__==Son.prototape)//true
console.log( Son.__proto__==Function.prototape)//true
var obj=new Object()或者 var obj=new Object({})或者var obj={}
console.log( obj.__proto__==Object.prototape)//true
console.log(Object.__proto__==Function.prototape)//true
console.log(Array.__proto__==Function.prototape)//true
console.log(JOSN.__proto__==Object.prototape)//true
console.log(Math.__proto__==Object.prototape)//true
console.log(JOSN.__proto__==Function.prototape)//false
console.log(Math.__proto__==Function.prototape)//false
console.log(Function.__proto__==Function.prototape)//true 特殊
上述表明,函数的__proto__属性指向的,都是Function.prototape,而实例对象指向的
是构造函数的prototape属性。

constructor属性的例子

constructor属性,指向构造它的函数

function Son() {

    }
var son=new Son();
console.log(son.constructor==Son)//true
console.log(Son.constructor==Function)//true
console.log(Object.constructor==Function)//true
console.log(Date.constructor==Function)//true
console.log(Math.constructor==Object)//true
console.log(JOSN.constructor==Object)//true
console.log(Function.constructor==Function)//true
上述表明,函数的constructor属性指向的,都是Function,而实例对象指向它的构造函数

prototape属性的例子

prototape属性 函数特别拥有,对象没有这个属性

var obj={}
console.log(obj.prototape)//undefined
function Son() { }
var son=new Son();
console.log(obj.prototape)//undefined
console.log(son.prototape)//undefined
console.log(Date.prototape)//undefined
console.log(JOSN.prototape)//undefined
console.log(Object.prototape) //{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ,...}
console.log(Son.prototape) //{constructor: ƒ}
console.log(Function.prototape) //ƒ () { [native code] }
由此可见,对象没有这个属性,并且,prototape的值为对象
我们可以通过prototape.constructor,可以知道这个原型对象的构造函数是谁
console.log(Object.prototape.constructor)//ƒ Object() { [native code] }
console.log(Son.prototape.constructor)//ƒ Son() { }
console.log(Son.prototape.constructor)//ƒ Function() { [native code] }
我们可以向prototape中添加方法或者属性,但一般添加方法,代表这个链上的公共方法,个人认为,
这个方法如果很多对象都用到了,不妨放在Object.prototape中。
Object.prototape.sing=function(){
console.log("我会唱歌");
}
Object.prototape.sing()//我会唱歌
首先new 一个实例对象
var son=new Son();
然后通过实例对象.方法名就可以使用
son.sing();//我会唱歌

原型链

  • 没有改变prototape的原型链

    • 如图

  • 箭头指向它们的方向,上述关系(–proto–,constructor,prototape)都在例子中讲解了,就不再赘述,这里关注的是原型链。(备注:本图的JOSN和Math对象不好画在图的左边,因此又画了一个Object.prototape原型对象在右边。)我们看Son的实例对象,为了方便,我写一个var son=new Son()。通过son.–proto–,可以得到Son.prorotape。如果继续通过链式,即son.–proto–.–proto–可以得到Object.prototape。我们已经知道了,son.–proto–就是为Son.prorotape,那么我们通过Son.prorotape.–proto–也可以得到Object.prototape。也就是说,–proto–能够让我们顺藤摸瓜,一直向上查找,这就是原型链(图中已经用红色大边框包裹起来了)。当我们son.–proto–.–proto–.–proto–(相当于Object.prototape–proto–,也相当于Son.prorotape.–proto–.–proto–)时,得到null,这说明,原型链是有终点的。

  • 为什么要原型链?

    当我们需要一些方法时,别的函数已经有了这些方法,那么我们不需要自己再次造轮子,可以通过原型链,查找各个父级的prototape属性的值,得到想要的方法。比如:我们想要让Object的实例对象可以使用sing()方法,同时也想让Son的实例对象使用一个sing()方法,那么我们就可以通过在Object的原型对象上,写上这个sing()共有方法,不需要让Son的原型对象上,再写下这个sing()方法。

Object.prototape.sing=function(){
console.log("我会唱歌");
}
首先new 一个实例对象
var son=new Son();
然后通过实例对象.方法名就可以使用
son.sing();//我会唱歌
  • 所以,通过原型链,可以让我们少些一些相同的代码。我们只需要把相同的方法放在父级上,就不必自己重写一个;除此之外,如果我们这个实例对象的构造函数如果没有这个方法,那么这个实例对象会依次向原型链查找,直到查找到Object.prototape为止,如果还没有找到,那就终止,并且报错。如果这个构造函数有这个方法,那么按照构造函数的来,而不是父级的prototape里面的方法。这说明,存在一定的优先级,我们需要注意。
  • 改变了prototape的值原型链
    • 如图

我们已经知道了prototape的值是一个对象,那么我让prototape的值等于一个实例对象,会怎么样?

如上图所示,即Son.prototape=new Parent()。我们试着打印一下Son.prototape的值,console.log(Son.prototape)//Parent {},显示是parent的实例对象,再试着打印console.log(Son.prototape.constructor)//function Parent(){},发现竟然是Parent,你会心想,这就不合理了。我应该指向Son才对啊。这里我做出解释,因为我们是将Son.prototape重新赋值了,将原本的对象覆盖了,即地址发生了变化。现在的地址和之前的地址不是同一个地址(对象的重新赋值,会改变地址),那么大家是不是瞬间清楚了。同时,这也说明了,Son.prototape.constructor继承Parent.prototape.constructor,我相信这下子大家都清楚了,说明constructor也可以继承。为了解决这种问题,我们必须让Son.prototape.constructor重新指向Son,所以Son.prototape.constructor=Son,这样子,console.log(Son.prototape.constructor)//function Son(){}就解决问题了。之后通过–proto–与没有改变prototape的原型链是一样的,只不过Son.prototape.–proto–为Parent.prototape。那么,我们new 一个Son的实例对象,有
var son=new Son();
console.log(son.--proto--)//Son.prototape
console.log(son.--proto--.--proto--)//Parent.prototape
console.log(son.--proto--.--proto--.--proto--)//Object.prototape
console.log(son.--proto--.--proto--.--proto--.--proto--)//null
console.log(Son.prototape.--proto--)//Parent.prototape
console.log(Son.prototape.--proto--.--proto--)//Object.prototape
console.log(Son.prototape.--proto--.--proto--.--proto--)//null
console.log(Patent.prototape.--proto--)//Object.prototape
console.log(Patent.prototape.--proto--.--proto--)//null
console.log(Object.prototape.--proto--)//null
并不是说,只有一个函数的prototape的值可以更改,我们还可以更改很多函数的prototape值。比如Parent的prototape的值,让Parent.prototape=new Grandparent又加了一个父级,多出来一条链。有兴趣的,可以去试试。

本文转载于:

https://juejin.cn/post/7101887288652595230

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--JS原型链的更多相关文章

  1. 深入理解JS原型链与继承

    我 觉得阅读精彩的文章是提升自己最快的方法,而且我发现人在不同阶段看待同样的东西都会有不同的收获,有一天你看到一本好书或者好的文章,请记得收藏起来, 隔断时间再去看看,我想应该会有很大的收获.其实今天 ...

  2. js 原型链和继承(转)

    在理解继承之前,需要知道 js 的三个东西: 什么是 JS 原型链 this 的值到底是什么 JS 的 new 到底是干什么的 1. 什么是 JS 原型链? 我们知道 JS 有对象,比如 var ob ...

  3. 简单粗暴地理解js原型链–js面向对象编程

    简单粗暴地理解js原型链–js面向对象编程 作者:茄果 链接:http://www.cnblogs.com/qieguo/archive/2016/05/03/5451626.html 原型链理解起来 ...

  4. JS原型链

    JS作为发展了多年了对象语言,支持继承,和完全面向对象语言不同的是,JS依赖原型链来实现对象的继承. 首先JS的对象分两大类,函数对象和普通对象,每个对象均内置__proto__属性,在不人为赋值__ ...

  5. 深入分析JS原型链以及为什么不能在原型链上使用对象

    在刚刚接触JS原型链的时候都会接触到一个熟悉的名词:prototype:如果你曾经深入过prototype,你会接触到另一个名词:__proto__(注意:两边各有两条下划线,不是一条).以下将会围绕 ...

  6. js原型链与继承(初体验)

    js原型链与继承是js中的重点,所以我们通过以下三个例子来进行详细的讲解. 首先定义一个对象obj,该对象的原型为obj._proto_,我们可以用ES5中的getPrototypeOf这一方法来查询 ...

  7. JS 原型链图形详解

    JS原型链 这篇文章是「深入ECMA-262-3」系列的一个概览和摘要.每个部分都包含了对应章节的链接,所以你可以阅读它们以便对其有更深的理解. 对象 ECMAScript做为一个高度抽象的面向对象语 ...

  8. 一张图看懂 JS 原型链

    JS 原型链,画了张图,终于理清楚各种关系有木有 写在最后: __proto__是每个对象都有的一个属性,而prototype是函数才会有的属性!!! function Person() { } 是函 ...

  9. JS原型链与继承别再被问倒了

    原文:详解JS原型链与继承 摘自JavaScript高级程序设计: 继承是OO语言中的一个最为人津津乐道的概念.许多OO语言都支持两种继承方式: 接口继承 和 实现继承 .接口继承只继承方法签名,而实 ...

  10. 02 js原型链

    1 js原型链是一个绕不开的话题.直接上说吧. /** * 1. js里的原型链是怎么样的? 带class 和不带class的原型链的不同. */ const util = require('util ...

随机推荐

  1. NC227595 跳跳跳

    题目链接 题目 题目描述 dd在玩跳格子游戏,具体游戏规则如下, \(n\) 个格子呈环形分布,顺时针方向分别标号为 \(1\sim n\) ,其中 \(1\) 和 \(n\) 相邻,每个格子上都有一 ...

  2. MindSponge分子动力学模拟——定义Collective Variables

    技术背景 在前面的几篇博客中,我们介绍了MindSponge分子动力学模拟框架的基本安装和使用和MindSponge执行分子动力学模拟任务的方法.这里我们介绍一个在增强采样领域非常常用的工具:Coll ...

  3. NVME(学习笔记四)—概念解读

    1. 综述 NVMe over PCIe协议,定义了NVMe协议的使用范围.指令集.寄存器配置规范等. 名词解释 1.1.1 Namespace Namespace是一定数量逻辑块(LB)的集合,属性 ...

  4. 如何从零实现属于自己的 API 网关?

    序言 上一篇文章:你连对外接口签名都不会知道?有时间还是要学习学习. 有很多小伙伴反应,对外的 API 中相关的加签,验签这些工作可以统一使用网关去处理. 说到网关,大家肯定比较熟悉.市面上使用比较广 ...

  5. 多线程系列(七) -ThreadLocal 用法及内存泄露分析

    一.简介 在 Java web 项目中,想必很多的同学对ThreadLocal这个类并不陌生,它最常用的应用场景就是用来做对象的跨层传递,避免多次传递,打破层次之间的约束. 比如下面这个HttpSer ...

  6. Mysql 插入timestamp没有使用默认值问题

    在一次升级过程中,发现Mysql插入数据报了个错 Column 'create_time' cannot be null. 但是看了下这个字段虽然是非null,但是是有默认值的 `create_tim ...

  7. mysql进阶语句优化---day40

    # ###part1: sql语句优化 #(1) mysql 执行流程 客户端: 发送连接请求,然后发送增删改查sql语句进行执行 服务端: 1.连接层:提供和客户端连接的服务,在tcp协议下 提供多 ...

  8. oracle不等于1怎么查?

    空值null比较特殊,它不能通过=或者<>进行查询,只能用is null或者is not null进行查询,例如你的数据中有null值,那么用 字段名=1,字段名<>1,字段名 ...

  9. 【Azure Key Vault】.NET 代码如何访问中国区的Key Vault中的机密信息(Get/Set Secret)

    问题描述 使用 .NET Azure.Identity 中的 DefaultAzureCredential 认证并连接到Azure Key Vault中, 在Key Vault 的示例中,并没有介绍如 ...

  10. 一次生产环境OOM排查

    一.背景 前几天下午飞书告警群里报起了java.lang.OutOfMemoryError: unable to create new native thread告警,看见后艾特了对应的项目负责人但是 ...