关于prototype、原型链和继承的关系

我觉得搞懂prototype、原型链和继承的关系,对精通和掌握Javascript这门语言是非常重要的。

1.到底什么是原型?

在Javascript权威指南上面有这样一段定义:

一个对象的原型就是它的构造函数的prototype的值。所有的函数都有一个prototype属性,当这个函数被定义时,prototype属性自动创建和初始化。prototype属性的初始化值是一个对象,这个对象只带有一个属性。这个属性名为constructor,它指向和原型相关联的那个构造函数。

通俗的讲原型就是prototype是函数的一个属性,每个函数都有一个prototype属性,可以通过原型去改写对象下面公用的方法和属性,让公用的方法或者属性在内存中存在一份,提高性能。所以原型对象的用途是为每个实例对象存储共享的方法和属性,它仅仅是一个普通对象而已。并且所有的实例是共享同一个原型对象,因此有别于实例方法或属性,原型对象仅有一份。

2.prototype,__proto__和原型链

前面提到过,prototype是函数的一个内置属性,__proto__则是对象的内置属性(指针)。
__proto__是JS内部使用寻找原型链的属性。
特别需要注意的是:只有函数才有prototype属性,普通对象是没有prototype属性的。
但所有的对象都有__proto__属性

1
2
3
4
5
6
7
var Person = function(){};
var p = new Person();
/*new的过程拆分成以下三步:
(1) var p={}; 也就是说,初始化一个对象p
(2) p.__proto__ = Person.prototype;
(3) Person.call(p); 也就是说构造p,也可以称之为初始化p*/

alert(p.__proto__ === Person.prototype);//true


如上图,在js中,对象在调用一个方法时会首先在自身里寻找是否有该方法,若没有,则去原型链上去寻找,依次层层递进,直到Object.__proto__=null为止,这里的原型链就是实例对象的proto属性,而这样一层层往上追溯,一环扣一环,就是原型链了。

3.继承

在传统面向对象的语言中,有两个非常重要的概念 - 类和实例。 类定义了一类事物公共的行为和方法;而实例则是类的一个具体实现。 另外,面向对象编程有三个重要的概念 - 封装、继承和多态。

但是在JavaScript的世界中,所有的这一切特性似乎都不存在。 因为JavaScript本身不是面向对象的语言,而是基于对象的语言。 这里面就有一些有趣的特性,比如JavaScript中所有事物都是对象, 包括字符串、数组、日期、数字,甚至是函数。

其实当初网景公司的工程师Brendan Eich觉得没有必要把这门语言搞得和C++,Java那么复杂。不过他还是受到了当时思想的影响,Javascript一切皆对象。他引入了new关键字,后面跟构造函数来继承,但是有一个缺点就是无法共享属性和方法。

1
2
3
4
5
6
7
function Person(name){
this.name=name;
this.species='human';
};

var man = new Person('John');
var woman = new Person('Alice');

用了new关键字后,每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的内存资源浪费,1994的硬件条件,你懂的~
考虑到这一点,Brendan Eich决定为构造函数设置一个prototype属性。所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
就这样大神花了10天时间…就定下了Javascript基于原型的继承机制。