JavaScript 變量作用域和聲明提升


一、變量作用域
說到這個概念,不有自主的想到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
聲明歸聲明,該覆蓋的還是會覆蓋,不舉例子了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM