從作用域到作用域鏈,思維腦圖+代碼示例讓知識點一目瞭然!系列(三)

作用域

作用域的篇幅不會太長,作爲自己對Js總結的第三篇文章,主要是承上啓下。
之後會涉及到執行上下文,閉包等相關專題,爲了避免內容過多,作用域這一部分單獨總結。

目錄

前言

在這裏插入圖片描述

JavaScript內功系列:

  1. this指向詳解,思維腦圖與代碼的結合,讓你一篇搞懂this、call、apply。系列(一)
  2. 從原型到原型鏈,修煉JavaScript內功這篇文章真的不能錯過!系列(二)
  3. 本文

一、作用域的定義

一張導圖概括本節內容

在這裏插入圖片描述

注意:除了作用域,在此送大家2020最新企業級 Vue3.0/Js/ES6/TS/React/Node等實戰視頻教程,點擊此處免費獲取,小白勿進哦

1.1 常見的解釋

  1. 一段程序代碼中所用到的名字並不總是有效,而限定它的可用性的範圍就是這個名字的作用域;
  2. 作用域規定了如何查找變量,也就是確定當前執行代碼對變量的訪問權限;
  3. 通俗的講作用域就是一套規則,用於確定在何處以及如何查找某個變量的規則
function func(){
	var a = 100;
	console.log(a); // 100
}
console.log(a) // a is not defined a變量並不是任何地方都可以被找到的

1.2 JavaScript中作用域工作模型

JavaScript 採用是詞法作用域(lexical scoping),也就是靜態作用域:

  • 函數的作用域在函數定義的時候就決定了

與之對應的還有一個動態作用域:

  • 函數的作用域是在函數調用的時候才決定的;

1.3 全局變量和局部變量

根據定義變量的方式又可以分爲:

局部變量:只能在函數中訪問,該函數外不可訪問;

  • 定義在函數中的變量
function fn(){
	var name = '餘光';
	console.log(name);
}
console.log(name); // ?
fn(); // ?

全局:任何地方都能訪問到的對象擁有全局作用域。

  • 函數外定義的變量
  • 所有末定義直接賦值的變量自動聲明爲擁有全局作用域
var a = 100;
console.log('a1-',a);

function fn(){
	a = 1000;
	console.log('a2-',a);
}
console.log('a3-',a);
fn();
console.log('a4-',a);

注意:在ES6之後又提出了塊級作用域,它們之間的區別我們之後再來討論。

在這裏插入圖片描述

二、理解作用域

根據第一節的描述,我們一一驗證一下

2.1 理解詞法作用域

var value = 1;
function foo() {
    console.log(value);
}
function bar() {
    var value = 2;
    foo();
}
bar();

我們結合定義去分析:

  • 執行bar函數,函數內部形成了局部作用域;
  • 聲明value變量,並賦值2
  • 執行foo函數,函數foo的作用域內沒有value這個變量,它會向外查找
  • 根據詞法作用域的規則,函數定義時,foo的外部作用域爲全局作用域
  • 打印結果是1

如果是動態作用域的話:結果就是2,不知道你是否想明白了?

2.2 全局變量

var str = '全局變量';
function func(){
	console.log(str+1);
	function childFn(){
		console.log(str+2);
		function fn(){
			console.log(str+3);
		};
		fn();
	};
	childFn();
}
func();
// 全局變量1
// 全局變量2
// 全局變量3

再來分析下面的代碼:

var a = 100;
function fn(){
	a = 1000;
	console.log('a1-',a);
}
console.log('a2-',a);
fn();
console.log('a3-',a);
// a2- 100 // 在當前作用域下查找變量a => 100
// a1- 1000 // 函數執行時,全局變量a已經被重新賦值
// a3- 1000 // 全局變量a => 1000

2.3 局部作用域

局部作用域一般只在固定的代碼片段內可訪問到,最常見的就是以函數爲單位的:

function fn(){
    var name="餘光";
    function childFn(){
        console.log(name);
    }
    childFn(); // 餘光
}
console.log(name); // name is not defined

三、作用域鏈

3.1 當查找變量的時候都發生了什麼?

  • 會先從當前上下文的變量對象中查找;
  • 如果沒有找到,就會從父級(詞法層面上的父級)執行上下文的變量對象中查找;
  • 一直找到全局上下文的變量對象,也就是全局對象;
  • 作用域鏈的頂端就是全局對象;

這樣由多個執行上下文的變量對象構成的鏈表就叫做作用域鏈,從某種意義上很類似原型和原型鏈。

3.2 作用域鏈和原型繼承查找時的區別:

  • 查找一個普通對象的屬性,但是在當前對象和其原型中都找不到時,會返回undefined
  • 查找的屬性在作用域鏈中不存在的話就會拋出ReferenceError

3.3 作用域嵌套

既然每一個函數就可以形成一個作用域(詞法作用域 || 塊級作用域),那麼當然也會存在多個作用域嵌套的情況,他們遵循這樣的查詢規則:

  • 內部作用域有權訪問外部作用域;
  • 外部作用域無法訪問內部作用域;(真是是這樣嗎?)
  • 兄弟作用域不可互相訪問;

在《你不知道的Js》中,希望讀者可以將作用域的嵌套和作用域鏈想象成這樣:

在這裏插入圖片描述

四、思考與總結

4.1 總結

在這裏插入圖片描述

4.2 思考

最後,讓我們看一個《JavaScript權威指南》中的兩段代碼:

var scope = "global scope";
function checkscope1(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f(); // 注意
}
checkscope1();

var scope = "global scope";
function checkscope2(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope2()();

兩段代碼的結果都是"local scope",書中的回答是:JavaScript 函數的執行用到了作用域鏈,這個作用域鏈是在函數定義的時候創建的。嵌套的函數 f() 定義在這個作用域鏈裏,其中的變量 scope 一定是局部變量,不管何時何地執行函數 f(),這種綁定在執行 f() 時依然有效。

但是它們內部經歷的事情是一樣的嗎?

參考

寫在最後

JavaScript內功基礎部分已經總結到第三篇了,總結這個系列是受到了冴羽大大的鼓勵和啓發,本系列大約會有15篇文章,都是我們在面試最高頻的,但在工作中常常被忽略的。

JavaScript內功系列:

  1. this指向詳解,思維腦圖與代碼的結合,讓你一篇搞懂this、call、apply。系列(一)
  2. 從原型到原型鏈,修煉JavaScript內功這篇文章真的不能錯過!系列(二)
  3. 本文
  4. 下一篇預發:執行上下文

關於我

  • 花名:餘光
  • 一名工作不久的前端小白

其他沉澱

如果您看到了最後,不妨收藏、點贊、評論一下吧!!!
持續更新,您的三連就是我最大的動力,虛心接受大佬們的批評和指點,共勉!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章