原型&&原型链
字数统计:2.2k字目录
概述
摘自JavaScript高级程序设计:
继承是OO语言中的一个最为人津津乐道的概念.许多OO语言都支持两种继承方式: 接口继承
和 实现继承
.接口继承只继承方法签名
,而实现继承则继承实际的方法
.由于js中方法没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支持实现继承,而且其 实现继承 主要是依靠原型链
来实现的.
在 JavaScript 中,是一种面向对象的程序设计语言,但是 JS 本身是没有 “类” 的概念,JS 是靠原型和原型链实现对象属性的继承。
在理解原型前,需要先知道对象的构造函数是什么,构造函数都有什么特点?
构造函数
1 | // 构造函数 Person() |
以上代码,普通函数 Person()
,加上 new
关键字后,就构造了一个对象 person
所以构造函数的定义就是普通函数加上 new
关键字,并总会返回一个对象。
函数对象
同时,JS 中的对象分为一般对象和函数对象。那什么是一般对象,什么又是函数对象呢?
JavaScript
的类型分为基本数据类型
和引用数据类型
,基本数据类型目前有 6 种(null
, undefined
, string
, number
, boolean
, Symbol
)。 其余的数据类型都统称为 object
数据类型,其中,包括 Array
, Date
, Function
等,所以函数可以称为函数对象。1
2
3
4
5let foo = function(){}
foo.name = "bar";
foo.age = 24;
console.log(foo instanceof Function) //true
console.log(foo.age) // 24
以上代码就说明了函数其实是一个对象,也可以具有属性
。
原型链
JavaScript
中的对象,有一个特殊的 [[prototype]]
属性, 其实就是对于其他对象的引用(委托)。当我们在获取一个对象的属性时,如果这个对象上没有这个属性,那么 JS 会沿着对象的 [[prototype]]
链 一层一层地去找,最后如果没找到就返回 undefined
;
这条一层一层的查找属性的方式,就叫做原型链。1
2
3var o1 = {
age: 6
}
那么,为什么一个对象要引用,或者说要委托另外一个对象来寻找属性呢?
本文开篇的第一句话,就指出来的,JavaScript 中,和一般的 OOP 语言不同,它没有 ‘类’的概念,也就没有 ‘模板’ 来创建对象,而是通过字面量或者构造函数的方式直接创建对象。那么也就不存在所谓的类的复制继承。
原型
那什么又是原型呢?
既然我们没有类,就用其他的方式实现类的行为吧,看下面这句话↓↓。
每个函数都有一个原型属性 prototype 对象
1 | function Person() {} |
通过构造函数创造的对象,对象在寻找 name 属性时,找到了 构造函数的 prototype 对象上。
这个构造函数的 prototype 对象,就是 原型
用示意图来表示:
查找对象实例属性时,会沿着原型链向上找,在现代浏览器中,标准让每个对象都有一个 __proto__
属性,指向原型对象。那么,我们可以知道对象实例和函数原型对象之间的关系。
每个原型对象都有一个 constructor 属性指向关联的构造函数
为了验证这一说话,举个例子。1
2function Person() {}
Person === Person.prototype.constructor; // true
那么对象实例是构造函数构造而来,那么对象实例是不是也应该有一个 constructor
呢?1
2
3
4function Person() {}
const person = new Person();
person.constructor === Person // true
但事实上,对象实例本身并没有 constructor
属性,对象实例的 constructor
属性来自于引用了原型对象的 constructor
属性person.constructor === Person.prototype.constructor // true
原型链顶层:Object.prototype.__proto__== null
既然 JS 通过原型链查找属性,那么链的顶层是什么呢,答案就是 Object
对象,Object
对象其实也有 __proto__
属性,比较特殊的是 Object.prototype.__proto__
指向 null
, 也就是空。Object.prototype.__proto__ === null
我们回过头来看函数对象:
所有函数对象的
proto
都指向Function.prototype
,它是一个空函数(Empty function
)
1 | Number.__proto__ === Function.prototype // true |
所有的构造器都来自于
Function.prototype
,甚至包括根构造器Object
及Function
自身。所有构造器都继承了·Function.prototype
·的属性及方法。如length
、call
、apply
、bind
以图会友,这就是网上经常看到的 JS 原型和原型链关系图:
对于以上看似很复杂的关系图,只需要理解 5 点:
每个函数都有一个原型属性 prototype
对象
普通对象的构造函数是 Object()
,所以 Person.prototype.__proto__ === Object.prototype
函数对象都来自于 Function.prototype
函数对象也是对象,所有 Function.prototype.__proto__ === Object.prototype
记住,所有函数原型的都是 Object()
的实例
Object.prototype.__proto__
是 null
第二版
原型
原型对象只存在于函数对象。也就是本质上只要是通过new Function
创建的函数对象会有一个原型对象。
而对于其他非Function的引用类型归根结底也是通过new Function创建的。
如上面提到的Array类型、Object类型。
实际上,在每个函数对象创建的时候,都会自带一个prototype的属性,这个属性相当于一个指针,指向他本身的原型对象,这个原型对象里包含着自定义的方法属性,
现假设创建了函数对象a1
2
3
4
5
6function a(){
this.name='xiaoming';
this.sayName=function () {
console.log(this.name);
}
}
则会有如下所示的结构
在默认情况下,a.prototype
下会带有一个constructor
属性,这个属性指向创建当前函数对象的构造函数,比如这里
constructor指向构造函数a本身也就是说1
a.prototypr.constructor==a //true
另外默认还有一个_proto_
属性,这个属性指向由创建这个函数对象的引用类型中继承而来的属性和方法。
当通过构造函数实例化一个对象b时,即new a()
;
则会产生如图所示结构
首先这个new出来的对象属于普通对象,所以没有prototype属性。但他有_proto_
这个属性,这个属性指向创建它的引用类型的原型对象,在这个例子中指向a.prototype
,从而继承来自引用类型a的属性和方法。
而原型的很大一部分作用是用来继承的,在上面的例子中,b就继承了a中的属性name,和方法sayName
总结一下要点就是:1
21、原型对象只存在于函数对象中;
2、prototype为函数对象的一个属性,这个属性指向原型对象;(a.prototype);
原型链
上面原型说的继承是指当前引用类型的实例继承来自引用类型的属性方法。而通过原型链我们可以进行两个类型之间的继承。假设当前有两个aa、AA:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 function aa(){
a.prototype.name='xiaoming';
a.prototype.sayName=function () {
console.log(this.name);
}
}
function AA() {
A.prototype.age=15;
A.prototype.sayAge=function(){
console.log(this.age);
}
}
//AA继承aa
AA.prototype=new aa();
var BB=new AA();
console.log(BB);
console.log(BB.sayName()) //"xiaoming"
打印出的结果为
大致流程就是通过AA.prototype=new aa();
使得AA.prototype(原型对象)下的proto指向aa.prototype,当创建引用类型AA的实例化对象BB时,BB内部会产生一个proto属性指向AA的原型对象,再通过AA原型对象中的proto指向aa的原型对象,从而实现实例对象BB对aa的继承,整个链向:BB的proto —->AA.prototype—->aa.prototype,这就是一条原型链,如果在继续延伸的话,aa的原型对象下的proto属性会指向Function本身。
整个结构图大致如图所示:
转载:
JavaScript原型与原型链
参考:
2019 面试准备 - JS 原型与原型链
JS原型链与继承别再被问倒了
三张图搞懂JavaScript的原型对象与原型链
JavaScript原型及原型链
温故js系列(15)-原型&原型链&原型继承
继承与原型链