博客 / 詳情

返回

詳解函數作用域

一、變量提升

1、變量

var 表示是變量 存在提前申明 同時也會和window存在映射機制(只存在於全局變量和window之間)

console.log(a); // undefined  沒報錯,變量提升了
console.log(window.a, 'a' in window) // undefined true
var a = 5;
console.log(a); // 5
console.log(window.a, 'a' in window) // 5 true

沒有var就不是變量,也就不存在變量提升,相當於 window.b=9

console.log(b)  // Uncaught ReferenceError: b is not defined
console.log(window.b, 'b' in window) // undefined false
b = 9;
console.log(b) // 9
console.log(window.b, 'b' in window)  // 9 true

var c = d = 10; 相當於

var c=10;
d=10;

2、函數

function和var變量得區別是:function不僅提前聲明,還提前定義(賦值)

另外,function由於是引用類型,定義的時候其實是賦值了一個引用地址,這個引用地址指向某個堆內存,這個堆內存裏存着這個函數字符串

var f1 = () => {
    console.log(2)
}
function f1() {
    console.log(1)
}
f1(); // 2  因為function f1提前聲明定義了

函數內部變量提升, 私有變量不帶var, 就向上級索取

console.log(a, b);  // undefined undefined
var a = 12, b = 12;
function fn() {
    console.log(a, b); // undefined 12  b不帶var 向上級索取
    
    // 這裏b賦值,其實是window.b=13, 由於全局作用域變量b和         window.b存在映射關係,外部b變量也成了13
    var a = b = 13; 
    console.log(a, b) // 13 13
}
fn();
console.log(a, b); // 12 13

條件語句中的變量提升

  • 條件語句中的var變量仍然會變量提升
  • 條件語句中的函數也會變量提升,但只是聲明,不會定義
  • 如果條件成立,條件中第一步就是先給之前預檢查時只聲明沒定義的函數定義
console.log(c); // undefined
console.log(f1); // undefined  變量提升了 但只是聲明,不定義
if (1) {
    console.log(f1); // funciton f1(){}  如果條件成立,在塊級作用域中,先把變量提升的函數賦值(因為之前只是聲明瞭,沒有賦值)
    var c = 2;
    function f1() {
        console.log('f1');
    }
}
console.log(f1); // funciton f1(){}
console.log(c);  // 2

當然要是條件不成立,那if中也不會賦值

console.log(c); // undefined
console.log(f1); // undefined  變量提升了 但只是聲明,不定義
if (0) {
    console.log(f1);
    var c = 2;
    function f1() {
        console.log('f1');
    }
}
console.log(f1); // undefined
console.log(c);  // undefined

下一題

f = function () { return true }
g = function () { return false }
~function () {
    if (g() && [] == ![]) {  // 行4 這裏會報錯 [] == ![] true
        f = function () { return false }
        function g() {
            return true
        }
    }
}();
console.log('f()', f())
console.log('g()', g())

解析:在第四行就報錯了,執行不下去了,為什麼,因為在自執行函數中,if之前,function g提前聲明瞭,但沒有賦值;結果if判斷條件g() && [] == ![]去調了,但此時g還是undefined呢

變換一下,if判斷條件中g()換為f()

f = function () { return true }
g = function () { return false }
~function () {
    if (f() && [] == ![]) {  // 行4 [] == ![] true
        f = function () { return false }
        function g() {
            return true
        }
    }
}();
console.log('f()', f()) // false
console.log('g()', g()) // false

解析:由於自執行函數中沒有f函數和f變量,不存在提前申明,所以if判斷中f()調用全局作用域的f,所以為true; 然後執行if語句中代碼,全局作用域中的f被重新賦值(return false),而g函數只是在自執行函數中生效,不會影響全局作用域中的g

二、重命名

1、var和function名稱重複會相互覆蓋

ff() // ff
var ff =12;
ff()  // 報錯 因為此時ff是12,不是函數
function ff(){
    console.log('ff')
}
ff() // 報錯 因為此時ff是12,不是函數

2、js很懶,如果已經聲明且賦值,再聲明賦值,只會重新賦值

fn();  // 4
function fn() { console.log(1) }
fn(); // 4
function fn() { console.log(2) }
fn(); // 4
var fn = 2;
fn(); // not function error
function fn() { console.log(3) }
fn(); // not function error
function fn() { console.log(4) }
fn(); // not function error

三、let const

1、let const 不存在變量提升 且和window切斷映射

console.log(l); // error
let l = 5;
console.log(window.l) // undefined
console.log('l' in window) // false

2、瀏覽器執行之前會檢測使用let得變量, 會提前報錯

let a=12;
a=12;
console.log(a) // 報錯 從執行上這一行還沒出錯,但是提前檢測了
let a=13; // 報錯,已經聲明瞭
var a=13; // 報錯

其實第一行就報錯了

var b=12; // 報錯
let b=13; 

3、暫時性死區

let a = 10, b = 10;
let fn = function () {
    console.log(a) // 報錯 因為函數作用域中定義了let a
    console.log(b) // 10
    let a = b = 20
    console.log(a) // 20
    console.log(b) // 20
}
fn()
console.log(a, b) // 10 20
var a = 12;
if (true) {  // let會形成{}塊級作用域
    console.log(a);  // 報錯
    let a = 13;
    console.log(a) // 13
}
console.log(a)  // 12

四、作用域

1、函數得上級作用域是誰,和執行時的位置沒有關係,和該函數定義的位置有關係

var a = 12;
function fn2() {
    console.log(a)  // 12
}
function sum() {
    var a = 20;
    fn2();
    console.log(a); //20
}
sum();

2、閉包

var n = 10;
function fn3() {
    var n = 20;
    function fn4() {
        n++;
        console.log(n)
    }
    fn4();
    return fn4;
}
var x = fn3(); // 21
x(); //22
x(); //23
console.log(n); //10

`
堆內存:存儲引用數據類型值(對象:鍵值對,函數:代碼字符串)

  • 1) 堆內存釋放 讓引用堆內存地址的變量賦值為null
  • 棧內存:提供js代碼執行環境,存儲基本類型值
  • 1) 棧內存釋放:當函數執行完成, 所形成的私有作用域自動釋放,
  • 但如果棧內存某內容被棧內存以外的變量引用了,就不能釋放
  • 2) 全局棧內存只有頁面關閉才能釋放

`

五、鄙視題

私有作用域首先是形參賦值,然後是變量提升,再然後才是代碼執行

var a = 12, b = 13, c = 14;
function fn(a) {
    // 私有作用域 首先形參賦值 a=12
    // 然後變量提升 var b;
    // var function 形參 都是私有變量
    console.log(a, b, c) // 12 undefined 14
    var b = c = a = 20;
    console.log(a, b, c) // 20 20 20
}
fn(a); // 傳入實參12
console.log(a, b, c) // 12 13 20

衍生一下

var arr = [12, 23];
function fn1(arr) {
    console.log(arr); // [12, 23]
    // 這樣賦值,改的不僅僅是實參,而且把全局中的arr[0]也改了,全局arr成了[100.23]
    arr[0] = 100; 
    arr = [100];
    arr[0] = 0;
    console.log(arr); // [0]
}
fn1(arr);
console.log(arr); // [100,23]
user avatar zzd41 頭像 tigerandflower 頭像 dujing_5b7edb9db0b1c 頭像 iymxpc3k 頭像 huanjinliu 頭像 qianduanlangzi_5881b7a7d77f0 頭像 light_5cfbb652e97ce 頭像 pangsir8983 頭像 dashnowords 頭像 yiiouo 頭像 william_wang_5f4c69a02c77b 頭像 tufeiyuan_5d9f0a380c30e 頭像
15 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.