1. 程式人生 > 其它 >作用域原理揭祕,你真的瞭解作用域嗎

作用域原理揭祕,你真的瞭解作用域嗎

技術標籤:jsjavascriptjs

首先,啥是作用域,作用域就是一個變數可被訪問的範圍,也就是說,你定義了一個變數,我在哪裡可以訪問到它,哪裡就是它的作用域,全域性可以訪問就是全域性,函式體內可以訪問就是函式體內。

在這裡插入圖片描述
然後,es6新加了let和const 我們先不討論,因為這倆的作用域很好判斷,我們討論var,也就是這個坑最多,面試經常問的東西

我們要明確,var是沒有塊級作用域的!

var scope="global";
function t(){   
 console.log(scope);   
 var scope="local"
console.log(scope);}

猜猜輸出啥,輸出undefine,那麼為啥輸出這個呢,中間經過了什麼呢

這就要涉及到第一個知識點
var宣告的變數會有變數提升,就是會把宣告提升到函式內部最前面,賦值操作在原位置
然後就引出了第二個概念函式作用域
就是說,函式內部的變數,在函式體內任何位置都是有定義的!但是隻有賦值之後才有值,懂了吧,

我們可以看到,由於函式作用域的特性,區域性變數在整個函式體始終是由定義的,我們可以將變數宣告”提前“到函式體頂部,同時變數初始化還在原來位置。

為什麼說Js沒有塊級作用域呢,有以下程式碼為證:

var name="global"
; if(true){ var name="local"; console.log(name) } console.log(name)

說到這裡,有一個非常需要注意的問題,快作用域和函式作用域

var a=10;
	function fun()
	{
		var a=100;
		a++;
		console.log(a);
	}
	fun();//101

	console.log(a);//10

觀察,為什麼第一個修改了全局裡面的name,但是第二個沒有修改全局裡面的a呢,因為第一個使if大括號,第二個是函式大括號,有函式作用域,裡面定義的變數是獨立於函式體外的
你以為加一個大括號就是塊級作用域了嗎,錯,只有放在函式體內,才會實現塊級作用域的功能

function t(flag){
    if(flag){
        s="ifscope";
        for(var i=0;i<2;i++) 
            ;
    }
    console.log(i);
}
t(true);
console.log(s);

將宣告s中的var去掉。
程式會報錯還是輸出“ifscope"呢?

讓我揭開謎底吧,會輸出:”ifscope"

這主要是Js中沒有用var宣告的變數都是全域性變數,而且是頂層物件的屬性。

當使用var宣告一個變數時,建立的這個屬性是不可配置的,也就是說無法通過delete運算子刪除

var name=1 ->不可刪除

sex=”girl“ ->可刪除

this.age=22 ->可刪除

三:作用域鏈

name="jack";
function t(){
    var name="mary";
    function show(){
        var name="tom";
        console.log(name);
    }
    function shows(){
        console.log(name);
    }
    show();
    shows();
}
t();

分析:
當呼叫t函式show指向時,將自己的函式環境壓入棧中,該函式內部有name所以不需要上級索引,直接輸出

當呼叫shows函式時,函式環境入棧,但是本函式裡面沒有name,所以將上一級函式環境入棧,也就是t函式,使用裡面的name,注意不是show函式,而是父函式

這就是作用域鏈

下面看一個很容易犯錯的例子:

Button1 Button2 Button3 當文件載入完畢,給幾個按鈕註冊點選事件,當我們點選按鈕時,會彈出什麼提示框呢? 很容易犯錯,對是的,三個按鈕都是彈出:"Button4",你答對了嗎?

當註冊事件結束後,i的值為4,當點選按鈕時,事件函式即function(){ alert(“Button”+i);}這個匿名函式中沒有i,根據作用域鏈,所以到buttonInit函式中找,此時i的值為4,

所以彈出”button4“。

四:with語句

說到作用域鏈,不得不說with語句。with語句主要用來臨時擴充套件作用域鏈,將語句中的物件新增到作用域的頭部。

看下面程式碼

person={name:“yhb”,age:22,height:175,wife:{name:“lwy”,age:21}};
with(person.wife){
console.log(name);
}
with語句將person.wife新增到當前作用域鏈的頭部,所以輸出的就是:“lwy".
with語句結束後,作用域鏈恢復正常。