Javascript中的原型链、prototype、__proto__的关系

2024-05-05 16:38

本文主要是介绍Javascript中的原型链、prototype、__proto__的关系,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文链接:http://shuaihua.cc/article/javascript/prototype-chain.php

Javascript中的原型链、prototype、__proto__的关系

上图是本宝宝用Illustrator制作的可视化信息图,希望能帮你理清Javascript对象与__proto__、prototype和原型链之间的关系。如果暂时看不懂也没关系,这篇文章让你从0变成1。

0、感性认识JS里的“德罗斯特效应”之原型链

如果你打开浏览器的控制面板,随便输入一个JS内置的构造器函数,比如Array,控制台输出的是一个名为Array的函数体,这好像并没有什么稀奇的,但是,当你接着输入Array.prototype,控制面板输出了一堆我们经常用到的Array构造器的方法,把目光转移到最下方,有一个叫__proto__的属性,好奇的点开,咦~?列表列出的不是Object构造器的方法么,里边有我们非常熟悉的hasOwnProperty还有toString等方法。我们常说的一切皆对象,一切对象皆null这么玄妙的编程界哲理和这种嵌套会不会有什么关系呢?更神奇的在下边。

当我继续深入,如果Array是构造器,那么控制面板输出的Array.prototype的所有属性中constructor又是什么构造器?点开看看,之后就像身处德罗斯特效应中一样,__proto__和constructor,还有Array构造器中常用的方法名不断的出现,一层套一层,一层层展开,没有尽头。。。

一、满满一堂证明题课

当你也发现了JS里这种循环往复、不断嵌套的规律后,仍然不相信自己的眼睛,于是决定亲自证明一下,哦不,是好几下。(对于初学者只靠空想太烧脑,于是本宝宝做了几个GIF动图。)

1、怎么证明你就是你?

这个问题可难不倒JS中的构造器们,一个证明秒秒钟证明“我就是我”,是颜色不一样的烟火。

拿Array举例,Array.prototype中有一个constructor属性,这个属性的值就是Array构造器自己!

Array.prototype.constructor === Array

2、“遗传进化链__proto__”,怎么证明一切皆对象?

所有的JS内置构造器都本是对象,这要long long ago说起。可是从什么线索开始向过去前进呢,原型链(我给他起了一个名字叫遗传进化链)就是突破口。所有JS构造器(当然不止构造器有)都有一个__proto__属性,这是原型链指针,指向遗传进化成它的“那个”。它“遗传”了“那个”的特性,而有进化出了它自有的特性。

JS内置构造器本是函数

从上图中发现,JS内置构造器其中之一的Array原本就是一个函数,而这个函数就是Function的prototype,所以Function.prototype有的方法,JS内置构造器都有,比如call()、apply()、bind()等(其实我们自定义的函数也是继承自Function.prototype,所以我们自己也可以定义构造器,创造属于自己的小小王国)。

而Function.prototype的进化链指针又指向了Object.prototype。

3、怎么证明到头来一切都是空?

不管你从那个属性开始,连续引用__proto__的指针,最后输出的那个值就是null。

空不异色,色不异空,空即是色,色即是空。

4、怎么证明所有JS内置构造器和自定义函数都是Function构造器的原型(prototype)。

不断强化这一认知,实践出真知。

①、String构造器的进化链指针__proto__指向Function构造器的原型

//String构造器
String.__Proto__ === Function.prototype

String构造器的进化链指针指向Function构造器的原型。

②、Number构造器的进化链指针__proto__指向Function构造器的原型

//Number构造器
Number.__Proto__ === Function.prototype

Number构造器的进化链指针指向Function构造器的原型。

③、Boolean构造器的进化链指针__proto__指向Function构造器的原型

//Boolean构造器
Boolean.__Proto__ === Function.prototype

Boolean构造器的进化链指针指向Function构造器的原型。

④、Array构造器的进化链指针__proto__指向Function构造器的原型

//Array构造器
Array.__Proto__ === Function.prototype

Array构造器的进化链指针指向Function构造器的原型。

⑤、没错,Function构造器的进化链指针__proto__也指向自己的原型

//Function构造器
Function.__Proto__ === Function.prototype

Function构造器的进化链指针指向Function构造器的原型。

⑥、Date构造器的进化链指针__proto__指向Function构造器的原型

//Date构造器
Date.__Proto__ === Function.prototype

Date构造器的进化链指针指向Function构造器的原型。

⑦;、Error构造器的进化链指针__proto__指向Function构造器的原型

//Error构造器
Error.__Proto__ === Function.prototype

Error构造器的进化链指针指向Function构造器的原型。

⑧、Object构造器的进化链指针__proto__指向Function构造器的原型

//Object构造器
Object.__Proto__ === Function.prototype

Object构造器的进化链指针指向Function构造器的原型。

⑨、RegExp构造器的进化链指针__proto__指向Function构造器的原型

//RegExp构造器
RegExp.__Proto__ === Function.prototype

RegExp构造器的进化链指针指向Function构造器的原型。

⑩、Event构造器的进化链指针__proto__指向Function构造器的原型

//Event构造器
Event.__Proto__ === Function.prototype

Event构造器的进化链指针指向Function构造器的原型。

这里需要注意所有构造器的prototype都是对象(object)类型,只有Function.prototype是函数(function)类型,这是为了保证函数构造器们的__proto__指向的都是函数。

二,咦?JSON和Math哪去啦?

JS内置的构造器函数都可以使用new关键字实例化一个对象,我们称实例化后的这个对象就是某某构造器的一个实例。就像我们每一个“人”都是“人类”这个构造器函数的一个实例

//实例化一个String构造函数
var str = new String("Hi, today! ");

实例化一个String构造函数str

既然上边10个构造器函数都能这样实例化对象,那么JSON和Math是不是也可以用new 关键字实例化呢?试试看!

//尝试实例化JSON和Math两个构造器函数
var json = new JSON();
var Math = new Math();

JSON和Math不是构造器函数,他们是普通的对象

哦No,不可以。JSON和Math不是构造器函数,他们是普通的对象。

上边提到过,只有构造器函数才能使用new 关键字实例化一个对象,而JSON和Math已经是对象了,所以我们可以不用实例化直接使用JSON和Math中的属性和方法~~(我们实例化的目就是想用实例化后的对象里的属性和方法,那么既然JSON和Math已经是对象了,就省去实例化的操作喽。当然,能实例化有能实例化的好处~)

所以JSON和Math不属于10个构造器函数,但他们12个共同属于Javascript的内置对象。

三、__proto__进化链指针设计为什么如此重要!!

javascript中为什么会有__proto__原型链的设计,不放做一个小实验先。

//实例化一个String对象
var str = new String("Hi!")

我们先实例化一个String对象并将其赋值给str这个变量,然后我们输出这个str

输出String实例化后的对象

从str输出的内容来看,str有四个属性,分别是0、1、2、length

//我们一个个输出这些属性
console.log(str[0]) //H
console.log(str[1]) //i
console.log(str[2]) //!
console.log(str['length']) //3

这毋庸置疑,但是接着往下看。

//charAt()是str对象中不存在的属性方法,但是没有报错,依然可以输出!
str.charAt(0) // 输出'H'

charAt()是str对象中不存在的属性方法,但是没有报错,依然可以输出!

调用str中不存在的属性方法

这是为什么?这就是进化链__proto__的用处。

str这个对象本身的确没有charAt()这个方法,但是str的进化链上存在这个属性方法,那么charAt()这个方法在进化链的那个节点上呢?

String.prototype拥有charAt这个方法,而str的__proto__指针指向String.prototype

哦~,原来String.prototype拥有charAt这个方法,而str的__proto__指针指向String.prototype

这么说的话,str.__proto__.__proto__指向的对象所拥有的属性str也都可以直接用喽?答案是肯定的!

str.__proto__.__proto__指向的对象所拥有的属性中有一个hasOwnProperty属性

看到str.__proto__.__proto__指向的对象所拥有的属性中有一个hasOwnProperty属性方法了么,str可以直接使用这个属性方法。

在验证之前先说下str.__proto__.__proto__指向了谁?指向的是Object的prototype属性。

str.__proto__.__proto__ === Object.prototype // true

Object.prototype.hasOwnProperty()属性方法用来检验一个对象是否自己拥有一个属性而非通过进化链__proto__继承来的属性。

好的,实验开始:

//检查str是否拥有length属性
str.hasOwnProperty('length') //true
//检查str是否拥有0属性(str.charAt(0)的输出是'H')
str.hasOwnProperty(0) //true
//检查str是否拥有1属性
str.hasOwnProperty(1) //true
//检查str是否拥有2属性
str.hasOwnProperty(2) //true
//检查str是否拥有3属性(str的length是3,所以不存在索引3这个属性)
str.hasOwnProperty(3) //false
//str是否拥有hasOwnProperty这个属性呢?答案是否定的。
str.hasOwnProperty('hasOwnProperty') //false

检验str的自有属性

四、结语

现在再来看这张图,是不是思路清晰多了呢!

Javascript中的原型链、prototype、__proto__的关系

当你弄清楚了原型链(我喜欢叫他进化链)__proto__,prototype之间的关系,还有Javascript中12个内置对象,其中10个函数类型,2个对象类型。再来学习这12个内置对象的属性和属性方法是不是如鱼得水,心里跟明镜似的~

对于Javascrip初学者,一时半会肯定还是搞不清楚,唯一的办法就是多看、多想、多练、多总结、多分享~,学习的本质无非就是这些嘛,教育的本质无非就是教会你学习你所感兴趣的嘛。

原文链接:http://shuaihua.cc/article/javascript/prototype-chain.php

这篇关于Javascript中的原型链、prototype、__proto__的关系的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/962160

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)