构造函数函数中的自执行函数的OO上下文/范围



当在Constructor函数中使用自执行的anon函数时,我有一个关于上下文/范围的快速问题。

遵守以下工作代码:

function Foo() {
    this.height = 10;
    this.width = 10;
    this.init = function() {
        this.create();
    };
    this.create = function() {
        alert('test');
    };
} 
var span1 = new Foo();
span1.init();

警报按预测显示。但是,我不想在底部调用span1.init。我宁愿Constructor函数中的init函数是自执行的。这会给我以下代码:

function Foo() {
    this.height = 10;
    this.width = 10;
    this.init = (function() {
        this.create();
    })();
    this.create = function() {
        alert('test');
    };
} 
var span1 = new Foo();

然而,在谷歌上搜索了一番之后,似乎使用了一个自执行程序,这给了它全局范围,因此this.create在全局意义上并不存在。

我想我必须对电话/申请做点什么,但我不确定具体是什么或在哪里。

任何建议都将不胜感激。

干杯,Ad.

Adi,您在这个例子中所做的有两个问题。第一个问题是在立即调用的函数this === window内部。

第二个问题是,这些都是函数表达式。因此,它们都是在线定义的。

function makeIt () {
    this.a = function () { this.b(); };
    this.b = function () { console.log("B"); };
}

由于后期静态绑定,这将起作用。这意味着,在a内部,浏览器直到调用函数的那一刻才知道this指的是什么。然后它找到this在那个确切时刻所指的对象。

否则,它就像分配变量一样:

function makeIt () {
    this.a = this.b;
    this.b = "George";
}

你会得到一个错误。为什么?只是因为在分配a时,b还没有值。

function Foo () {
    this.init = (function (context) { context.change(); }(this));
    this.change = function () { doStuff(); };
}

那么这个说法有什么问题呢?好吧,立即调用函数是指立即调用的函数。这意味着,即使我们已经解决了this问题,通过将this的值作为参数传递到内部范围。。。

我们要求它运行一些还不存在的东西。

function Foo () {
    this.change = function () { doStuff(); };
    this.init = (function (context) { context.change(); }(this));
}

这应该很好用。…然而…

你为什么要那样做?与中一样,当您希望它自动构造时,为什么要给它一个public init属性(即undefined)?

为什么initundefined?因为你没有返回任何东西——你正在运行一个函数,并将init设置为该函数的返回值,但它没有返回任何内容,所以它将init设置为undefined。为什么init在那里?

两种解决方案:

function Foo () {
    this.change = function () { doStuff(); };
    var init = function () { this.change(); };
    // other stuff......
    init();
}

或:

function Foo () {
    this.change = function () { doStuff(); };
    // other stuff....

    (function (context) {
       context.change();
       /* a bunch of other stuff that would be in init
          if there was no other stuff, why not just call this.change()? */
    }(this));
}

老实说,如果init是私有的,并且自动运行,那么create真的需要是公共的吗?你打算在myObj.create();创建后再调用它吗?

为什么不做一些类似的事情:

function Foo () {
    this.public1 = "Bob";
    this.public2 = 32;
    this.publicMethod = function () {};
    var create = function () { /* initialize here */ };
    create();
}

或者,如果你做的不仅仅是create:

function Foo () {
    this.public1 = "Bob";
    this.public2 = 32;
    this.arrayOfThings = [];
    this.publicMethod = function () {};
    var create = function () {},
        overclock = function () {},
        polish = function () {};
    // Initialize Everything
    (function (context) {
        var thing;
        for (/* ... */) {
            thing = create();
            polish(thing);
            context.arrayOfThings.push(thing);
        }
        overclock(context.arrayOfThings); 
    }(this));
}

现在,所有的函数、属性和变量都在一个范围内,初始化在另一个范围中——所有的设置逻辑都与最终对象的逻辑是分开的。。。你可以做一些事情,比如根据输入参数来分支你的对象(比如多态构造函数,它会根据它得到的修改它给你的东西,同时保持相同的接口——或者一个独立的工厂模式,所有的蓝图都是100%私有和封闭的),而不会让实际的分配看起来像是一堆ifs和fors。

您不必在完成的对象之外调用设置(这意味着没有其他可以调用完成对象上的设置来重新创建/重置它)。它所花费的只是一个匿名函数,你无论如何都要在this.init上使用它。

您可以将上下文传递给新的闭包:

this.init = (function(ctx) {
    ctx.create();
})(this);

请注意,这是在声明init时执行的,因此除非在分配init之前分配create,否则它不会工作。在前面的示例中,这不是问题,因为span1.init()是在分配了createinit之后手动调用的。

我可能遗漏了一些东西,但也许你想要的是:

function Foo() {
    this.height = 10;
    this.width = 10;
    this.init = function() {
        this.create();
    };
    this.create = function() {
        alert('test');
    };
    this.init();
} 

当然,类似地,您可以完全删除init函数,或者将其设为私有函数。例如:

function Foo() {
    this.height = 10;
    this.width = 10;
    this.create = function() {
        alert('test');
    };
    this.create();
} 

此外,请注意,this.init = (function() {})();this.init设置为undefined,因为您的函数不返回任何内容。

是的,call会起作用(apply只是有一个不同的参数,你根本不使用它):

this.init = (function() {
    this.create();
    // return something?
}).call(this);

它是如何工作的(请参阅演示:http://jsfiddle.net/Jw8jz/1/):

function Foo() {
  this.height = 10;
  this.width = 10;
  this.init();
}
Foo.prototype = {
  init: function() {
    this.create();
  },
  create: function() {
    alert('test');
  }
};
var span = new Foo();

了解更多关于原型属性的信息!

最新更新