我正在玩弄(阅读:学习)Javascript,并发现了一些我理解的东西,看起来很奇怪。它与闭包和引用有关,似乎“失去”了它对浏览器的重要性。
我使用的浏览器是Chromium 5.0.307.7。
无论如何,这里有一些代码:
HTMLElement.prototype.writeInSteps = function() {
var i = 0;
var elem = this;
var args = arguments;
function step() {
elem.innerHTML += args[i];
if(i < args.length) {
i += 1;
} else {
elem.innerHTML = "";
i = 0;
}
setTimeout(step, 500);
}
step();
}
这里发生的情况是第一个参数被写入正确的 HTMLElement,但后面的所有参数都没有。似乎发生的情况是,在第一个参数之后,以下参数被写入现在由“elem”引用的某个其他元素。
我还应该提到,这似乎只有当我在调用这个函数后直接写一些东西时才会发生,就像这样:
div.writeInSteps("This", " is", " not", " working");
$id("body").innerHTML += "Doh!";
如果我在调用这个函数后不写任何东西,它似乎工作正常。
如果我将上面的代码更改为:
HTMLElement.prototype.writeInSteps = function() {
var i = 0;
var e = this.id;
var args = arguments;
function step() {
var elem = $id(e);
elem.innerHTML += args[i];
if(i < args.length) {
i += 1;
} else {
elem.innerHTML = "";
i = 0;
}
setTimeout(step, 500);
}
step();
}
一切都很好。我的问题是,第一个版本的幕后到底发生了什么?
编辑:按照 Bryan Matthews,我不确定如何提供测试页面而不使这个问题过于混乱。
I was toying (read: learning) around with Javascript and came across something to my understanding, seems very odd. It has to do with closures and a reference that seems to 'loose' its importance to the browser.
The browser I am using is Chromium 5.0.307.7.
Anyway, here's some code:
HTMLElement.prototype.writeInSteps = function() {
var i = 0;
var elem = this;
var args = arguments;
function step() {
elem.innerHTML += args[i];
if(i < args.length) {
i += 1;
} else {
elem.innerHTML = "";
i = 0;
}
setTimeout(step, 500);
}
step();
}
What happens here is that the first argument gets written to the correct HTMLElement, but all the ones after does not. What seems to happen is that after the first argument, the following arguments are written to some other element that is now being referenced by 'elem'.
I should also mention that, this only seems to happen when I write something directly after calling this function, like this:
div.writeInSteps("This", " is", " not", " working");
$id("body").innerHTML += "Doh!";
If I refrain from writing anything after calling this function, it seems to work ok.
If I instead change the above code to:
HTMLElement.prototype.writeInSteps = function() {
var i = 0;
var e = this.id;
var args = arguments;
function step() {
var elem = $id(e);
elem.innerHTML += args[i];
if(i < args.length) {
i += 1;
} else {
elem.innerHTML = "";
i = 0;
}
setTimeout(step, 500);
}
step();
}
Everything is dandy. My question is, what's really happening behind the scenes in the first version?
EDIT: Updated with requested details about "...write something directly after..." and browser usage as requested by ntownsend. Bryan Matthews, I'm not sure how to provide a test page without making this question overly cluttered though.
发布评论
评论(3)
我怀疑这是 DOM 问题,而不是 JavaScript 问题。
我的猜测是,某些东西正在改变您尝试逐步写入的元素的祖先。例如,如果设置了元素父元素的
innerHTML
(我认为甚至设置为完全相同的字符串),则您所拥有的元素引用将指向不再位于 DOM 中的元素。每次通过 ID 重新获取元素就可以解决这个问题。I suspect this is a DOM issue, not a JavaScript issue.
My guess is that something's mutating an ancestor of the element to which you're trying to write in steps. For example, if
innerHTML
of the element's parent is set (even to the exact same string, I think), the element reference you have will be to an element that's no longer in the DOM. Re-getting the element by ID each time would work around that problem.如果您要替换
elem
祖先的innerHTML
(body
元素,根据您的示例),则elem< /代码> 不再存在。当
step
位于原始elem
被销毁之后,elem
引用的内容将是其他内容。浏览器正确的做法可能应该是删除
elem
引用,但它看起来并没有这样做。If you're replacing the
innerHTML
of an ancestor ofelem
(thebody
element, as per your example), thenelem
no longer exists. Whenstep
is after the originalelem
is destroyed, whatelem
is referencing is going to be something else.The correct thing for the browser to do should probably be to remove the
elem
reference, but it doesn't look like it's doing that.我的猜测是,执行
setTimeout(step, 500);
回调几乎不知道this
是谁 - 无论您是否正在调用当前元素的step
或者也许HTMLElement.prototype.writeInSteps.step()
?同时在两个不同的元素上尝试第二个代码(以便第二个
writeInSteps
在第一次超时之前出现)。我很确定它不会达到您的预期。当然,杰夫也可能是对的。
innerHTML
根据规范是只读的,写入它可能会也可能不会重建整个树(并杀死所有引用)。my guess is, execution of
setTimeout(step, 500);
callback has very little idea whothis
is - whether you're calling current element'sstep
or maybeHTMLElement.prototype.writeInSteps.step()
?Try your second code on two different elements simultaneously, (so that second
writeInSteps
comes before first timeout). I'm pretty sure it won't do quite what you expect.Of course Jeff can be right too.
innerHTML
is read-only by specification, and writing to it may or may not rebuild the whole tree (and kill all the references).