JavaScript原型继承机制

一、基本概念

  1、JavaScript 中的所有事物都是对象:字符串、数值、数组、函数等等

  2、函数是一种对象,每个函数都有一个prototype属性,它是一个指向原型对象的指针

  3、每个对象都有一个__proto__属性,指向这个对象的构造函数的原型对象

二、 JavaScript 原型链

​ JavaScript没有”子类”和”父类”的概念,也没有”类”(class)和”实例”(instance)的区分,全靠一种很奇特的”原型链”(prototype chain)模式,来实现继承。

JavaScript 原型链

​ 图 1. JavaScript 原型链

​ 一会再来深究这张图,先来理解何为prototype属性和__proto__属性

三、显式原型(prototype)

​ 先来探究JavaScript的构造函数,我们先创建一个手机的构造函数。

function Phone(name){
    this.name = name;
    this.system = 'none';
}

​ 使用new来生成两个手机对象的实例

var phone1 = new Phone('Android');
var phone2 = new Phone('IOS');

alert(phone1.name); // 输出Android
alert(phone2.name); // 输出IOS
alert(phone1.system); // 输出none
alert(phone2.system); // 输出none

phone1.system = 'Android';
alert(phone2.system); // 输出none,不受phone1影响

​ 也就是说,构造函数赋予的属性只是本地的,即实例对象之间互不影响。前面说过,函数都带有prototype属性,这个属性包含一个对象(以下简称”prototype对象”),所有实例对象需要共享的属性和方法,都放在这个对象里面。改变这个对象里面的属性或者方法,其他实例对象的属性或方法会进行相应的更改。

​ 例如上述手机例子改写

function Phone(name){
    this.name = name;
}
Phone.prototype = {
    system : 'none';
}

var phone1 = new Phone('Android');
var phone2 = new Phone('IOS');

phone1.prototype.system = 'Android';
alert(phone2.system); // 输出Android

​ 简单来说,就是所有实例对象需要共享的属性和方法,都放在prototype对象里面;那些不需要共享的属性和方法,就放在构造函数里面。此外,每个prototype对象都带有一个constructor属性,该属性指向原函数。

img

四、隐式原型(__proto__)

​ 这个__proto__是一个隐藏的属性,javascript不希望开发者用到这个属性值,所以有些低版本浏览器访问不到这个属性值。前面说了,每个对象都有一个__proto__属性,指向这个对象的构造函数的原型对象。

JavaScript 原型链

​ 现在再来看这张图,实例对象f1是由构造函数Foo创建的,所以其__proto__属性指向构造函数Foo的原型对象,即f1.__proto__ === Foo.prototype。而Foo.prototype本质上也是对象,也是由Object创建的,所以Foo.prototype.__proto__ === Object.prototype。按这么说Object.prototype也是一个对象,也是由Object创建的,但是Object.prototype.__proto__ === null啊,并不是想象中的Object.prototype.__proto__ === Object.prototype。

首先要明确一点,原型链是指对象的原型链,所以原型链上的所有节点都是对象,不能是字符串、数字、布尔值等原始类型。

另外,规范要求原型链必须是有限长度的(从任一节点出发,经过有限步骤后必须到达一个终点。显然也不能有环。)

那么,应该用什么对象作为终点呢?很显然应该用一个特殊的对象。

​ 所以,此处的Object.prototype就作为了一个特殊对象,用来作为原型链的终点。那为什么属性值不能是undefined呢,而是null呢?因为返回undefined代表着原型不存在,这样,在原型链上就会存在一个非对象的值。如果选择了null,首先,没法访问null的属性,所以起到了终止原型链的作用;此外,null在某种意义上也是一种对象,即空对象。如此说来,也没有违反“原型链上只能有对象”的规定。

​ 都说函数也是一个对象,那么函数也带有__proto__属性,但是函数是由谁构建的呢?答案是Function(注意F是大写)。JavaScript里,除了上面实例用的创建函数的方式,还有另外一种创建函数的方式。

var foo = new Function ("x","y","return x+y;");

​ 这表示了函数是由Function构造的,所以任何函数的__proto__的属性都是Function的原型对象。此外,由于Function也是一个函数,也是由Function构造,所以其__proto__属性指向自己的原型对象,即Function.__proto__ === Function.prototype。

文章参考

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据