6

前几段描述了我想要实现的目标,实际问题在最后。谢谢

以前,我只是简单地使用new关键字来创建对象,并使用原型来分配方法和处理继承。然而,最近(部分受到 CoffeeScript 生成的 JS 的启发)我决定使用一个看起来像这样的对象创建函数:

var Test = function(a) {
    function Test(a) {
        this.a = a;
    }
    var numCalls = 0;
    Test.prototype.output = function() {
        alert('I was initialized with ' + this.a);
        numCalls++;
    };
    Test.prototype.called = function() {
        alert('You called the output ' + numCalls + ' times');
    };
    return new Test(a);
};

然后我会像这样创建一个新对象:

var obj = Test('string');

new与用于每个实例的典型方法相比,这种方法有几个优点。首先,我不太可能忘记使用这个词new(我知道还有其他避免的方法new,但据我所见,它们与我在下面描述的问题有类似的问题),其次,我可以很容易地看到 '构造函数在现在属于类的任何函数中看到的私有变量。不过,我在测试时确实遇到了警告。instanceof不再起作用,因为它看不到内部范围的对象 Test。为了解决这个问题,我尝试使用构造函数属性:

var a = Test('one');
var b = Test('two');
a.constructor == b.constructor              // false
a.constructor.name == b.constructor.name    // true

这就是让我感到困惑的原因。在不使用对象创建函数的情况下创建相同的对象会导致它们的构造函数相等,但在这种情况下它们是不同的。我的猜测是,每次函数运行时都会生成一个全新的对象类型(对象结构相同,但原型的实例不同)。

如果我对问题的理解是正确的,这是否也意味着代码为每个对象实例分配额外的内存给我的 JavaScript 用于应该在实例之间共享的函数,方法是欺骗它为每个实例创建相同的对象原型(击败整个使用原型的目的)?如果是这样,是否有一种避免这种情况的好方法,同时仍然保持这种模式的好处(能够在内部函数之间共享私有变量并且不必使用new关键字)?

如果我误解了这个问题,有人可以告诉我实际发生的事情吗?

4

2 回答 2

3

如果是这样,是否有一种避免这种情况的好方法,同时仍然保持这种模式的好处......

尝试使用模块方法:

var Test = (function TestModule() {

  function Test(a) {
    this.a = a;
  }

  Test.prototype = {

  };

  return function(a) {
    return new Test(a);
  };

}());

var a = Test('foo');
var b = Test('baz');

a.constructor == b.constructor; // true
a.constructor.name == b.constructor.name; // true
于 2013-02-02T23:03:48.713 回答
0

现在我努力搜索以完成此任务:具有完全封装且无需“新”实例化它的完美类。经过一段时间的搜索,我想出了这个:

function Test(x){

    var innerFunction = function(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new innerFunction(x);
}

但测试结果证明是错误的:

var a = Test("foo");
var b = Test("baz");

alert(a.constructor == b.constructor);             //false, not good!
alert(a.constructor.name == b.constructor.name);   //true

所以似乎有错误的范围,所以我使用了一个公共内部函数

function Test(x){

    function innerFunction(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new innerFunction(x);
}

并且运行一些广泛的测试证明它是正确的:

var a = Test("foo");
var b = Test("baz");

alert(a.constructor == b.constructor);             //true, made it!
alert(a.constructor.name == b.constructor.name);   //true

alert(a.getA());                        //"foo" as expected
alert(a.getA() == b.getA());            //false as expected

a.variable = "whatever";
alert(a.getA());                       //"foo" as expected
alert(a.variable);                     //"whatever", doesn't seem preventable

a.setA("somewhere");
alert(a.getA());                       //"somewhere", as expected
alert(a.variable);                     //"whatever", doesn't seem preventable

但是,我们可以这样使用几个函数吗?这是我的第一种方法:

function Test(x){

    function innerFunction(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new innerFunction(x);
}

function TestToo(x){

    function innerFunction(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new innerFunction(x);
}

var a = Test("foo");
var b = Test("baz");

var c = TestToo("foo");
var d = TestToo("baz");

alert(a.constructor == b.constructor);             //true, as expected
alert(a.constructor.name == b.constructor.name);   //true, as expected

alert(c.constructor == d.constructor);             //true, as expected
alert(c.constructor.name == d.constructor.name);   //true, as expected

alert(a.constructor == c.constructor);             //false, as expected
alert(a.constructor.name == c.constructor.name);   //true, as NOT expected

所以这是它?我们真的总是需要知道a.constructor.name与字符串比较的内部类结构吗?不,因为在 Javascript 中你可以做任何事情(你只需要知道如何做,而不是为什么),我找到了这个最终解决方案:

function Test(x){

    function Test(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new Test(x);
}

function TestToo(x){

    function TestToo(y){
        var variable = y;

        this.getA = function(){
            return variable;
        }

        this.setA = function(x){
            variable = x;
        }
    }
    return new TestToo(x);
}

var a = Test("foo");
var b = Test("baz");

var c = TestToo("foo");
var d = TestToo("baz");

alert(a.constructor == b.constructor);             //true, as expected
alert(a.constructor.name == b.constructor.name);   //true, as expected

alert(c.constructor == d.constructor);             //true, as expected
alert(c.constructor.name == d.constructor.name);   //true, as expected

alert(a.constructor == c.constructor);             //false, as expected
alert(a.constructor.name == c.constructor.name);   //false, q.e.d.!

我是认真的,我不知道为什么会这样。但它确实 100% 有效,具有 100% 的对象封装,以及与 Java 类的 1:1 相等性。;-)

于 2013-02-03T07:23:48.290 回答