溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

了解Javascript中變量和詞法環(huán)境

發(fā)布時(shí)間:2020-07-08 10:17:14 來(lái)源:億速云 閱讀:304 作者:Leah 欄目:web開(kāi)發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)了解Javascript中變量和詞法環(huán)境,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

其實(shí),我覺(jué)得Javascript核心中重要的東西并非是從舊版本擴(kuò)展來(lái)的高大上的語(yǔ)法,例如解構(gòu)賦值啊、展開(kāi)語(yǔ)法和剩余參數(shù)(嘛……雖然的確是很666),但是用好這些,其實(shí)都建立在你對(duì)變量的認(rèn)識(shí)上(常有人不知道什么是左值或右值的區(qū)別)正因如此,我覺(jué)得了解一個(gè)Javascript還是從最基本做起,就是了解一下何為變量吧。

本文其實(shí)也并非完全基礎(chǔ),還是建立在對(duì)Javascript有一定了解之上的,至少對(duì)對(duì)象要有一定的認(rèn)知。開(kāi)始吧。

變量與數(shù)據(jù)

什么是變量?

越簡(jiǎn)單的問(wèn)題答案往往越是讓人感到意外,多數(shù)人的答案都與有關(guān);事實(shí)上變量就是程序可操作的存儲(chǔ)區(qū)(術(shù)語(yǔ)內(nèi)存空間),在Javascript程序運(yùn)行時(shí),存儲(chǔ)區(qū)(術(shù)語(yǔ)就是內(nèi)存空間)可以保存我們所需的任何東西,代碼、數(shù)據(jù)……等等。然后可以將變量保存的數(shù)據(jù)大致上分割為兩類(lèi):原始類(lèi)型(同基本類(lèi)型)和引用類(lèi)型;從變量中取出來(lái)的數(shù)據(jù)就是值,把一個(gè)值放到變量中時(shí),該值就又變成了數(shù)據(jù)。

Javascript和其他語(yǔ)言相似,變量也需要被聲明才能實(shí)際存在,聲明后的變量稱之為實(shí)例化,為變量賦予一個(gè)值(默認(rèn)為undefined)時(shí),稱之為變量的初始化,只是實(shí)例化而未初始化的變量都處于uninitialized狀態(tài)。

例如:

let a = a ;         // (*)
console.log(a);
// ReferenceError: can't access lexical declaration `a' before initialization

很完美的報(bào)錯(cuò)了(在(*)標(biāo)志的位置),提示我們沒(méi)有初始化這個(gè)變量時(shí)就無(wú)法使用。這是不同于C++這類(lèi)底層的變量的地方。其實(shí)這種現(xiàn)象在Javascript有一個(gè)極為高大上的名字:暫存死區(qū),等過(guò)幾章節(jié)我就說(shuō)明產(chǎn)生的原因。

(忘了說(shuō)了,在變量聲明也需要一個(gè)名字,術(shù)語(yǔ)稱作標(biāo)識(shí)符,我覺(jué)得不補(bǔ)充也不影響什么……)

不過(guò)Javascript另外特殊的地方在于,它對(duì)var聲明的變量是可以自動(dòng)初始化的,Javascript會(huì)自動(dòng)會(huì)為var聲明的變量賦予一個(gè)undefined值。
例如:

var a = a;console.log(a);    // undefined.

看吧,明明都差不多,結(jié)果卻全然不同。
但是實(shí)際上卻沒(méi)什么卵用,見(jiàn)下面的代碼:

var a = a;console.log(a+2);   // NaN

結(jié)果是NaN, 得到了一個(gè)我們完全不想要的結(jié)果。在如果無(wú)法順利數(shù)學(xué)計(jì)算時(shí),Javascript便會(huì)給出一個(gè)非數(shù)字的結(jié)果,用NaN表示。但是比較有趣的是,如果你用typeof去驗(yàn)證NaN類(lèi)型:

typeof NaN ;      // number

卻告訴我們,這TMD是一個(gè)數(shù)值 number。
Javascript莫名其妙的地方還有許多許多,不過(guò)我們還是不要繼續(xù)調(diào)戲javascript了,開(kāi)始認(rèn)真學(xué)習(xí)了。

類(lèi)型與存儲(chǔ)

Javascript一共有 7 種原始類(lèi)型 和 1 種 引用類(lèi)型,如下:

  • 原始類(lèi)型

    1、number

    2、string

    3、boolean

    4、symbol

    5、bigint

    6、undefined

    7、null

  • 引用類(lèi)型:

    object

(這里面我就用小寫(xiě)了,因?yàn)?code>typeof返回的是小寫(xiě)的)
我就是介紹一下這些必須要了解的東西,具體用法其他資料都有我就不贅述了。不過(guò)關(guān)于typeof還有要補(bǔ)充的一點(diǎn)是,它對(duì)于nullfunction結(jié)果:

function sayHello(){
     console.log('hello the world');
 }
 console.log(typeof sayHello);  // function
 console.log(typeof null);      // object

……對(duì)于一個(gè)函數(shù),它真的返回的是一個(gè)“函數(shù)”,某種意義上用處很大,不過(guò)對(duì)null值返回一個(gè)object,這只能說(shuō)有得就有缺吧。

我覺(jué)得對(duì)變量加深了解的辦法就是明白它的底層運(yùn)作方式。其實(shí)也沒(méi)有什么了不起的,原始值是直接放在內(nèi)存棧區(qū), 引用類(lèi)型值則是放在內(nèi)存堆區(qū)(這是它的實(shí)際存儲(chǔ)區(qū)位置);(如果是常量,那么就會(huì)放在中,好像也是棧區(qū)的一部分)。正常情況下,變量取值都是直接都是從內(nèi)存棧區(qū)中獲取的,但是引用類(lèi)型的值是放在內(nèi)存堆中,那么怎么辦?

引用類(lèi)型值的訪問(wèn):

1、一個(gè)引用類(lèi)型的變量,會(huì)在內(nèi)存棧中保存一個(gè)指針

2、這個(gè)指針是用于引用內(nèi)存堆中的存儲(chǔ)區(qū)的內(nèi)存地址

3、在訪問(wèn)一個(gè)類(lèi)型值時(shí)

4、會(huì)通過(guò)指針找到內(nèi)存堆中的存儲(chǔ)區(qū),然后從中獲取值。

例如:

  var first  = {
      name:'hahei...'
  }
  var gggiii=111222;

映射圖如下:

了解Javascript中變量和詞法環(huán)境

注意:此處我用 ref. first表示  存儲(chǔ)區(qū)的引用 , 因?yàn)殡m然保存的盡管是指針,但是在訪問(wèn)這個(gè)值時(shí),會(huì)進(jìn)行二次解析(即通過(guò)這個(gè)指針找到存儲(chǔ)區(qū)), 而不是直接返回這個(gè)指針的具體數(shù)據(jù)。詳細(xì)可以參考 C++引用。

初識(shí)詞法環(huán)境

想必各位都已經(jīng)對(duì)什么是作用域了若指掌,但是我還是必須重新提一下。作用域標(biāo)識(shí)符的可訪問(wèn)范圍,在Javascript中的任何操作,幾乎都有作用域的參與。Javascript中使用詞法環(huán)境決定作用域,在下面我會(huì)簡(jiǎn)單介紹一下。(請(qǐng)注意,這里我沒(méi)有用變量這個(gè)術(shù)語(yǔ),因?yàn)榻馕鰳?biāo)識(shí)符范圍時(shí),應(yīng)該還沒(méi)有真正生成代碼,感興趣的可以去了解一下AST語(yǔ)法樹(shù)

看,以下代碼:

 var val=111;
 function hahaha(){
     console.log(val);
 }
 function hihihi(){
    hahaha();
 }
 hihihi();  /// 111

的確是正確輸出了,111

但是我更喜歡把 val放在一個(gè)函數(shù)中,如:

   function hahaha(){
       console.log(val);      /// (**)
   }
   function hihihi(){
      var val=111;            /// (*)
      hahaha();
   }
   hihihi();

結(jié)果就是Uncaught ReferenceError: val is not defined, 根本沒(méi)找到val這個(gè)標(biāo)識(shí)符,這是為什么?

因?yàn)閳?zhí)行過(guò)程是這樣的:

  1. hihihi函數(shù)執(zhí)行  , 然后為 val賦值……
  2. hahaha函數(shù)執(zhí)行
  3. hahaha找不到val標(biāo)識(shí)符,便去外部詞法環(huán)境
  4. hahaha外部詞法環(huán)境就是** hahaha函數(shù)聲明時(shí)代碼的外部**,即全局代碼(下稱全局詞法環(huán)境)
  5. 全局詞法環(huán)境沒(méi)找到val,終了。
    (請(qǐng)注意3-5步, 找val找的是函數(shù)聲明代碼的外部,而不是函數(shù)調(diào)用時(shí)的位置。)

現(xiàn)在應(yīng)該提一下概念了,詞法環(huán)境(Lexical Environment)就是根據(jù)代碼結(jié)構(gòu)時(shí)決定的作用域,也可以稱作詞法作用域(Lexical Scoping)它是靜態(tài)作用域??梢赃@么說(shuō),在源代碼寫(xiě)好時(shí),所有標(biāo)識(shí)符的作用域就已經(jīng)被決定。當(dāng)然也有動(dòng)態(tài)作用域,你可以去試試bash腳本,它就是動(dòng)態(tài)的。嘿嘿。詳細(xì)也可以參考靜態(tài)作用域、詞法作用域

此處只要發(fā)現(xiàn)了個(gè)中區(qū)別就極好掌握,所以我就略了。

詞法環(huán)境的抽象

在Javascript常用三種詞法環(huán)境: 一、塊級(jí)作用域 二、全局作用域 三、函數(shù)作用域。

有時(shí),我們會(huì)將一個(gè)詞法環(huán)境(即作用域,下面我會(huì)正式使用詞法環(huán)境替代作用域這個(gè)術(shù)語(yǔ))抽象成偽代碼,如下:

	LexicalEnvironment = {
		OuterEnv: < ... > ,
		This :   < ... > ,
		EnvironmentRecord:{
			// ... identifiername:variable
		}
	}

很簡(jiǎn)單:

  • OuterEnv:當(dāng)前詞法環(huán)境的外部詞法環(huán)境
  • This: 當(dāng)前詞法環(huán)境的 this的值,但它是運(yùn)行時(shí)決定的。
  • EnvironmentRecord(環(huán)境記錄): 標(biāo)識(shí)符-變量的映射,注意,這里的標(biāo)識(shí)符只是單純的字符串,變量指的是存儲(chǔ)區(qū)的數(shù)據(jù)。而且標(biāo)識(shí)符必須是當(dāng)前詞法環(huán)境,而不是當(dāng)前代碼的。

例如:

  function first(){
      var a  =100;
      let d = 220;
      {     // Block, 
          var b = a+100;
          let c = b*10;
          console.log(a,b,c,d);
      }
  }
  first();  // 100 200 2000 220

一定不要忽略first函數(shù)中的塊級(jí)作用域,這很重要。

然后寫(xiě)成抽象就是:
函數(shù)內(nèi)部的塊級(jí)作用域

	BlockEnv = {
		OuterEnv: < FuncFirstEnv > ,
		This :   < window > ,
		EnvironmentRecord:{
			c:< 2000 >              // 這里沒(méi)有b
		}
	}

函數(shù)作用域

	FuncEnv = {
		OuterEnv: < GlobalEnv > ,
		This :   < window > ,
		EnvRec:{
			a:< 100 >,
			d:< 220 >,
			b:< 200 >
		}
	}

OKay,先到這里吧。

一些問(wèn)題:

1、為什么用詞法環(huán)境代替作用域?
–詞法環(huán)境涵蓋了作用域,但反之則不能。
–但注意,詞法作用域和詞法作用域鏈與作用域以及作用域鏈都可通用。

2、環(huán)境記錄是什么?
–當(dāng)前環(huán)境下的標(biāo)識(shí)符-變量的映射
–但是標(biāo)識(shí)符只是“合法標(biāo)識(shí)符”的字符串形式。
–變量是是指存儲(chǔ)區(qū)的內(nèi)容,但是確切說(shuō)法是存儲(chǔ)區(qū)。

最后

我把我的筆記,重新整理后發(fā)到博客上后發(fā)現(xiàn)——我筆記干凈了好多,艸。

這種只深入核心的內(nèi)容很有用,而且在寫(xiě)代碼時(shí)也變得靈活很多了。我覺(jué)得這就是最有用的地方。
最后:
個(gè)人理解,常有失誤;細(xì)細(xì)查看不知何處,望君做到心中有數(shù)。

關(guān)于了解Javascript中變量和詞法環(huán)境就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI