前言
上次牛客網做到這樣一個題,非常有意思,陷阱非常多,當時覺得搞明白了,現在再看到,又糊涂了,發現了新的點,看一下:
var foo={n:1}; (function (foo) { console.log(foo.n); foo.n = 3; var foo = {n:2}; console.log(foo.n); })(foo); console.log(foo.n);
乍一看,是局部和全局變量的一些區分問題,其實坑很多,下面我們一點點分析下;
變量的定義(宣告)和賦值
首先我們看一段代碼:
var a = 1; var a; console.log(a);//1
這里第二行對a是一個重復宣告,而不是賦值,變量只有定義(宣告)后未賦值的情況下才會輸出undefined,除非手動賦值undefined;那么這里,JS引擎對于重復宣告的規定以最近的變量指定(也就是賦值)作為變量在執行時的值,所以第二行的var a;其實相當于無效;
函數中形參和局部變量同名
在我們自己寫代碼時,一般不會做這種蠢事情,把形參和局部變量定義為同名,可如果真的這樣做了呢?
那就要分析下JS執行上下文中的變量對象了,這個知識點不牢固的同學可以移步這里:重拾ECMAScript基礎——變量、作用域;
作用域鏈對變量的保存都是在變量對象中的,那么ES5對形參在變量對象中是如何保存的呢,請看規范:
10.5 Declaration Binding Instantiation
Every execution context has an associated VariableEnvironment. Variables and functions declared in ECMAScript code evaluated in an execution context are added as bindings in that VariableEnvironment‘s Environment Record. For function code, parameters are also added as bindings to that Environment Record.
就是說,無論是形參還是函數中聲明的變量,JS對他們的處理是沒有區別的,都是保存在這個函數的變量對象中作為局部變量進行處理;那么結合上面我們說到的變量的重復宣告,接下來同名的問題就很簡單了,看代碼:
(function fun (param) { var param; console.log(param);//1 param = 2; console.log(param);//2 })(1);
在這里,同名的局部變量和形參其實是同一個東西,都是在函數的變量對象里的保存的那個變量;
如果變量是引用類型呢?
那么如果變量是個對象的話,就是我們文章一開始提到的題目了,下面我們分析下:
var foo = {n : 1}; (function(foo) { console.log(foo.n); foo.n = 3; var foo = {n : 2}; console.log(foo.n); })(foo); console.log(foo.n);
var foo = {n : 1}; function fun(foo) { var foo; console.log(foo.n); foo.n = 3; foo = {n : 2}; console.log(foo.n); }; fun(foo); console.log(foo.n);
上下兩段代碼,意思是一樣的,我把匿名立即執行函數換成了普通函數并在下一行調用,方便大家理解;
其實分析一下,就是這么幾個問題;
內部foo變量提升;
內部foo和形參同名;
內部foo重復宣告;
所以內部var的那個foo和形參foo是同一個東西,并沒有發生變化;
然后重復宣告不影響之前的賦值,所以第一個為1;
接下來,foo.n=3,由于形參為對象,所以是傳進來的是一個對象的引用(指針);
對這塊知識點不牢固的同學還是請移步我之前那篇文章;
那么這個引用指向的堆內存的那塊空間里的n改變為3;
接下來,foo={n:2};這個就很有意思了,我們剛才也說了,傳進來的是個引用;
現在給這個引用賦值,實際上就是讓它指向新開辟的空間,存放著{n:2}這個對象;
那么之前的引用就斷掉了,也就是說形參foo已經不指向全局里那個foo指向的空間了;
固然,在函數里,會輸出新空間里的2;
而在函數外,舊空間仍然沒有改變,故為3;
總結
其實這個題目考了很多知識點,最后就是參數傳遞中引用類型的用法,這個也是ECMAScript中基礎的一個難點,結合前面的一些,變量提升,重復宣告,形參與局部變量同名,算是解釋清楚了。
若有錯誤,歡迎指出。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108588.html
摘要:延長作用域鏈下面兩種語句可以在作用域鏈的前端臨時增加一個變量對象以延長作用域鏈, 問題 今天看筆記發現自己之前記了一個關于同名標識符優先級的內容,具體是下面這樣的: 形參優先級高于當前函數名,低于內部函數名 形參優先級高于arguments 形參優先級高于只聲明卻未賦值的局部變量,但是低于聲明且賦值的局部變量 函數和變量都會聲明提升,函數名和變量名同名時,函數名的優先級要高。執行代...
摘要:函數使用它們實參的值來計算返回值,稱為該函數調用表達式的值。通常函數名的第一個字符為小寫。在函數內部定義的變量,外部無法讀取,稱為局部變量。注意對于命令來說,局部變量只能在函數內部聲明,在其他區塊中聲明,一律都是全局變量。 函數 1.函數簡介 通過函數可以封裝任意多條語句,而且可以在任何地方、任何時候調用。 ECMAScript中的函數使用function關鍵字來聲明,后跟一組參數以及...
摘要:函數函數的概述所謂函數就是只被定義一次但可能被執行或調用多次變量與函數的區別變量存的是數據內容而函數存的是語句塊定義函數備注定義函數時函數體的內容不會被執行調用函數時函數體才被執行注意一般以個函數就去完成一個功能函數聲明方式語法函數名稱 函數 函數的概述 所謂函數就是只被定義一次,但可能被執行或調用多次 變量與函數的區別: 變量存的是數據內容而函數存的是語句塊 定義函數 備注: ...
摘要:因為即使包含了其他的代碼,也只會下載并執行屬性內的外部腳本文件,嵌入的額外代碼會被忽略。在腳本中,如果程序員在對某個變量賦值之前未聲明,賦值操作將自動聲明該變量。共有中數據類型。阻止事件冒泡火狐瀏覽器創建元素添加子元素刪除子元素數組拼接方法 所有的web開發都是【請求】+【響應】 推薦JavaScript中使用單引號引用字符串,HTML中使用雙引號,防止沖突 JavaScript代碼...
摘要:函數函數是什么函數是這樣的一段代碼它只定義一次但可能被執行或調用多次簡單來說函數就是一組可重用的代碼可以在程序的任何地方調用定義函數函數聲明方式定義函數時函數體的內容是不會被執行的這是一個函數字面量直接量方式這是一個函數調用函數定義一個函數 函數 函數是什么 函數是這樣的一段JavaScript代碼 它只定義一次 但可能被執行或調用多次簡單來說 函數就是一組可重用的代碼 可以在程序的任...
閱讀 2571·2021-09-26 10:13
閱讀 5968·2021-09-08 10:46
閱讀 685·2019-08-30 15:53
閱讀 2956·2019-08-29 16:13
閱讀 2750·2019-08-26 12:23
閱讀 3478·2019-08-26 11:24
閱讀 1084·2019-08-23 18:09
閱讀 1028·2019-08-23 17:08