网页设计学习笔记

HTML,CSS,JavaScript

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

JavaScript 的原型(prototype)及其实例是不容易理解的东西,这里总结一下。

一、原型与实例

先看下面的例子:

function Foo(value) {
  this.name = value;
  this.prototype.type = "example";
}
var foo1 = new Foo("hello");
var foo2 = new Foo("world");

在这个例子中,总共出现三种不同的东西:Foo 是构造函数(constructor),通过使用 new 关键字,我们调用这个构造函数,从 Foo.prototype 这个原型对象中生成 foo1 和 foo2 这两个实例(instance)。三者的关系图示如下:

               Foo
Foo.prototype =====> foo1 和 foo2

举个实际的例子作类比:原型对象 Foo.prototype 相当于纸张,构造函数 Foo 相当于打印机,而实例 foo1 和 foo2 相当于我们打印出来的文章。

默认情形 Foo 的原型 Foo.prototype 是一个空的对象,我们可以在 Foo 构造函数中添加该原型对象的一些属性,例如上例中的我们增加了 type 属性到 Foo.prototype 中。原型中的属性是被所有实例中共享的,因此 foo1.type 和 foo2.type 都等于 "example";但是构造函数中的属性是与实例有关的,因此 foo1.name = "hello" 不同于 foo2.name = "world"。

对 Foo 的原型对象的修改也可以放在 Foo 外面,上面的例子这样写也是可以的:

function Foo(value) {
  this.name = value;
}
Foo.prototype.type = "example";
var foo1 = new Foo("hello");
var foo2 = new Foo("world");

对于构造函数,通过 prototype 属性可以访问它的原型;而对于实例 foo1 和 foo2,可以通过内部属性 __proto__ (在ECMAScript 标准中称为 [[Prototype]])来访问它们的原型。即有

foo1.__proto__ === foo2.__proto__ === Foo.prototype

这个 __proto__ 属性是隐藏的,在实际编程中最好不要使用它。因为利用构造函数的 prototype 属性就可以访问原型对象了。

最后,利用 constructor 属性可以访问原型对象或者实例对象的构造函数。即有

Foo.prototype.constructor === Foo
foo1.constructor === foo2.constructor === Foo

这样,对于这个简单的例子,三者的关系就明朗了。

二、原型与继承

在 JavaScript 中,一个构造函数的原型对象是可以自己设定的,如果我们将该原型对象指向另外一个对象,就达到了继承的目的。例如:

function Parent() {};
Parent.prototype.familyName = "Warner";

function Child(name) {
  this.givenName = name;
}
Child.prototype = new Parent();

var child1 = new Child("Tom");
var child2 = new Child("Jerry");

child1.gender = "male";
child2.gender = "female";

在这个例子中,我们指定 Child 构造函数的原型对象为 Parent 函数的原型对象的实例,从而 child1 和 child2 都继承了 Parent 函数的原型对象的 familyName 属性。即有

child1.familyName === child2.familyName === "Warner"

在使用原型链实现继承之后,当我们要查找实例的某个属性时,是沿着原型链逐级往上的。比如我们要获取 child1.familyName 的值,首先是在 child1 对象中查找,没找到;接着到 child1.__proto__ 即 Child.prototype 对象中查找,还是没找到。接着就到 child1.__proto__.__proto__ 即 Child.prototype.__proto__ 即 Parent.prototype 对象中查找,终于找到了child1.familyName = "Warner"。如果要获取 child1.givenName 的值,则需要两步。而获取 child1.gender,一步就找到了。

console.log(child1.gender, child1.givenName, child1.familyName);
// male Tom Warner

另外,对于继承的情形, child1,child2,以及 Child.prototype 的 constructor 属性指向了 Parent 函数。即有

child1.constructor === child2.constructor === Child.prototype.constructor === Parent

三、默认的原型

对于任何的函数(不管它是否作为构造函数)和对象,它们可以分别看成 Function() 和 Object() 构造函数的实例,因此它们实际上都有一个默认的原型对象,分别指向 Function.prototype 和 Object.prototype。即对于下面的例子:

fun1 = function() {};
fun2 = new Function();
obj1 = {};
obj2 = new Object();

我们有如下的结果:

fun1.__proto__ === fun2.__proto__ === Function.prototype;
obj1.__proto__ === obj2.__proto__ === Object.prototype;

有意思的是,Function() 和 Object() 自身也是函数,因此它们的原型对象,同样指向 Function.prototype。即有

Object.__proto__ === Function.__proto__ === Function.prototype

最后,Function.prototype 的 __proto__ 属性指向 Object.prototype,而 Object.prototype 的 __proto__ 为空。即有

Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null

实际上,在 JavaScript 中,所有数据类型都是对象的实例。而 Object.prototype 这个原型已经处于最顶层了。

四、例子及解释

function Foo() {};
Foo.prototype.type = "example";
var foo1 = new Foo();

function Bar() {};
Bar.prototype = {type: "example"};
var bar1 = new Bar();

上面两种方式看似一样,实际却不同:foo1 的构造函数为 Foo,而 bar1 的构造函数变成了 Object,即为

foo1.constructor === Foo.prototype.constructor === Foo
bar1.constructor === Bar.prototype.constructor === Object

这是因为 Foo.prototype.type = "example"; 没有修改 Foo.prototype 的指向。而 Bar.prototype = {type: "example"};  修改了 Bar.prototype 的指向,相当于继承了 Object ,从而使得 bar1 的构造函数变成了 Object()。即上面的 Bar 代码相当于如下:

var obj = new Object();
obj.type = "example";
function Bar() {};
Bar.prototype = obj;
var bar1 = new Bar();

注记:如果一个函数作为构造函数,而在它内部又用 return 语句返回一个对象,将会导致它所生成的实例指向这个返回的对象。因此,在构造函数中使用 return 是画蛇添足,反而添乱。

参考资料:
[1] Javascript Object Hierarchy
[2] Constructors considered mildly confusing
[3] 构造函数 - JavaScript 秘密花园
[4] new - JavaScript | MDN
[5] constructor - JavaScript | MDN
[6] __proto__ - JavaScript | MDN

posted on 2013-01-10 17:11  zoho  阅读(230)  评论(0编辑  收藏  举报