好久没写博客了,原本想实训结束能对整个实训项目认真总结一下,没想到回到学校一点都不轻松,最近在制作网页版简历,遇到了一个小问题,现在不总结以后肯定忙得顾不上,所以长话短说,抓紧时间写下来.
对js语法比较熟的同学可能都知道:js是没有块级作用域的,有一个新手很容易出错的地方
for(var i = 0 ; i < 10 ; i ++){ setTimeout(function(){ console.log(i) },1000*i)}
这段代码会输出10个10,而不是期望的1,2,...,10,原因正是因为每次循环的作用域是相同的,于是他们共享同一个i,这个问题很简单,这里就不细说了,这次要说的在后面
注意下面这段代码
console.log(a) // undefined
if(!a){ var a = 1;}console.log(a) //1
倘若a不存在,就声明一个变量a并复制为1,这段代码运行起来,控制台会不出意外地打印出 1 , 看起来好像没什么问题, 但实际发生了什么呢?
1. 将该作用域中使用的所有变量提升到当前作用域的首部进行声明,对于这段代码,因为js没有块级作用域,相当于将代码改为
var a;/*其他变量*/
2. 声明完毕后执行代码
if(!a){ //执行到这里时a其实已经声明了,只是没有赋值 a = 1;}console.log(a); //1 /*其他语句*/
看起来好像不是什么太大的问题,但是在某些情况下对这件事情不理解就麻烦啦,举个例子,在遍历树的时候我们要用到递归,就拿递归来说事吧
function test(){ console.log("test"); if(!a){ var a = 1; } if(a < 3){ a++; test(); }}
在外部调用test会发生什么呢? 至少我在写这篇博客以前以为会输出3个test了事,事实是,函数不断地调用自身,无休止地打印test
在开发中写这样一段函数时,我的想法是:在递归的入口函数保存一个数组,函数执行的过程中会根据条件进入不同的分支,从而在这个数组中增或删一些元素,最终将该数组返回,实际运行时发现返回的结果并不符合预期,每一次调用好像都重新声明了一个数组, if(!a) 的判断根本没有向调用者(父作用域)中查找变量,抓耳挠腮了很久,才突然想起以前看过变量提升相关的总结.只是因为当时觉得这个事情在其他语言里也会发生,很简单,就没当回事,果然今天就受到了制裁,耽误了不少时间才想到这里
其实理解这个问题很简单,写下面一段代码测试
if(!a){ //a尚未声明 a = 1;}console.log(a) //报错Uncaught ReferenceError: a is not defined
因为没有使用var关键字,js引擎没有对a进行变量提升,所以在运行到第一行的时候a是未声明的变量,会直接报错
那么怎么test()到底怎么写呢?
var path;function find(orignNode, desNode){ if(!path){ console.log("初始化路径") path = [orignNode]; } temp = /*其他代码*/; find(orignNode, temp); }
目前我是这么解决的,很明显的缺点:占用了父作用域中的变量path,并且每次调用find前都要对path置空,这样的代码看起来很不爽,但是迫于水平有限,一时也没有想出更好的办法(还有一个方法是绑定到当前函数所处的闭包中(this.path),函数return之前
var path = this.path;delete this.path;return path;
感觉也没好到哪里去,若您有好的解决方案,欢迎在下面留言
另外,打个小广告,也是我发现这个问题的源头,就是我的个人主页啦(http://iny7.com),页面还在开发当中,希望能得到各位的指点 ^_^