一文帮你彻底搞懂JS原型和原型链!
在JavaScript
中,原型(prototype)
和原型链(prototype chain)
是非常重要的概念,但是很多人对它们的掌握都有点不太清楚,本文带大家彻底搞懂它们!
非常重要的基本概念
首先我们需要搞清楚几个非常重要的基本概念:
所有的对象都是通过new 函数
创建的,且所有的对象都是引用类型
var obj1 = {};
var obj2 = new Object();
console.log(obj1, obj2)
如上,字面量
{}
只是创建对象的一种语法糖,其实上面的两种写法是完全等价的。而Object
其实就是一个js
内置的构造函数。
所有的函数也都是对象,都是通过new Function
创建的(可以称为函数对象)
function fn1() { };
var fn2 = new Function();
console.log(fn1, fn2)
既然函数也都是对象,又称为函数对象,而且所有的对象都是通过
new 函数
创建的,那么函数又是怎么创建的呢?其实函数都是通过new Function
创建的。如上,同理可得,上面的两种写法也是完全等价的。而
Object
既然是一个函数,所以它也不例外,它其实就是相当于var Object = new Function()
。
思考:其实Function
也是一个函数对象,那它又是怎么创建的呢?
结论:Function函数是JS内置的,不需要创建!
综上所述,可以用一张图来形象的展示出它们的关系:
原型prototype
什么是原型?
所有的函数都有一个属性叫prototype
,称之为函数原型
function Fn() {
};
console.log(Fn.prototype)
console.log(Object.prototype)
console.log(Array.prototype)
可以运行如上代码看看效果,
Object
和Array
其实都是js
内置的构造函数。
默认情况下,prototype
是一个普通的object
对象
你应该可以在上面的打印中看到它是一个对象,包含了若干的属性和方法。什么意思呢?既然它是一个对象,那么其实就是通过
new Object()
创建的。
默认情况下,prototype
中有一个属性叫constructor
,它也是一个对象,它指向构造函数本身
function Fn() {
};
console.log(Fn.prototype.constructor === Fn) // true
console.log(Object.prototype.constructor === Object) // true
console.log(Array.prototype.constructor === Array) // true
通过这个特点,我们可以在原型链上查找实例化后的对象所对应的构造函数,这个后面会讲到。
隐式原型__proto__
什么是隐式原型?
所有的对象都有一个属性叫__proto__
,称之为隐式原型
function Fn() {
};
var fn1 = new Fn();
console.log(fn1.__proto__)
var obj1 = {};
console.log(obj1.__proto__)
可以运行如上代码看看效果,所有的对象在被创建的时候就会有一个叫
__proto__
的属性。
默认情况下,__proto__
指向创建该对象的函数的原型,即prototype
function Fn() {
};
var fn1 = new Fn();
console.log(fn1.__proto__ === Fn.prototype) // true
var obj1 = {};
console.log(obj1.__proto__ === Object.prototype) // true
运行如上代码,可以验证结论。在这里其实就已经初步形成了一种三角关系了。
原型链prototype chain
到重点了,上面讲了原型和隐式原型,估计有的人会问:搞这么麻烦,它们有什么用呢?
function User(name) {
this.name = name;
this.sayHello = function () {
console.log(`hello,${this.name}`);
};
};
var user1 = new User('zhangsan');
var user2 = new User('lisi');
user1.sayHello(); // hello,zhangsan
user2.sayHello(); // hello,lisi
console.log(user1.sayHello === user2.sayHello) // false
如上代码,我通过构造函数
User
实例化了两个对象user1
和user2
,因为它们都有各自属性和方法,所以user1
和user2
的sayHello
方法是不相等的。但是我们可以发现,它们的方法其实是一样的,完全没必要创建多次。可以假设一下,如果我们创建了很多个示例,那么这样就会浪费内存空间。
现在让我们来改下代码
function User(name) {
this.name = name;
};
User.prototype.sayHello = function () {
console.log(`hello,${this.name}`);
};
var user1 = new User('zhangsan');
var user2 = new User('lisi');
user1.sayHello(); // hello,zhangsan
user2.sayHello(); // hello,lisi
console.log(user1.sayHello === user2.sayHello) // true
先看结果,这时候我们会发现,代码依然能够如期运行,而且
user1
和user2
的sayHello
方法是完全相等的!那为什么
user1
和user2
中并没有sayHello
这个属性,但是却能够运行呢?其实这就是原型链
的强大之处。
那这个到底是怎么实现的呢?原型链它到底是什么呢?
function User(name) {
this.name = name;
};
User.prototype.sayHello = function () {
console.log(`hello,${this.name}`);
};
var user1 = new User('zhangsan');
console.log(user1.__proto__)
console.log(user1.__proto__.__proto__)
console.log(user1.__proto__.__proto__.__proto__) // null
运行如上代码,需要理解以下几点:
首先根据上面所说的,
user1
是个实例化的对象,那么user1.proto
一定是指向User.prototype
那
User.prototype
其实也是个对象,既然是对象就有proto
属性,那么User.prototype.proto
一定是指向Object.prototype
,也可以通过user1.proto.proto
来访问,即原型对象的原型对象同理,那
user1.proto.proto
也是个对象,也有proto
属性,不过这里就指向null
了因为每个一对象都有隐式原型,隐式原型的指向就形成了一个链条,称之为
原型链
所以上面的示例就会按照
原型链
继续依次查找
当我们访问一个对象的成员时:
首先看该对象自身是否拥有该成员,如果有直接使用;
如果没有再看该对象的隐式原型是否拥有该成员,如果有直接使用;
如果还没有就会继续查找原型对象的原型对象(该对象的隐式原型所指向的原型对象),直到找到为止;
这样形成的链条就被称为
原型链(prototype chain)
。
读到这里,再看上面的问题,为什么实例对象user1
和user2
中并没有sayHello
这个方法,但是却能够运行?答案就是可以在原型链上查找到sayHello
这个方法。
通过这种方法,我们就可以实现多个实例对象共享一个原型对象,这样就可以极大的减少对内存空间的消耗且极大的提升了代码的可复用性。这就是原型链
的作用。(其实这也是js
实现继承
的基本原理,这里就不展开说了)
核心:原型链
的全貌
下面就是js
中原型链
的全貌了
这里有两个比较特殊的点需要注意一下:
Object
的prototype
的__protp__
指向null
Function
的__protp__
指向自身的prototype
仔细对照这张图,你是否能够理解如下问题:
为什么任意一个对象都能调用
toString
和hasOwnProperty
方法了吗?为什么任意一个函数都能调用
call
和toString
方法了吗?为什么任意一个数组都能调用自身没有的
api
了吗,如filter
,push
等方法了吗?其实答案都是因为原型上有!
如果你能够看到这里并且完全理解了,那么其实你已经对原型和原型链已经有了一个比较全面的认知了。
不过关于原型和原型链还有很多其它方面的知识,比如说,js中的继承,多态以及扩展具体是怎么实现的?怎么通过js去操作原型和原型链?原型和原型链具体有什么应用场景呢?
如果大家还想笔者继续分享原型和原型链的知识,或者笔者觉得文章写的还不错的话,可以动动小手点个👍,这样我才有更新的动力!
转载自:https://juejin.cn/post/7223402434960588861