一、變量作用域
說到這個概念,不有自主的想到this,scope 這兩個關鍵字。
JavaScript的this總是指向一個明確的對象,這個對象是在執行的時候動態綁定的。通俗的說就是誰調用我,我的this就是誰。
除去不常用的with和eval,具體的實際應用中,this的指向分為以下四種情況:
·作為對象的調用;
·作為普通函數的調用;
·構造器的調用;
·Function.propotype.call 和 Functio.propotype.apply 調用;
下面我們分別進行介紹。
1、作為對象的調用
當函數作為對象的方法被調用時,this指向這個對象:
var obj={ a:1, getA:function(){ alert(this===obj); alert(this.a); } }; obj.getA();//執行結果:true、 1
在getA函數中,this指向obj對象。
2、作為普通函數的調用
當函數不作為對象的屬性被調用時,也就是我們常說的普通函數調用方式,此時的this總是指向全局對象,在瀏覽器中,javascripta的全局對象是window對象。
window.name='globalname'; var getName=function(){ return this.name; } alert(getName());//'globalname' //或者 window.name='globalname'; var obj={ name:'scopename', getName:function(){ return this.name; } }; var myObjName=obj.getName; alert(myObjName());//'globalname'
嚴格模式下,函數內部的this不會指向window,而是underfind。
var funF=function(){ alert(this); }; var funStrictF=function(){ 'use strict' alert(this); }; funF();//window funStrictF();//underfind
3、構造器的使用
javascript沒有類,但是可以從構造器中創建對象,同事也提供了new運算符的,是的構造器更像一個類。
除了宿主提供的一些內置函數,發部分javascript函數都可以單做構造器使用。構造器的外表和普通函數一抹一樣,他們的區別在於調用的方式。當用new運算符調用一個函數時,改函數總會返回一個對象,通常情況下,構造器里的this就指向了這個對象,案例如下:
var createClass=function(){ this.name='sven'; return { name:'anne' }; }; var obj=new createClass(); alert(obj.name);//輸出 anne
4、Function.propotype.call 和 Function.propotype.apply 的使用
跟普通的函數調用相比,用Function.propotype.call 和 Function.propotype.apply 可以動態的改變傳入函數的this。
var obj1={ name:'sven', getName:function(){ return this.name; } }; var obj2 ={ name:'anne' }; alert(obj1.getName());//輸出 sven alert(obj2.name);//輸出 anne alert(obj1.getName.call(obj2));//輸出 anne
javascript中,變量的作用域不是塊級的,而是以function為單位。所謂塊級,就是{}花括號括起來為一塊,以function為單位,就是指變量的作用域上限就是當前所在的函數。看個例子:
var a=100; var fun=function(){ var a=6; alert(a); }; alert(a);//100 fun();//6 for(var i=0;i<5;i++){ var a =i; } alert(a);//4
雖然在for{} 中重新聲明的a變量,實際上,a的作用域仍是全局環境,所以也只是改變了最初a的值。而在fun(){}中,作用域變成了fun函數內部,所以輸出 5
二、接下來就是本篇的重點:變量聲明提升
當我們的聲明在同作用域靠后的位置,變量的聲明會被自動提升到作用域的開頭。
當我們如下聲明一個變量
var a=100; 其實做了三件事:聲明變量、執行變量的數據類型、賦值 如果是下面的代碼呢? alert(a); var a=100; 實際上等同於 var a; alert(a);//underfind a=100;
來個復雜的例子
var a = 100; alert(a);//100 var a = 200; alert(a);//100 function fun2() { alert(a);//underfind; var a = 3; } fun2(); alert(typeof a);//unmber var a = function() {} alert(typeof a);//function
上述例子中,對a進行了多次聲明,我們要區分個a的作用域,將聲明做一次提升。其實等同於
var a; var a ; var a ;//多次聲明會合並為一個對象 a=100; alert(a); a=200; alert(a); function fun2() { var a; alert(a);//underfind; a = 3;//確定變量的數據類型,賦值 } fun2(); alert(typeof a);//unmber a=function(){}; alert(typeof a);//function
變量提升還有隱式提升?
看下面的例子
function foo; var foo; alert(typeof foo); foo = function(){}
輸出function?這是為什么?再看看下面的例子
alert(fun1);//fucntin alert(fun2);//underfind function fun1(){} var fun2=function(){}
沒有的對比就沒有傷害,上述例子中應該就能知道一點答案。
普通的通過var聲明一個變量,只做聲明,沒有定義變量的數據類型。而通過function聲明一個變量,在聲明的同時會定義變量的數據類型為funtion。上面我們有說過,在同級作用域內,相同名稱的變量會合並為一個變量,也就是后者會覆蓋前者,這里也是一樣的。
前者定義一個foo變量,數據類型為function;
后者有頂一個foo變量,覆蓋前者,然而沒有定義變量類型。所以foo的數據類型仍然是function。這個道理和下面的例子是一樣的
var a=100; var a; alert(a);//輸出100
這也是我們在寫javascript的時候,變量要寫在被使用的前面,而函數可以寫在后面的解釋。
變量的聲明也有優先級?
優先級如下:
語言內置:作用域內的this和arguments關鍵字
形式參數:函數的參數在作用域內總是有效的
函數聲明:例如function(){}
變量聲明:例如 var a
舉個例子:
function fun(a){ var b = 100; function go() {} }
聲明的順序是:fun->this,arguments->go->a
聲明歸聲明,該覆蓋的還是會覆蓋,不舉例子了。