大家好,我是IT修真院鄭州分院第四期的學員王相博,一枚正直、純潔、善良的前端程序員
今天給大家分享一下,修真院官網JS(職業)任務4,深度思考中的知識點——如何理解JS作用域與作用域鏈?
1.背景介紹
任何程序設計語言都有作用域的概念,簡單的說,作用域就是變量與函數的可訪問范圍,即作用域控制著變量與函數的可見性和生命周期。在JavaScript中,變量的作用域有全局作用域和局部作用域兩種。
2.知識剖析
1.全局作用域(Global Scope)
在代碼中任何地方都能訪問到的對象擁有全局作用域,一般來說一下幾種情形擁有全局作用域:
(1)最外層函數和在最外層函數外面定義的變量擁有全局作用域,例如:
var aName="瀧澤蘿拉";
function doSomething(){
var bName="吉澤明步";
function innerSay(){
alert(bName);
}
innerSay();
}
alert(aName); //瀧澤蘿拉
alert(bName); //腳本錯誤
doSomething(); //吉澤明步
innerSay() //腳本錯誤
(2)所有末定義直接賦值的變量自動聲明為擁有全局作用域,例如:
function doSomething(){
bName="加藤鷹";
alert(bName)
}
alert(bName); //加藤鷹
(3)所有window對象的屬性擁有全局作用域
一般情況下,window對象的內置屬性都都擁有全局作用域,例如window.name、window.top等等。
1. 局部作用域(Local Scope)
和全局作用域相反,局部作用域一般只在固定的代碼片段內可訪問到,最常見的例如函數內部,所有在一些地方也會看到有人把這種作用域成為函數作用域
例如下列代碼中的bName和函數innerSay都只擁有局部作用域:
function doSomething(){
var bName="楊澤平";
function innerSay(){
alert(bName);
}
innerSay();
}
alert(bName); //腳本錯誤
innerSay(); //腳本錯誤
作用域鏈(Scope Chain)
有了JavaScript的作用域的劃分,那么可以將JavaScript的訪問作用域連成一個鏈式樹狀結構.
JavaScript的作用域鏈一旦能清晰的了解,那么對于JavaScript的變量與閉包就是非常清晰的了.
下面采用繪圖的辦法,繪制作用域鏈.
3.1 繪制規則:
1) 作用域鏈就是對象的數組
2) 全部script是0級鏈,每個對象占一個位置
3) 凡是看到函數延伸一個鏈出來,一級級展開
4) 訪問首先看當前函數,如果沒有定義往上一級鏈檢查
5) 如此往復,直到0級鏈
先看一段代碼
var num = 10;
var func1 = function() {
var num = 20;
var func2 = function() {
var num = 30;
alert(num);
};
func2();
};
var func2 = function() {
var num = 20;
var func3 = function() {
alert(num);
};
func3();
};
func1();
func2();
下面分析一下這段代碼:
-> 首先整段代碼是一個全局作用域,可以標記為0級作用域鏈,那么久有一個數組
var link_0 = [ num, func1, func2 ];// 這里用偽代碼描述
-> 在這里func1和func2都是函數,因此引出兩條1級作用域鏈,分別為
var link_1 = { func1: [ num, func2 ] };// 這里用偽代碼描述
var link_1 = { func2: [ num, func3 ] };// 這里用偽代碼描述
-> 第一條1級鏈衍生出2級鏈
var link_2 = { func2: [ num ] };// 這里用偽代碼描述
-> 第二條1級鏈中沒有定義變量,是一個空鏈,就表示為
var link_2 = { func3: [ ] };
-> 將上面代碼整合一下,就可以將作用域鏈表示為
// 這里用偽代碼描述
var link = [ // 0級鏈
num,
{ func1 : [ // 第一條1級鏈
num,
{ func2 : [ // 2級鏈
num
] }
]},
{ func2 : [ // 第二條1級鏈
num,
{ func3 : [] }
]}
];
4.常見問題
如何更加直觀的體現作用域鏈
4.解決方案
5.編碼實戰
var x = 10;
function getX() {
alert(x);
}
function foo() {
var x = 20;
getX();
}
foo();
6.擴展思考
如何運用作用域鏈的知識進行閉包優化
其實作用域鏈就是JS引擎查詢數據的一個鏈表,后定義的覆蓋先定義的,查詢不到定義的數據就往深一層查詢,一直到全局作用域為止
function changeColor(){
document.getElementById("btnChange").onclick=function(){
document.getElementById("targetCanvas").style.backgroundColor="red";
};
}
這段代碼可以重寫如下:
function changeColor(){
var doc=document;
doc.getElementById("btnChange").onclick=function(){
doc.getElementById("targetCanvas").style.backgroundColor="red";
};
}
這段代碼比較簡單,但是如果程序中有大量的全局變量被從反復訪問,那么重寫后的代碼性能會有顯著改善。
7.參考文獻
參考一51cto
參考二博客
8.更多討論
變量提升
var X=10;
functiona(){
? ? alert(X)
}
functionb(){
? ? alert(X);
? ? varX=5;
}
為什么函數a可以讀到全局變量X,函數b讀到的是Undefined?
因為尋找變量的時候優先尋找局部變量,找到了就不會去找全局變量了,但是局部變量的X還沒有賦值,所以就是Undefined的狀態,這種形式稱為變量提升。
PPT鏈接:https://ptteng.github.io/PPT/PPT/js-04-scopes.html#/