如何使用javascript原型在实例之间共享属性

时间:2013-06-04 06:26:54

标签: javascript inheritance instance-variables prototype-chain

我对原型继承的理解是每个对象都有一个原型属性。如果某个对象上不存在某个属性,那么它的原型对象就会被检查,依此类推,直到链上。

在这个例子中,我的原型对象是一个带有计数器属性的简单对象。

我希望每个实例共享相同的原型,但它们似乎会获得新的实例。这段代码的输出是00,我期待01。

我错过了一些明显的东西吗?

"use strict";

var ConstructorFunction = function () {};

ConstructorFunction.prototype = {
    counter: 0,
    count: function () {
        return this.counter++;
    }
};

var a = new ConstructorFunction();
var b = new ConstructorFunction();

$("#output").append(a.count());
$("#output").append(b.count());

这是jsfiddle:http://jsfiddle.net/hdA6G/5/

5 个答案:

答案 0 :(得分:4)

原型属性确实是所有实例的共享。问题是你永远不会改变原型属性。看看你的fiddle还有一些额外的日志:

"use strict";

var ConstructorFunction = function () {};

ConstructorFunction.prototype = {
    counter: 0,
    count: function () {
        return ++this.counter;
    }
};

var a = new ConstructorFunction();
var b = new ConstructorFunction();

$("#output").append(a.hasOwnProperty("counter") + " "); //false
a.count();
$("#output").append(a.hasOwnProperty("counter") + " "); //true

如您所见,只要您调用++this.counter,就会创建一个本地属性,该属性将从中使用。

假设发生了这种情况:

++this.counter被解释为this.counter = this.counter + 1。首先,评估等号的右半部分,由于您的实例没有counter属性,因此使用原型的计数器属性。此属性的值将被添加到1,然后分配给this.counter,现在创建一个本地属性,就像它分配一个根本不存在的属性一样,如a.xy = 1。在这种情况下,xy将是实例的本地属性。

修改

有两种解决方法仍然允许您使用prototype属性:

1)在count方法中明确设置prototype属性:

ConstructorFunction.prototype.count = function() {
    return ++this.constructor.prototype.counter;
};

2)使用apply调用count方法,并将原型用作上下文:

a.count.apply(a.constructor.prototype);

但是,如果按照您的方式设置prototype属性,则会遇到这两种方法的问题。

 ConstructorFunction.prototype = {
     //...
 };

这会覆盖完整的原型对象,因此也会覆盖它的构造函数属性。构造函数属性现在将指向原型链中的下一个更高对象Object对象。要解决这个问题,您可以在分配原型对象后手动设置构造函数:

ConstructorFunction.prototype.constructor = ConstructorFunction;

或单独分配原型对象的每个属性:

ConstructorFunction.prototype.counter = 0;
ConstructorFunction.prototype.count = function () {
    return ++this.counter;
};

答案 1 :(得分:3)

考虑使用闭包,这样您就可以将该变量保持为私有并跨实例共享:

var Class = (function ClassModule() {

  var counter = 0;

  function Class() {}

  Class.prototype = {

    count: function() {
      return counter++;
    }
  };

  return Class;
}());

var class1 = new Class();
var class2 = new Class();

console.log(class1.count()); //=> 0
console.log(class2.count()); //=> 1

counter变量与闭包(模块模式)保持在上下文中,并且不会与每个新实例重复。

答案 2 :(得分:1)

在基于原型的OOD中,共享意味着共享相同的定义并将其用于不同的上下文。如果你需要在上下文之间共享静态属性,你可以按照下面的说明进行操作

var ConstructorFunction = function (context) {
    this.count = function () {
        return context + "," + (++this.counter) + "," + (++ConstructorFunction.staticCounter);
    };
};

ConstructorFunction.prototype.counter = 0;

ConstructorFunction.staticCounter = 0;

var context1 = new ConstructorFunction("context1");
var context2 = new ConstructorFunction("context2");

$("#output").append(context1.count());
$("#output").append(" ");
$("#output").append(context2.count());

http://jsfiddle.net/hdA6G/1/

以及更好的定义方法

var ConstructorFunction = function (context) {
    this.context = context;
    this.counter = 0;
};
ConstructorFunction.staticCounter = 0;

ConstructorFunction.prototype.count = function () {
    return this.context + "," + (++this.counter) + "," + (++ConstructorFunction.staticCounter);
};

http://jsfiddle.net/hdA6G/3/

答案 3 :(得分:0)

你的继承向后,原型继承自基础对象。我认为这与你想要完成的事情更相似。

function MyObject () {
    this.count = 0;
};

ConstructorFunction.prototype = {
    counter: function () {
        return this.count++;
    },
    print: function () {
        return this.count;
    }
};


Var Foo = new MyObject();

console.log(Foo.counter()); // 1
console.log(Foo.counter()); // 2
console.log(Foo.print()); // 2

我希望这会有所帮助。

修改

如果您希望计数器在所有实例中共享,那么它应该放在您的基础对象中。

function MyObject () {
    this.count = 0;
    this.counter = function () {
        return this.count++; //may not need the this
    }
};

答案 4 :(得分:0)

我知道这是一个老帖子,但我是Javascrpt的新手,在尝试时遇到了类似的东西,所以想要投入2美分。

一旦使用new实例化,每个对象都会获得自己在原型中声明的变量副本。从那时起,使用this进行访问将进行常规查找并找到操作的私有副本。

但是,如果在创建所有对象后创建原型变量,则该变量将被共享并且行为类似于静态。我认为解释为什么会发生这种情况相当简单,但是,尽管如此,我发现这是一个有趣的黑客。我不确定这是否是技术规范中可能在未来版本中解决的错误,或标准行为的副作用,因此不知道这是否可靠。我甚至不确定这是否是一个新推出的功能'在该语言的更高版本中。实际上,我开始在谷歌上搜索这个事实,并发现了这篇文章。

试试这段代码。

var Worker = function (name) {
    this.name = name;
}
Worker.prototype.jobs = 0;
Worker.prototype.Work = function () {
    console.log("jobs finished", this.name, ++this.jobs);
}
Worker.prototype.WorkCount = function () {
    console.log("work count", this.name, this.workCount);
}
var ca = new Worker("ca");
var cb = new Worker("cb");
ca.Work();// shows 1
cb.Work();// shows 1
ca.WorkCount();// shows undefined
cb.WorkCount();// shows undefined
Worker.prototype.workCount = 2;
ca.WorkCount();// shows 2
cb.WorkCount();// shows 2