【JS】深入理解JS原型和继承
前言
在学习JS中的原型,原型链,继承这些知识之前,我们先学习下基础知识:函数和对象的关系。
我们一直都知道,函数也是对象的一种,因为通过instanceof就可以判断出来。但是函数和对象的关系并不是简单的包含和被包含的关系,这两者之间的关系还是有点复杂的。接下来我们就来捋一捋。
首先,阐述一点,对象都是通过函数创建的
对于下面这种类型的代码,一般叫做“语法糖”
var obj = {a:10,b:20};
var arr = [5, 'x', true]; 但是,其实上面这段代码的实质是下面这样的: //var obj = { a: 10, b: 20 };
//var arr = [5, 'x', true]; var obj = new Object();
obj.a = 10;
obj.b = 20; var arr = new Array();
arr[0] = 5;
arr[1] = 'x';
arr[2] = true;
而Object和Array都是函数,可以自己用typeof函数进行验证。
所以,可以得出:对象都是通过函数创建的
正文
说完了前言,接下来我们进入正题。
1. 原型prototype
在前言中,我们说了函数也是一种对象,所以函数也是属性的集合,同时,也可以对函数进行自定义属性。
每个函数都有一个属性——prototype。这个prototype的属性值是一个对象(属性的集合),默认只有一个叫做constructor的属性,指向这个函数本身。 如下图所示:
上图中,SuperType是一个函数,右侧的方框就是它的原型。
原型既然作为对象(属性的集合),除了constructor外,还可以自定义许多属性,比如下面这样的:
当然了,我们也可以在自己定义的方法的prototype中增加我们自己的属性,比如像下面这样的:
function Fn() { }
Fn.prototype.name = '张三';
Fn.prototype.getAge = function () {
return 12;
};
那么问题来了:函数的prototype到底有何用呢?
在解决这个问题之前,我们还是先来看下另一个让人迷糊的属性:_proto_
2. “隐式原型”proto
我们先看一段非常常见的代码:
function Fn() { }
Fn.prototype.name = '张三';
Fn.prototype.getAge = function () {
return 12;
};
var fn = new Fn();
console.log(fn.name);
console.log(fn.getAge ());
即,Fn是一个函数,fn对象是从Fn函数new出来的,这样fn对象就可以调用Fn.prototype中的属性。
但是,因为每个对象都有一个隐藏的属性——“_proto_”,这个属性引用了创建这个对象的函数的prototype。即:fn._proto_ === Fn.prototype
那么,这里的_proto_到底是什么呢?
其实,这个__proto__是一个隐藏的属性,javascript不希望开发者用到这个属性值,有的低版本浏览器甚至不支持这个属性值。
var obj = {};
console.log(obj.__proto__);
console.log(Object.prototype);
从上面来看,obj.__proto__和Object.prototype的属性一样!为什么呢?
原因是:obj这个对象本质上是被Object函数创建的,因此obj.proto=== Object.prototype。我们可以用一个图来表示。(图片来源于王福明博客)
即,每个对象都有一个_proto_属性,指向创建该对象的函数的prototype。
说一下自定义函数的prototype:
自定义函数的prototype本质上就是和 var obj = {} 是一样的,都是被Object创建,所以它的__proto__指向的就是Object.prototype。
但是,Object.prototype确实一个特例——它的__proto__指向的是null,切记切记!!!(图片来源于王福朋博客)
另外一个问题:函数也是一种对象,函数也有__proto__吗?
答:当然也不例外啦!
下面用一段代码和一张图来说明这个问题,看完相信就有个比较直观的理解啦!
function fn(x, y) {
return x+y;
}
console.log(fn(10,20)); //以下只是为了演示函数被Function创建的
var fn1 = new Function("x","y","return x+y;");
console.log(fn1(5,6));
用图表示就是:(图片来源于王福朋博客)
从上图可以看出:自定义函数Foo.__proto__指向Function.prototype,Object.__proto__指向Function.prototype。
但是,为什么有Function.__proto__指向Function.prototype呢?
其实原因很简单:Function也是一个函数,函数是一种对象,也有__proto__属性。既然是函数,那么它一定是被Function创建。所以Function是被自身创建的。所以它的__proto__指向了自身的Prototype
最后一个问题:Function.prototype指向的对象,它的__proto__是不是也指向Object.prototype?
答案是肯定的。因为Function.prototype指向的对象也是一个普通的被Object创建的对象,所以也遵循基本的规则。如下图:(图片来源于王福朋博客)
说了这么多,我们将上面这些图片整合到一整个图片,便于整体理解,图片如下:(图片来源于王福明博客)
3. instanceof
主要是说明下instanceof的判断规则是如何进行的。先看如下代码和图片:(图片来源于王福朋博客)
function fn() {
}
var f1 = new fn(); console.log(f1 instanceof fn);//true
console.log(f1 instanceof Object);//true
instanceof的判断规则为:
假设instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
instanceof的判断规则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。
结合这个判断规则,上面的代码和图示相信很容易看懂了。
4. 原型继承
首先说一下什么是原型链:
访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着_proto_这条链向上找,这就是原型链。
举一个例子说明下吧:
在实际应用中如何区分一个属性到底是基本的还是从原型中找到的呢?
答案就是:hasOwnProperty这个函数,特别是在for…in…循环中,一定要注意。(图片来源于王福明博客)
但是!!f1本身并没有hasOwnProperty这个方法,那是从哪里来的呢?答案很简单,是从Object.prototype中来的。看下图:(图片来源于王福明博客)
对象的原型链是沿着__proto__这条线走的,因此在查找f1.hasOwnProperty属性时,就会顺着原型链一直查找到Object.prototype。
由于所有对象的原型链都会找到Object.prototype,因此所有对象都会有Object.prototype的方法。这就是所谓的“继承”。
【JS】深入理解JS原型和继承的更多相关文章
- js经典试题之原型与继承
js经典试题之原型与继承 1:以下代码中hasOwnProperty的作用是? var obj={} …….. obj.hasOwnProperty("val") 答案:判断obj ...
- js原型链理解(2)--原型链继承
1.原型链继承 2.constructor stealing(构造借用) 3.组合继承 js中的原型链继承,运用的js原型链中的__proto__. function Super(){ this.se ...
- 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链
1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...
- 前端开发必须知道的JS(一) 原型和继承
原型和闭包是Js语言的难点,此文主要讲原型及原型实现的继承,在(二)中会讲下闭包,希望对大家有所帮助.若有疑问或不正之处,欢迎提出指正和讨论. 一. 原型与构造函数 Js所有的函数都有一个protot ...
- 08 (h5*) js第9天--原型、继承
目录: 1:原型和原型链 2:构造函数的原型可以改变 3:原型的最终指向 4:先修改原型指向,在添加方法. 5:实例对象中的属性和原型属性重合, 6:一个神奇的原型链 7:继承 8:原型链 9:利用c ...
- js笔记——理解js中的call及apply
call及apply在js里经常碰得到,但一直感觉很陌生,不能熟练使用.怎样才能熟练应用呢? 为什么存在call和apply? 在javascript OOP中,我们经常会这样定义: function ...
- [JS]如何理解JS中的类和对象
-------------------------------------------------------------------------------------------- 变量:自由的 ...
- 深入理解JS原型链与继承
我 觉得阅读精彩的文章是提升自己最快的方法,而且我发现人在不同阶段看待同样的东西都会有不同的收获,有一天你看到一本好书或者好的文章,请记得收藏起来, 隔断时间再去看看,我想应该会有很大的收获.其实今天 ...
- 【学习笔记】深入理解js原型和闭包(6)——继承
为何用“继承”为标题,而不用“原型链”? 原型链如果解释清楚了很容易理解,不会与常用的java/C#产生混淆.而“继承”确实常用面向对象语言中最基本的概念,但是java中的继承与javascript中 ...
- 一篇文章理解JS继承——原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends
说实在话,以前我只需要知道"寄生组合继承"是最好的,有个祖传代码模版用就行.最近因为一些事情,几个星期以来一直心心念念想整理出来.本文以<JavaScript高级程序设计&g ...
随机推荐
- Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例
在Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们看到了XMLConfigBuilder(xml配置解析器)的实例化.而且这个实例化过程在文章:Mybatis源码解析,一步一步从浅 ...
- Idea 设置Eclipse快捷键(常用)
使用Idea不习惯,特此将其配置成Eclipse风格的. 1.选择Eclipse风格,选择copy一份,可以自己重命名. 2.设置生成快捷键的快捷键(例如:Eclipse中的Alt+/) 3.设置ma ...
- Java 内存模型与内存结构
Java内存模型 一.简介 Java内存模型(JMM)主要是为了规定线程和内存之间的一些关系:根据JMM的设计,系统存在一个主内存(Main Memory)和工作内存(Work Memory),Jav ...
- ES 32 - Elasticsearch 数据建模的探索与实践
目录 1 什么是数据建模? 2 如何对 ES 中的数据进行建模 2.1 字段类型的建模方案 2.2 检索.聚合及排序的建模方案 2.3 额外存储的建模方案 3 ES 数据建模实例演示 3.1 动态创建 ...
- Hbase入门(四)——表结构设计-RowKey
Hbase的表结构设计与关系型数据库有很多不同,主要是Hbase有Rowkey和列族.timestamp这几个全新的概念,如何设计表结构就非常的重要. 创建 Hbase就是通过 表 Rowkey 列族 ...
- laravel基础操作手册
laravel基础操作手册 1.路由配置 测试配置路由: Route::get('/test', 'TestController@index'); 2.控制器书写 3.模型文件 4.增加扩展类文件 L ...
- bootstrap准备工作(1)
1.下载bootstrap包 http://v3.bootcss.com/getting-started/#download 2.下载结构 如果要用js里面的js效果,需要先插入juqery.js & ...
- 五 mysql之多表查询
目录 一 介绍 二 多表连接查询 1.交叉连接:不适用任何匹配条件.生成笛卡尔积 2.内连接:只连接匹配的行 3 .外链接之左连接:优先显示左表全部记录 4 .外链接之右连接:优先显示右表全部记录 5 ...
- ng 循环数据
显示数据的索引值 在ts中 public list:any[]=[ {title:"新闻1",},{title:"新闻2"},{title:"新闻3& ...
- Spring Boot 2.x基础教程:Swagger静态文档的生成
前言 通过之前的两篇关于Swagger入门以及具体使用细节的介绍之后,我们已经能够轻松地为Spring MVC的Web项目自动构建出API文档了.如果您还不熟悉这块,可以先阅读: Spring Boo ...