4

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

观察以下工作代码:

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 。我宁愿让构造函数中的初始化函数自动执行。这会给我如下代码:

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

var span1 = new Foo();

然而,经过一些谷歌搜索,似乎使用自我执行给了它全局范围,因此 this.create 在全局意义上不存在。

我想我必须对 call/apply 做点什么,但我不确定具体是什么或在哪里。

任何指针将不胜感激。

干杯,广告。

4

5 回答 5

2

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

为什么是init undefined?因为你没有返回任何东西——你正在运行一个函数并设置init为函数的返回值,但它没有返回任何东西,所以它设置initundefined. 那为什么要在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% 私密和封闭),不会让实际作业看起来像一堆 if 和 fors。

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

于 2012-08-21T13:47:45.573 回答
0

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

this.init = (function() {
    this.create();
    // return something?
}).call(this);
于 2012-08-21T13:04:39.960 回答
0

它是如何工作的(参见演示: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();

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

于 2012-08-21T13:02:09.090 回答
0

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

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.initundefined,因为您的函数不返回任何内容。

于 2012-08-21T13:02:21.280 回答
0

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

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

请注意,这是在声明时执行的,因此除非您在分配之前分配init,否则它将不起作用。在您之前的示例中,这不是问题,因为在两者之后手动调用并已分配。create initspan1.init()createinit

于 2012-08-21T12:56:28.217 回答