第一个将在您的对象的每个实例化上创建一个新的函数对象,第二个将为每个实例分配一个原型方法的引用。简而言之:第二种效率更高,因为所有实例都将共享一个函数对象。
这只是原型链的逻辑,你可以尝试并通过任何对象访问任何东西:
var objLiteral = {foo:'bar'};
在访问objLiteral.foo时,JS会首先查看对象本身已经定义的属性,如果找到则返回值。如果 JS 找不到对象本身的属性,它会检查对象的原型,因此:
objLiteral.valueOf();//method defined @Object.prototype
objLiteral.valueOf === Object.prototype.valueOf //true
但是当你使用第一种方法时:
function SomeConstructor()
{
this.methd = function()
{
return true;
}
}
var f = new SomeConstructor();
var g = new SomeConstructor();
f.methd === g.methd;//FALSE!
这表明我们正在处理 2 个独立的函数对象。将函数定义移动到原型中,f.methd === g.methd; 将为真:
function SomeConstructor()
{
}
SomeConstructor.prototype.methd = function()
{
return true;
}
var f = new SomeConstructor();
var g = new SomeConstructor();
f.methd === g.methd;//true!
回应您的评论:
在原型级别定义方法允许您更改特定任务的方法,然后 “重置” 将其恢复为默认行为。假设您在创建 AJAX 请求的函数中:
someObject.toString = function(){ return JSON.stringify(this);}
//when concatinating this object you'll get its json string
//do a lot of stuff
delete (someObject.toString);
JS 将再次检查对象是否具有定义的toString 属性其自身。所以 JS 会删除你分配给 toString 属性的函数。下次调用toString 时,JS 将重新开始扫描原型链,并使用第一次出现的方法(在原型中)。让我们澄清一下:
function SomeConstructor()
{
}
SomeConstructor.prototype.methd = function()
{
return true;
}
var f = new SomeConstructor();
var g = new SomeConstructor();
f.methd = function(){return false;};
g.methd();//returns true, still <-- method is gotten from the prototype
f.methd();//returns false <-- method is defined @ instance level
delete (f.methd);
f.methd();//returns true, f doesn't have the method, but the prototype still does, so JS uses that.
或者更好的是,您甚至可以用另一个原型中的方法替换实例的方法:
f.methd = Object.prototype.valueOf;//for as long as you need
最后一个例子没有意义,因为 f 已经有了 valueOf 方法:它的继承链看起来像这样:var f ---> SomeConstructor ---> Object,让您也可以访问所有 Object.prototype 方法!整洁,不是吗?
这些只是虚拟示例,但我希望您能看到这是使 JS 成为一种极其灵活(我必须承认有时过于灵活)和富有表现力的语言的特性之一。