【问题标题】:object-creating function对象创建函数
【发布时间】:2013-02-03 15:54:06
【问题描述】:

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

以前,我只是使用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 关键字)?

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

【问题讨论】:

  • 为什么忘记关键字new?我的意思是,每次创建新对象时,都使用“new”,这非常简单。这个解决方案在许多 OO 语言中存在了几十年,这是一件很棒的事情,因为每个人都可以阅读和理解你的代码。虽然没有人理解 var obj = Test('string'),但您的代码的可读性会降低。但我知道,这是一些 JS 程序员的最终目标。 ;)
  • @Marcus,如前所述,new 关键字并不是这种模式的唯一优势。另外,我不是团队中唯一的程序员。假设每个人都永远记得使用new 关键字,同时还用Python 编写后端(它不使用new 进行类)是不现实的。在类声明的末尾发现缺少的new 比在每次创建实例时要容易得多。
  • 啊,我现在明白了:没有编译器告诉程序员他忘记了“new”,所以这就是你不想使用它的原因。
  • 据我所知,这是 IE 中的一个问题?!

标签: javascript memory-management


【解决方案1】:

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

尝试使用模块方法:

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

【讨论】:

  • 第一个(var Test = (function TestModule() { 行中是什么意思?这种代码我真的看不懂……
  • @Marcus:这是一个 IIFE。看看这里benalman.com/news/2010/11/…
【解决方案2】:

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

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

所以好像是作用域不对,所以我用了一个public内部函数

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 相等性。 ;-)

【讨论】:

  • 这就像使用“Goto”:转到“Test”,然后创建一个名为“Test”的类并实例化该类。这证明 Javascript 函数本身不是类,只有当你调用“new”时它才成为一个类!
  • 问题:我在 IE 中没有得到相同的结果!结果是:a.constructor == b.constructor // false 和 IE 中的 a.constructor.name == b.constructor.name // true。但是功能本身仍然可以正常工作。 %D
猜你喜欢
  • 1970-01-01
  • 2021-03-16
  • 2012-04-07
  • 2012-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
相关资源
最近更新 更多