首页 » JavaScript » JavaScript原型对象

原型对象的感性认识

Javascript并不真正支持类概念(Javascript 2.0计划引入真正的类),它使用原型模型/对象来实现面向对象所需的继承等特性。

定义一个矩形构造函数 Rectangle() :

function Rectangle(w, h) {
    this.width = w;
    this.height = h;
}

给 Rectangle() 添加一个属性,该属性指向一个面积计算函数,用以计算矩形的面积:

function Rectangle(w, h) {
    this.width = w;
    this.height = h;
    this.area = function() { return this.width * this.height; }
}

定义Rectangle类的两个对象:

var rect1 = new Rectangle(1,2);
var rect2 = new Rectangle(3,4);

调试发现,rect1和rect2对象都包含 area() 函数,可以通过 for/in 来测试:

function to_str(obj) {
    var os = "";
    for(var pro in obj) {
        os += pro + " , ";
    }
    document.write(os + "<br/>");
}

to_str(rect1);  // width , height , area , 
to_str(rect2);  // width , height , area , 

再用 Object.hasOwnProperty() 来测试:

rect1.hasOwnProperty("area");   // true
rect2.hasOwnProperty("area");   // true

在C++中,类的对象并不拥有方法(成员函数)。为了使 Rectangle 类的对象,都共享 area() 方法,可以把 area() 从 Rectangle() 构造函数中抽取出来,放在 原型对象 中:

Rectangle.prototype.area = function() { return this.width * this.height; }

再次用 Object.hasOwnProperty() 测试:

rect1.hasOwnProperty("area");   // false
rect2.hasOwnProperty("area");   // false

这里可见,rect1和rect2对象中,已经没有 area 属性了,area 属性现在在 Rectangle 的 原型对象 中。

原型对象的定义

那么, 原型对象 究竟是什么?它大致可以这么定义:

一个对象的原型对象是它的构造函数的prototype属性的值,当构造函数被定义时,prototype属性自动创建和初始化。

prototype属性的初始值是一个名为constructor的对象,指向与原型相关联的构造函数。

为了更好的理解这个概念,试看Firefox调试时的一个截图:

Javascript原型对象

上图表明:rect1有以下几个属性:

  1. height, width属性,被rect1自身所拥有,用 hasOwnProperty() 测试返回为true
  2. area 属性,来自 原型对象 __proto__,该原型对象有两个属性:
    1. constructor 由Javascript引擎自动创建,并指向与原型关联的构造函数,即 Rectangle()
    2. area 属性,我们为 原型对象 添加的属性。

原型对象与继承

现在我们可以理解一个对象从他的原型对象中继承了一些属性,例如 rect1, rect2 从 Rectangle.prototype 中继承了area()属性。

继承属性的读、写是不对称的。读取一个对象的属性时,如果在对象自身找不到,就会递归式地向上搜索它的原型对象中的属性,如果到根节点还找不到,就会返回 undefined 。写一个对象的属性时,如果在对象自身找不到,就会创建一个属性。

先为 原型对象 添加一个color属性:

Rectangle.prototype.color = "black";

测试继承后的属性读写:

rect1.color;            // read, black
rect1.border;           // read, undefined
rect1.border = "solid"; // write, add new proto
rect1.border;           // read, solid
rect2.border;           // read, undefined

在rect1对象中添加了border属性,并不影响rect2,当rect2读取border属性时返回undefined。

读取属性的伪代码,可见是递归向上搜索的:

function getProperty(obj, pro) {
    if(obj.hasOwnProperty(pro)) return obj[pro];
    else if(obj.__proto__ != null) return getProperty(obj.__proto__, pro);
    else return undefined;
}

分享

0