当在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
)?
为什么init
是undefined
?因为你没有返回任何东西——你正在运行一个函数,并将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()
是在分配了create
和init
之后手动调用的。
我可能遗漏了一些东西,但也许你想要的是:
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();
了解更多关于原型属性的信息!