两个JavaScript对象之间有什么区别?

4

我试图提高我的 JavaScript 技能。 我不理解为什么 (5) 能够正常工作,而 (2) 返回错误。它们难道不是一样的吗?

  1. B.fn() //OK
  2. B.fn2() //TypeError: Object # has no method 'fn2'
  3. var a = new A()
  4. a.fn() //OK
  5. a.fn2() //OK

    var A = function () {
        this.fn = function () { alert(3); }
    }
    A.prototype = {
        fn2: function () { alert(4); }
    };
    
    var B =
        {
            fn: function () { alert(1); }
        }
    B.prototype = {
        fn2: function () { alert(2); }
    };
    
4个回答

7

aA 类的一个实例,而 B 则是类本身。由于 fn2 没有被定义为静态函数,因此它只能在类 B 的实例中使用,而不能在类 B 本身中使用。

如果您想直接使用 B,可以使用:

new B().fn2()

如果您将 B 定义为 function(),则也可以这样做。

或者,您可以像定义 fn 一样定义 fn2


2
实际上 new B().fn2() 不会起作用,因为没有在任何地方定义 B() 函数。 - rsp

1
(易于理解的解释) prototype 属性仅适用于将函数作为构造函数使用(使用 new 运算符)。 function 创建其 prototype 的克隆,函数内部的 this 关键字设置为克隆。克隆上的属性是对原型属性的直接引用/指针。

对象文字{}new Object()的更强大的表达式替代方法,并因此“继承”了来自 Object.prototype 的属性。

所以:

function ClassLike() {}
ClassLike.prototype = {
    foo : "bar"
}

var instance = new ClassLike();
alert( instance.foo ); // bar

这段代码能够工作是因为new操作符会启动一些操作来创建一个新的对象,而:

var instance = {
    foo : "bar"
}
instance.prototype = {
    baz : "foobar"
}

仅仅是向已经创建的对象添加另一个属性(原型),并且没有启动任何过程来实际分配/更改对象的原始原型。

现在Mozilla已经添加了一种非标准的(IE不支持)方式,通过__proto__来更改已经实例化的对象的原型,并且有一些请愿正在进行中,希望将其添加到ES5(EcmaScript 5)中。我目前不会使用它,但它的工作方式如下:

var instance = {};
var parent = {
    foo : "bar"
}
instance.__proto__ = parent;

alert( instance.foo ); // bar

改变已实例化对象的原型的另一种方法是添加到Object构造函数的原型中(出于许多原因,不建议这样做)。如下:

var instance = {}; // a more powerful alternative to `new Object()`

Object.prototype.foo = "bar";

alert( instance.foo ); // bar

虽然所有的事情都是可能的,但是否明智去做...我会说不,但意见因人而异,我宁愿避免争论 ;)

无论如何,请记住只有在你使用new一个function时,prototype属性才会起作用,否则它只成为实例上的一个属性。


谢谢您的回答。我有一个相关的问题。我需要特权函数来访问私有成员,对吗?如果是这样,那么为什么当我调用“new C().fn()”时,“this”看不到“m”成员呢?var C = function(){var m; this.fn=function(){return this.m;} } - theateist
var m 创建一个局部变量,而不是属性。 this.m 尝试引用公共属性。 this 关键字在其他语言中的工作方式不同,它只是指向函数所属对象的“注入”变量。这将起作用:var C = function(){var m=1; this.fn=function(){return m;}} - BGerrissen
__proto__ 不会被添加到 ES5:ES5 已经是一个最终的不可更改的标准。然而,Object.getPrototypeOf(o) 可以给出 o 的原型。同样,说“现在 Mozilla 添加了”也不正确;它已经存在很长时间,并且也存在于 Carakan(Opera)、JSC(Safari)和 V8(Chrome)中。 - gsnedders

0

嗯,你的答案很简单。在JavaScript中,只有构造函数才有“原型”,即原型属性。例如“{}”的对象字面量没有。因此,除非您按以下方式更改它,否则您的第2个数字永远不起作用:

var B = function(){ // the B constructor
          return this;
        }
B.prototype = {
fn2: function(){ alert(2); }
}

0
你所展示的是 JavaScript 中面向对象编程最大的问题之一:原型必须是构造函数的属性,而不是对象本身。这意味着,如果你有一个可以轻松定义为对象字面量的单个对象,你仍然需要有一个无用的构造函数来定义你的对象的原型。
阅读 Douglas Crockford 的文章:JavaScript 中的原型继承。以下是相关部分:
[...] JavaScript本身对其原型性质存在矛盾。在原型系统中,对象从对象继承。然而,JavaScript缺乏执行该操作的运算符。相反,它有一个new运算符,使得new f()产生一个从f.prototype继承的新对象。这种间接性旨在使语言对经典训练有素的程序员更加熟悉,但未能做到这一点,正如我们可以从Java程序员对JavaScript的非常低的评价中看到的那样。JavaScript的构造函数模式并没有吸引经典派。它也掩盖了JavaScript真正的原型性质。因此,很少有程序员知道如何有效地使用这种语言。幸运的是,很容易创建一个实现真正原型继承的运算符。这是我工具包中的一个标准功能,我强烈推荐您使用。

阅读本文以获取一些关于如何有效解决您正在尝试完成的任务的想法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接