在对象类声明中设置javascript原型函数

时间:2010-05-06 22:04:28

标签: javascript oop prototype

通常,我已经看到在类定义之外声明的原型函数,如下所示:

function Container(param) {
    this.member = param;
}
Container.prototype.stamp = function (string) {
    return this.member + string;
}

var container1 = new Container('A');
alert(container1.member);
alert(container1.stamp('X'));

此代码生成两个值为“A”和“AX”的警报。

我想定义类定义的原型函数INSIDE。做这样的事情有什么不对吗?

function Container(param) {
    this.member = param;
    if (!Container.prototype.stamp) {
        Container.prototype.stamp = function() {
            return this.member + string;
        }
    }
}

我正在尝试这样,以便我可以访问类中的私有变量。但是我发现如果我的原型函数引用了私有var,私有var的值总是初始化函数初始化时使用的值,而不是对象实例中的值:

Container = function(param) {
    this.member = param;
    var privateVar = param;
    if (!Container.prototype.stamp) {
        Container.prototype.stamp = function(string) {
            return privateVar + this.member + string;
        }
    }
}
var container1 = new Container('A');
var container2 = new Container('B');
alert(container1.stamp('X'));
alert(container2.stamp('X'));

此代码生成两个值为“AAX”和“ABX”的警报。我希望输出为“AAX”和“BBX”。我很好奇为什么这不起作用,如果有其他模式我可以使用。

编辑:请注意,我完全理解,对于这个简单的示例,最好只使用this.stamp = function() {}这样的闭包,而不是使用原型。我就是这样做的。但我正在尝试使用原型来了解更多相关内容,并希望了解一些事情:

  • 什么时候使用原型函数而不是闭包?我只需要使用它们来扩展现有的对象,比如Date。我读过closures are faster
  • 如果我出于某种原因需要使用原型函数,在类中定义它是否“OK”,就像在我的例子中一样,还是应该在外面定义?
  • 我想了解为什么原型函数无法访问每个实例的privateVar值,只有第一个实例的值。

5 个答案:

答案 0 :(得分:21)

  

什么时候使用原型函数而不是闭包?

嗯,这是最轻量级的方法,假设您在某个构造函数的prototype中有一个方法,并且您创建了1000个对象实例,所有这些对象都将在您的原型链中拥有您的方法,并且所有这些都只引用一个函数对象

如果在构造函数中初始化该方法,例如(this.method = function () {};),所有1000个对象实例都将函数对象作为自己的属性。

  

如果我出于某种原因需要使用原型函数,那么在类中定义它是否“OK”,就像在我的例子中一样,或者它应该在外面定义?

在内部定义构造函数原型的成员,没有多大意义,我会向你详细解释它以及为什么你的代码不起作用。

  

我想了解为什么原型函数无法访问每个实例的privateVar值,只有第一个实例的值。

让我们看看你的代码:

var Container = function(param) {
    this.member = param;
    var privateVar = param;
    if (!Container.prototype.stamp) {  // <-- executed on the first call only
        Container.prototype.stamp = function(string) {
            return privateVar + this.member + string;
        }
    }
}

关于代码行为的关键点是在第一次方法调用时创建了Container.prototype.stamp函数

在您创建函数对象时,它会将当前封闭范围存储在名为[[Scope]]的内部属性中。

当您通过使用var或FunctionDeclaration在其中声明的标识符(变量)调用函数时,稍后会扩充此作用域。

[[Scope]]属性列表构成范围链,当您访问标识符(如privateVar变量)时,将检查这些对象。

由于你的函数是在第一个方法调用(new Container('A'))上创建的,privateVar绑定到第一个函数调用的Scope,无论你怎么做,它都将一直保持绑定打电话给方法。

看看这个answer,第一部分是关于with语句,但在第二部分我讨论范围链如何用于函数。

答案 1 :(得分:11)

很抱歉复活了一个旧问题,但我想在SO 上添加我最近在其他地方发现的东西(寻找链接,我会在找到后编辑/添加它):{ {3}}

我个人喜欢以下方法,因为我可以将所有原型和'实例'定义与函数定义一起进行可视化分组,同时避免多次评估它们。它还提供了使用原型方法进行闭包的机会,这对于创建由不同原型方法共享的“私有”变量非常有用。

var MyObject = (function () {
    // Note that this variable can be closured with the 'instance' and prototype methods below
    var outerScope = function(){};

    // This function will ultimately be the "constructor" for your object
    function MyObject() {
        var privateVariable = 1; // both of these private vars are really closures specific to each instance
        var privateFunction = function(){};
        this.PublicProtectedFunction = function(){ };
    }

    // "Static" like properties/functions, not specific to each instance but not a prototype either
    MyObject.Count = 0;

    // Prototype declarations
    MyObject.prototype.someFunction = function () { };
    MyObject.prototype.someValue = 1;

    return MyObject;
})(); 

// note we do automatic evalution of this function, which means the 'instance' and prototype definitions 
// will only be evaluated/defined once.  Now, everytime we do the following, we get a new instance
// as defined by the 'function MyObject' definition inside

var test = new MyObject();

答案 2 :(得分:1)

您需要将函数放在每个特定实例而不是原型上,如下所示:

Container = function(param) {
    this.member = param;
    var privateVar = param;

    this.stamp = function(string) {
        return privateVar + this.member + string;
    }
}

答案 3 :(得分:1)

要获得您想要的行为,您需要为每个单独的对象分配具有唯一闭包的单独stamp()函数:

Container = function(param) {
    this.member = param;
    var privateVar = param;
    this.stamp = function(string) {
        return privateVar + this.member + string;
    }
}

在原型上创建单个函数时,每个对象使用相同的函数,函数将关闭第一个Container的privateVar

通过在每次调用构造函数时分配this.stamp = ...,每个对象都将获得自己的stamp()函数。这是必要的,因为每个stamp()都需要关闭不同的privateVar变量。

答案 4 :(得分:0)

这是因为privateVar不是对象的私有成员,而是邮件关闭的一部分。您可以通过始终创建函数来获得效果:

Container = function(param) {
    this.member = param;
    var privateVar = param;
    this.stamp = function(string) {
      return privateVar + this.member + string;
    }
}

构建函数时会设置privateVar的值,因此您需要每次都创建它。

编辑:修改为不设置原型。