您的位置:首页 > Web前端 > JavaScript

读李战的悟透JavaScript的一点理解--深入了解预编译

2013-06-20 11:17 471 查看
向大师致敬!以前虽然看过,但是不求甚解,最近开始痛下苦功,有点收获。推荐李战的书 --悟透JavaScript

http://book.douban.com/subject/3271104/ 豆瓣上的介绍

在javascript里的全局环境就是一个对象,这个对象是javascript运行环境的根。对于浏览器中的javascript来说,这个根对象就是我们熟知的window对象。对于全局的javascript语句来说,window对象就相当于当前作用域。

当我们写下:

var myname="leadzen" ; //就是定义了window作用域的一个变量myname,而当我们写下:
myname="lead_zen";    //就是定义了window对象的一个属性myname


在这里,window作用域的一个变量 myname和window对象的一个属性myname几乎等价。

alert(window.myname1); //输出lead_zen
alert(window.myname); //输出leadzen
alert(myname); //输出leadzen



看另一个例子:

var yourname="真真";
myname="spring";
var interesting="laughing gor";
alert(myname + " like " + yourname);   //输出spring like 真真
changenames();     //直接调用函数,即使函数定义在后面。 函数预编译
function changenames() {
alert('your old name is ' + yourname);  //输出 your old name is undefined ,因为var yourname,这样yourname赋值undefine
alert('my old name is ' + myname);     //输出my old name is spring 因为myname是window新添加的一个属性。原型链的问题
//虽然后面有句重设值的语句,但是,还没执行到,所以取全局的值
//这说明在函数体内也有预编译,对于var定义的变量,会先定义,即使先使用了该变量,也不会报错,只是该值是undefined而已
alert('interesting ' + interesting);  //输出interesting laughing gor,这说明查找全局作用域(上层作用域)
//上面两个例子说明,全局变量,无论有无var定义,在函数内都能读取
alert('perporty of window name' + name );  //向上追溯的问题。最终找到window.name属性,这个为空。所以没有输出
var yourname = "海琴";
myname="小莫";                         //全局变量,重设了该值
alert(myname + " do like " + yourname); //输出小莫 do like 海琴 ,这说明yourname被赋值了
}
alert(myname + " like " + yourname);   //输出小莫 like 真真   海琴只是函数changenames的一个私有变量
alert(window.interesting);             //输出laughing gor,说明全局变量定义是添加或修改了window属性


这里涉及到函数预编译

当代码运行进入一个函数时,javascript会创建一个新的作用域,来作为当前作用域的子作用域。然后,将当前全局作用域切换为这个新建的子作用域,开始执行函数逻辑。javascript执行引擎会把此函数内的逻辑代码,当一个代码段单元来分析和执行。 在第一步的预编译分析中,javascript执行引擎将所有定义式函数直接创建为作用域上的函数变量,并将其值初始化为定义的函数代码逻辑,也就是为其建立了可调用的函数变量。而对于所有"var"定义的变量,也会在第一步的预编译中创建起来,并将初始值设为undefined。

看一个特别的例子

function changenames() {
var yourname = "海琴";
myname="小莫";
}
alert(myname);   //直接报错,为什么呢?因为预编译只是在该函数内有效
//alert(yourname);   //报错,也是同样道理


但是,

function changenames() {
var yourname = "海琴";
myname="小莫";
}
changenames();
alert(myname);  //输出小莫,因为函数已经执行了,myname是全局变量了,所以,能输出该变量的值

alert(yourname);  //报错,因为yourname是函数体内的私有变量,所以,无法读取。


随后,javascript开始解释执行代码。当遇到对函数名或变量名的使用时,javascript执行引擎会首先在当前作用域查找函数或变量,如果没有就到上层作用域查找,依次类推。

因此,用"var"定义的变量只对本作用域有效,尽管此时上层作用域有同名的东西,都与本作用域的"var"变量无关。退出本作用域之后,此"var"消失,回到原来的作用域,该有啥就还有啥,该是谁就还是谁。

额外的思考

var aa = "dd";
//aa="dd";
function bb() {
alert(this.aa);    //输出dd,而不是undefined
alert(aa);
var aa = "cc";
alert(aa);
}
bb();  //输出dd、undefined、cc,即使第一句没有var,aa="dd";调用该函数时,还是输出dd、undefined、cc
//这里强调的是this,this指谁呢?window对象 。
//alert(window.aa);  //输出dd


网上的例子:

var x = 1, y = z = 0;
function add(n) {
n = n+1;
  }

y = add(x);

function add(n) {
n = n + 3;
}

z = add(x);


执行完毕后 x, y, z 的值分别是多少? 仔细看的人马上就知道了, x, y 和 z 分别是 1, undefined 和 undefined。 因为调用函数后,没有返回值过来

还一个例子:

<script>
alert(typeof addA);
addA();
function addA() {
alert("A executed!");
};
</script>
<script>
alert(typeof addB);
addB();    //为什么会是报错呢,因为addB是undefined,如果,var addB ...放在addB()前,那结果正常。
//也就是预编译的值都是undefined的,你如果执行/调用,肯定报错
var addB = function() {
alert("B executed!");
};
</script>


第一个 <script> 块执行正常,结果就是弹出 "function" 和 "A executed!" 的对话框。

那么第二个 <script> 块呢? 执行结果是弹出 "undefined" 的对话框后报 JS 错误,说 addB 不是一个 function。 有点出乎意料?呵呵,其实第一个 script 块中的 addA 一句是函数声明,当然进行了"预编译",但是第二个 script 块中的 addB 一句并非函数声明。只不过在执行这段 <script> 之前对变量进行了"预声明",因此一开始变量addB是存在的,只不过是 undefined 的,调用肯定会出错。

将题目再变化下,如下

alert(typeof addB);
addB();
var addB = function addB() {
alert("B executed!");
};
将正常输出 ,结果就是弹出 "function" 和 "B executed!" 的对话框。

再变化一下:

alert(typeof addB);
var addB = "variable";
function addB() {
alert("function addB");
}
alert(addB);
执行结果是"function"和"variable"。

JS解析器先预定义了 addB 变量为 undefined, 但是 addB 函数覆盖了此变量,因此一开始执行结果是 function,然后 addB 被赋值为 "variable",因此最后执行结果是 "variable",上面的代码即使变为

alert(typeof addB);
function addB() {
alert("function addB");
}
var addB = "variable";
alert(addB);
这说明JS解析器先预声明变量,再预定义函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: