个人对JS原型链的一些理解(prototype、__proto__)
前言
在我一开始学习java web的时候,对JS就一直抱着一种只是简单用用的心态,于是并没有一步一步地去学习,当时认为用法与java类似,但是在实际web项目中使用时却比较麻烦,便直接粗略了解后开始使用jQuery。但现如今,前端发展迅速,js语法方便也有了相当大的改善,并且伴随着node.js的登场,js的适用性也更加广泛。其实也是自己了解到了electron的存在,再加上web开发中前端与后端开发也比较密切,于是这便又掉头回来重新开始学习js。在学习的过程中,仔细学习了一下js的原型链,也在这里做个记录,如果有不对的地方,还请各位指出,本人感激不尽!
正文
在js的世界中,一切皆对象,那么我们先将对象分为三类:实例对象、原型对象、函数对象。
实例对象简单说就是通过构造函数所创建的对象。
函数对象好理解,js的函数本身也是个对象,这个对象有这方法名、参数、方法体等属性。构造函数是一种特殊的函数,了解过其他OOP语言都知道,构造函数往往会在实例对象创建的时候调用,主要是用来完成实例对象的初始化操作。但是在js中,构造函数与普通函数并不太大区别,我们也可以像使用普通函数一样使用构造函数,即不使用new关键字。所以从本质上讲,普通函数也是构造函数,而构造函数只是从功能上区分的一个称呼,体现在代码里就是用不用new关键字。但为了接下来的说明,下面将都会使用构造函数对象。
原型对象比较特殊, 现在先暂时记住通过实例对象与函数对象都能找到对应的原型对象。
这三类对象之间其实都有着联系,而通过这些联系就形成了js的完整的原型链。我们接下来就按照这三类对象之间的关系来逐渐了解原型链。
实例对象与构造函数对象
首先来看实例对象与构造函数对象的联系。通过new关键字,我们可以通过构造函数得到一个实例对象。例如:
function Student(name){
this.name = name;
}
var stu = new Student('wang');
在上面的片段中,Student是一个构造函数,stu则是一个通过Student创建的实例对象。二者的联系很明显,而在js里则体现在实例对象stu的constructor属性中:
stu.constructor === Student; // true
那么反过来,我们虽然不能通过构造函数对象直接找到它所有的实例对象,但是可以通过instanceof关键字来判断一个对象是不是这个构造函数的实例对象:
stu instanceof Student; // true
原型对象与其他两类对象
上面我们也说了,构造函数与普通函数没有什么区别,那么直接使用构造函数,那this自然是指内置全局对象window。但如果用new,this就指的是新的实例对象,而且这个方法还会返回这个实例对象。到这里大致就能猜到加了关键字new做了什么操作了,它创建了一个新的空对象,并且把构造函数中的this替换为空对象,最后把这个对象返回。
那么为实例方法增加一个普通函数也这样做,从结果来说是没有问题的:
function Student(name){
this.name = name;
this.say = function(){
console.log`I'm ${name}`;
};
}
stu1 = new Student('wang');
stu2 = new Student('li');
stu1.say(); // I'm wang
stu2.say(); // I'm li
stu1.say === stu2.say; // false
但是我们会发现,stu1与stu2的say函数对象竟然不是一个,那就说明如果创建了1000个Student,就会有1000个say函数对象出现,而这1000个say实现的功能完全一致,这对内存而言显然是极大的浪费。
如何解决这个问题呢?既然多个函数完全一致,那么自然可以把这个函数对象放在一个地方,当访问stu1和stu2的say函数时,统一去拿这个地方的函数对象即可。如果我们自己实现这个功能,当在实例对象中使用函数对象时,我们又得自己去手动去公共的地方寻找函数对象,这么做显然太费劲了。
好在这些js都已经帮我们做了,每个构造函数对象都拥有一个prototype属性,这个属性指向的是一个对象,这个对象我们就叫它原型对象。而这个原型对象又拥有一个与实例对象一样的constructor属性,同样也是指向构造函数对象。
另外,对于每个对象,又都有一个__proto__的属性指向它的原型对象。当我们访问一个对象的某个属性时,实际上是先在当前对象寻找这个属性,如果没有找到,则会继续到__proto__所指的对象(原型对象)中寻找。
function Student(name){
this.name = name;
}
Student.prototype.say = function(){
console.log`I'm ${name}`;
};
new Student('liu').say === new Student('zhang').say; // true
为方便理解,这里再放一张图,对照着这张图下面的代码就容易看明白了,之后如果遇到不明白的也可以回过头来看图,直观明了。

var chen = new Student('chen')
chen.__proto__ === Student.prototype; // true
chen.constructor === Student; // true
chen.constructor === Student.prototype.constructor; // true
深入
明白了上面这些概念,我们把视角放大,不再局限于Student。前面我们说到所有对象都有一个__proto__的属性,那么对于函数对象和原型对象自然也不例外,我们接下来的关注点就是这两类对象的__proto__属性。
首先来看函数对象。在前面的代码中,Student函数对象的__proto__是谁呢?答案是Function的原型对象。
Student.__proto__ === Function.prototype; // true
一切皆对象,那么Function的__proto__又是谁?还是Function的原型对象:
Function.__proto__ === Function.prototype; // true
为什么?因为Function也是个函数对象。通常我们创建函数的方式为
function xxx(x){...}
var yyy = function(y){...};
那么其实还有一种写法:
var zzz = new Function('z','...');
// 例如:
var hello = new Function('msg','console.log(msg)');
hello('hi'); // hi
这样的写法显然能直接看出来,Function是个函数对象。于是便有一些有趣的事情了:
Function.__proto__ === Function.prototype; // true
Function.constructor === Function; // true
Function.constructor === Function.prototype.constructor; // true
Function是自己的函数对象,也是自己的实例对象:
var Function = new Function(...);
至于为什么会这样,这就比较像先有鸡还是先有蛋的问题了。我们只需要知道所有函数对象(包括Function)的__proto__都指向Function的原型对象。
与Function类似,Object也是一个函数对象。(举一反三,Array,String,Number等都是)
我们可以这样创建一个空的Object:
var obj = new Object();
那么Object的原型对象的__proto__是谁呢?是null。
Object.prototype.__proto__; // null
之前说过,当我们用.操作符去拿一个属性时,js会先在当前对象里寻找,没有的话去__proto__的对象(原型对象)里寻找。那么如果__proto__(原型对象)里还没有,就继续去它的__proto__里寻找,以此重复。那么什么时候是个头呢?直到__proto__为null时。
我们知道所有对象都有toString方法,Student的实例对象stu也是个对象,但我们明显没有给它添加toString方法,为什么它会有呢?因为stu的__proto__最终指向的是Object的原型对象。这也就是js继承的本质了。
stu.__proto__; // {constructor: ƒ}
stu.__proto__.__proto__; // {constructor: ƒ, …, toString: ƒ, …}
stu.__proto__.__proto__ === Object.prototype; // true
stu.toString === Object.prototype.toString; // true
所以,遍历所有对象的__proto__最终都会来到Object的原型对象。
个人对JS原型链的一些理解(prototype、__proto__)的更多相关文章
- js原型链的深度理解!
一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object .Function 是 JS 自带的函数对象.下面举例说明 var o1 = ...
- 深入理解JS原型链与继承
我 觉得阅读精彩的文章是提升自己最快的方法,而且我发现人在不同阶段看待同样的东西都会有不同的收获,有一天你看到一本好书或者好的文章,请记得收藏起来, 隔断时间再去看看,我想应该会有很大的收获.其实今天 ...
- 简单粗暴地理解js原型链–js面向对象编程
简单粗暴地理解js原型链–js面向对象编程 作者:茄果 链接:http://www.cnblogs.com/qieguo/archive/2016/05/03/5451626.html 原型链理解起来 ...
- js原型链理解(2)--原型链继承
1.原型链继承 2.constructor stealing(构造借用) 3.组合继承 js中的原型链继承,运用的js原型链中的__proto__. function Super(){ this.se ...
- 深入分析JS原型链以及为什么不能在原型链上使用对象
在刚刚接触JS原型链的时候都会接触到一个熟悉的名词:prototype:如果你曾经深入过prototype,你会接触到另一个名词:__proto__(注意:两边各有两条下划线,不是一条).以下将会围绕 ...
- JS 原型链图形详解
JS原型链 这篇文章是「深入ECMA-262-3」系列的一个概览和摘要.每个部分都包含了对应章节的链接,所以你可以阅读它们以便对其有更深的理解. 对象 ECMAScript做为一个高度抽象的面向对象语 ...
- js 原型链的介绍
对象的原型链:一个对象所拥有的属性不仅仅是它本身拥有的属性,他还会从其他对象中继承一些属性.当js在一个对象中找不到需要的属性时,它会到这个对象的父对象上去找,以此类催,这就构成了对象的原型链. 下面 ...
- js 原型链和继承(转)
在理解继承之前,需要知道 js 的三个东西: 什么是 JS 原型链 this 的值到底是什么 JS 的 new 到底是干什么的 1. 什么是 JS 原型链? 我们知道 JS 有对象,比如 var ob ...
- 前端知识总结--js原型链
js的原型链听着比较深奥,看着容易晕,梳理一下还是比较容易懂的 (先简单写下,后续有时间再整理) 简而言之 原型链:就是js的对象与对象之间,通过原型组成建立的层层关系,构成了整个链条,称之为原型链 ...
随机推荐
- 模拟实现 DBUtils 工具 , 技术原理浅析
申明:本文采用自己 C3P0 连接池工具进行测试 自定义的 JDBCUtils 可以获取 Connection: package com.test.utils; import java.sql.Con ...
- Linux基础知识第九讲,linux中的解压缩,以及软件安装命令
目录 Linux基础知识第九讲,linux中的解压缩,以及软件安装命令 一丶Linux Mac Windows下的压缩格式简介 2.压缩以及解压缩 3.linux中的软件安装以及卸载 1.apt进行安 ...
- springboot情操陶冶-jmx解析
承接前文springboot情操陶冶-@Configuration注解解析,近期笔者接触的项目中有使用到了jmx的协议框架,遂在前文的基础上讲解下springboot中是如何整合jmx的 知识储备 J ...
- Java提高班(六)反射和动态代理(JDK Proxy和Cglib)
反射和动态代理放有一定的相关性,但单纯的说动态代理是由反射机制实现的,其实是不够全面不准确的,动态代理是一种功能行为,而它的实现方法有很多.要怎么理解以上这句话,请看下文. 一.反射 反射机制是 Ja ...
- Spring Boot 2.x (二):How Hello World & 热部署
本篇摘要 上一篇文章,我们构建了一个HelloWorld的程序,现在,我们来看一下这个程序,下面是我们这一节要分析的点. spring-boot-starter-boot @SpringBootApp ...
- Java并发专题(二)线程安全
前言 随着时代的发展,CPU核数的增加和计算速度的提升,串行化的任务执行显然是对资源的极大浪费,掌握多线程是每个程序员必须掌握的技巧.但是同时多线程也是一把双刃剑,带来了共享资源安全的隐患.在本节会介 ...
- Django 系列博客(十四)
Django 系列博客(十四) 前言 本篇博客介绍在 html 中使用 ajax 与后台进行数据交互. 什么是 ajax ajax(Asynchronous Javascript And XML)翻译 ...
- LeetCode数组解题模板
一.模板以及题目分类 1.头尾指针向中间逼近 ; ; while (pos1<pos2) { //判断条件 //pos更改条件 if (nums[pos1]<nums[pos2]) pos ...
- [C# 设计模式] Adapter - 适配器模式(两种)
Adapter - 适配器模式 序 现实生活中,我们常用到适配器. 你当前打开我这篇文章的笔记本电脑,电源的另一边不正连着一块适配器吗? 你平时想将三口插座插进二口插座里面,不也需要一个适配器吗? 整 ...
- 【小o地图Excel插件版】计算两点间驾车路径,获取途径道路、驾车距离、耗时等信息
小o地图Excel插件版:一款基于Excel软件开发的地图软件,提供基于Excel表格进行地理数据挖掘.地理数据分析.地图绘制.地图图表等功能的工具类软件.具有易用.高效.稳定的特点,能够满足地理数据 ...