好的,你有很多学习要做。我的意思并不是说不好,但这里有一个 vanillaJS 示例,说明我将如何解决这个问题:
window.addEventListener('load',function l()
{//use addEventListener, to avoid mem-leaks
"use strict";//for JSLint
var more = document.getElementById('more'),
less = document.getElementById('less'),
div = more.parentNode,//3 DOM reference, to be used by event handlers
added = [],//keep references to added elements, use as stack
rmHandle = function(e)
{//callback definition, don't bind unless less link should be usable
var rm = added.pop();
rm.parentNode.removeChild(rm);
if (added.length === 0)
{
less.removeEventListener('click', rmHandle, false);
}
e.preventDefault();
e.stopPropagation();
};
more.addEventListener('click',function(e)
{//add node:
var newP, count = added.length;
e.preventDefault();
e.stopPropagation();
if (count === 0)
{//bind less event handler here
less.addEventListener('click', rmHandle, false);
}
++count;
newP = document.createElement('p');//create node
newP.setAttribute('id','param'+count);//set id
newP.appendChild(document.createTextNode('New Paragraph #'+count));//add txt content
added.push(newP);//keep reference to node
div.insertBefore(newP, less);//append at end...
},false);
window.removeEventListener('load',l,false);//unbind load handler, this is the leak in IE
}, false);
现在,这本身有点没有意义,所以我继续设置这个小提琴
还有很多事情要做(即卸载事件处理程序,隐藏更少的链接等......)
一些澄清,以帮助您理解代码:
addEventListener
onload
:我没有设置属性,也没有使用or直接绑定事件处理程序onclick
,而是添加了事件侦听器。这样做的好处是保留了 JS 端的所有内容。您正在setAttribute('onclick'...)
代码中的某处使用。这在 DOM 中设置了一个属性,该属性引用回 JS。这被认为是不好的做法,而且已经过时了。
l
-打回来。我的主要回调 (window.addEventListener('load', function l()...
被称为l
。在这个函数中,我查询 DOM 三次(排序),并将这些 DOM 节点的引用分配给一个变量:more
和less
。div
接下来,我声明一个数组,以保存所有节点 I'将创建。一种堆栈,所以我永远不需要查询 dom 来获取对我创建的那些节点的引用。
- 我还声明了一个函数 (
rmHandle
),它将处理less
链接上的点击。因为我在 的范围内声明了这个函数l
,所以该函数可以访问我之前声明的所有变量 ( less
, more
, added
)。再说一遍:我永远不需要查询 DOM ......
more.addEventListener
:此链接必须从关闭开始工作,因此我将事件侦听器附加到加载时的此 DOM 节点。
- 否
return false
:您的问题表明您知道/使用过 jQuery。return false
在 jQ 中和return false
在 JavaScript中不是一回事。如果您在 VanillaJS 中将事件处理程序附加到表单return false
可能会让您大吃一惊,因为有时:表单仍将被提交。阅读 W3C 事件模型:捕获和冒泡阶段。quirksmode.org 是了解详细信息的好资源。你很快就会明白我为什么要显式调用这些方法。
document.createTextNode
: 现在我也承认偶尔使用innerHTML
。不过既然你是在学习,我不妨指出这innerHTML
不是标准,官方标准是使用createTextNode
。
- 最后我删除了
load
事件监听器,因为如果你不这样做,IE 往往会泄漏内存。然后,回调超出范围,您无能为力。所以所有东西都可以被标记为 GC,并且没有任何东西可以泄漏内存,仍然......
编辑:
我承认,列出一些列表项,其中一个只是简单地涉及 JS 在嵌套范围内解析变量名的方式还不够清楚。当我第一次开始学习闭包时,它并不适合我,当然也不足以解释为什么我发布的代码会比你的代码好很多。
因此,如果您能做到这一点,我将使用您的代码摘录对此进行更多解释,并引导您完成清理审查:
var less = document.getElementById("less");
less.onclick = function less()
{
var para1 = document.getElementById("para1");
var para2 = document.getElementById("para2");
alert("fr");
alert( para1.innerHTML);
para1.parentNode.removeChild(para1);
para2.parentNode.removeChild(para2);
var less = document.getElementById("less");
var toMore = less.setAttribute("id", "more");
var more = document.getElementById("more");
more.setAttribute("onclick", "more(); return false;");
more.innerHTML ="click here for more";
return false;
};
这段代码对您来说应该很熟悉(毕竟它是从您的问题中复制粘贴而来的)。现在我为什么要改变这个?首先:DOM API(不是JS BTW 的一部分)缓慢、笨重、不合逻辑并且是挫折的主要来源,并且过多地使用它会杀死林地生物。但是,在此代码段中,我们看到了这一点:
var less = document.getElementById("less");
less.onclick = function less()
{
//...
var less = document.getElementById("less");
}
因此,该名称 less
在分配上下文中被使用了 3 次。其中两个任务涉及 DOM 查询。不仅仅是一个查询,而是完全相同的查询:document.getElementById("less");
!人们常说,编写好代码的规则之一是不要重复自己。
您可能听说过的另一件事是,即使使用松散类型的语言,不将不同类型分配给一个变量也不是一个坏主意。您正在这样做,尽管当您编写function less(){}
. 除了一些(有时很重要,但那是其他时间)语义差异之外,这基本上与执行以下操作相同:
var less = function(){};
这些分配中的每一个都掩盖了前一个分配。如果你会写:
var less = document.getElementById("less");
less.onclick = function less_func()
{
console.log(less);//logs a dom reference!
};
//or even:
less.onclick = function()
{//anonymous or lambda functions are valid... and quite common, too
console.log(less);
};
您根本不需要使用该onclick
函数的第二个 DOM 查询。这是因为如果 JS 尝试将所有变量解析为先前声明的变量的方式。考虑一下:
var evilGlobal = 'Do not use Globals';
function()
{
var goodLocal = 'Declared in function',
funcVar = function()
{
console.log(goodLocal);
console.log(evilGlobal);
},
func2 = function goodLocal(evilGlobal)
{
console.log(goodLocal);
console.log(evilGlobal);
console.log(funcVar());
};
funcVar();//logs Declared in function and Do not use Globals
func2();//logs itself (function), and undefined and then same as above
func2(goodLocal);//logs itself, Declared in Function and the same as funcVar
}
这是怎么回事?其中funcVar
相当简单:
console.log(goodLocal);//<-- JS looks inside funcVar's function scope for var goodLocal
//not found? JS looks in the outer scope, that of the anonymous function that starts
//with var goodLocal = 'Declared in Function'
//This is the var used
这同样适用于console.log(evilGlobal)
。只有这一次,JS 会扫描funcVar
的作用域、匿名函数的作用域和全局命名空间。为什么不应该使用全局变量?好吧,它们显然更慢,它们可以更改状态,因为函数可以自由访问它们,并且它们会阻塞内存(垃圾收集器只会释放不再在任何地方引用的内容。全局命名空间始终是可访问的)。
第二种情况有点棘手,但不是很多:
function goodLocal()//the goodLocal name is defined as the function!
此名称掩盖了外部范围内的变量。JS开始扫描本地范围,发现goodLocal
指向函数。它从不检查外部范围,因此它永远不会goodLocal
在父函数中看到 var。
这同样适用于evilGlobal
:
function goodLocal(evilGlobal)
参数是在函数范围内声明的变量。JS 永远不会扫描全局 ns,因为这两个名称都可以本地解析,除了:
控制台.log(funcVar());
这将导致父函数的范围扫描,它声明funcVar
变量,并将前面讨论的函数分配给它。该函数的行为仍然没有什么不同,因为该函数是在其自己的范围/上下文中调用的。
调用上下文也很棘手,所以我将暂时忽略这一点。
回到您的代码:其他语句实际上也是您之前编写的内容的重复:var para1
并且var para2
是多余的,如果您只是让它们在外部范围内可访问。
嗯,继续阅读,继续学习,你很快就会明白的......