JavaScript原型与原型链
此处先说明原型与原型链的作用:JavaScript
的原型与原型链就是该编程语言为了实现面对对象编程的一种设计,基于原型链,可以让JavaScript
对象拥有封装、继承和多态等众多面向对象的特性。
下面我们一步一步来认识它。
JavaScript的面向对象
使用过Vue
的小伙伴应该会很熟悉下面这段代码,通过vue-cli
创建的Vue
项目,在其如口main.js
文件中,一般会有如下代码:
我们打开Vue
的源码,可以看到Vue
这个对象的定义,与其它语言有很大的不同,Vue
这个对象就是一个function
(函数):
JavaScript
是一门弱类型语言,但它也有其它高级语言(如Java
、C#
等)所拥有的的特征——面向对象,上面通过new
与function
关键字,便是创建了一个Vue
对象。
function
用来定义一个类,new
关键字则用来创建一个类的实例对象。 实例化对象之后, 实例便继承了类的属性和方法。 例如:
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.getName = function(){
return this.name;
};
var person = new Person('james', 18);
console.log(person.name); // putput: james
console.log(person.age); // putput: 18
console.log(person.getName()); // putput: james
JavaScript创建对象的过程
在介绍原型与原型链之前,我们还需要先了解一下JavaScript
是如何创建一个对象的
当我们通过new
创建一个对象时(如:var person = new Person('james', 18)
)到底经历了什么呢?
-
首先会创建一个空对象Object
// 在栈内新建了一个obj,这个obj实际上是指的堆中对应的一个地址 let obj = new Object();
-
设置原型链---这里所说原型链,就是设置新建对象obj的隐式原型即
_proto_
属性指向构造函数Person
的显示原型prototype
对象,即:obj.__proto__ = Person.prototype;
-
改变构造函数Person的this绑定到新对象obj,并且利用call()或者是apply()来执行构造函数Person,如下:
let result = Person.call(obj);
-
将第三步中初始化完成后的对象地址,保存到新对象中,同时要判断构造函数Person的返回值类型,为什么要判断值类型呢?因为如果构造函数中返回this或者是基本数据类型(number数值,string字符串,Boolean布尔,null,undefined)的值时,这个时候则返回新的实例对象,如果构造函数返回的值是引用类型的,则返回的值是引用类型,如下:
if (typeof (result) === "object") { func = result; } else { func = obj; // 默认返回 }
整个过程使用伪代码来模拟其执行的过程:
new Person('james', 18) = {
var obj = {};
obj.__proto__ = Person.prototype;
var res = Person.call(obj, 'james', 18);
return typeof res === 'object' ? res : obj;
}
JavaScript的原型与原型链
在通过new
关键字创建对象的过程中,在其第二步:obj.__proto__ = Person.prototype
,在这一步,就涉及到了原型的概念。
-
prototype
: 每一个函数都有一个特殊的属性,叫做原型(prototype
) -
constructor
: 相比于普通对象的属性,prototype
属性本身会有一个属性constructor
,该属性的值为prototype
所在的函数 -
__proto__
: 每一个对象都有一个__proto__
属性,该属性指向对象(实例)所属构造函数(类)的原型prototype
原型与对象的关系图如下:
每个实例的的__proto__
指向对象的prototype
,同时对象prototype
的__proto__
又指向上层对象的prototype
。也就是原型链(下面蓝色的这条链)。
JavaScript
对象的prototype
内置属性, 其实就是对于其他对象的引用, 几乎所有的对象在创建时, prototype
的属性都会被赋予一个非空的值(除了Object.create(null)
)。 所有普通的prototype
链最终都会指向内置的Object.prototype
。在创建一个新对象成功之后, 如果调用一个新对象没有的属性或方法的时候, JavaScript会沿着原型链向上逐层查找对应的属性或方法。 这就类似于类继承。