【JS基礎】作用域和閉包

ican發表於2019-02-16

變數提升的理解

js語言的執行規則:先定義,後執行
變數定義、函式宣告就屬於定義程式碼的範疇(注意“函式宣告”和“函式表示式”的區別)

說明this幾種不同的使用場景

this 要在執行時才能確認值,定義時無法確認

var a = {
    name: `A`,
    fn: function () {
        console.log(this.name);
    }
};
a.fn(); // this === a
a.fn.call({name: `B`}); // this === {name: `B`}
var fn1 = a.fn;
fn1(); // this === window

全域性環境下的this

function fn() {
    console.log(this); // this === window
}
fn();

物件方法中的this

var obj = {
    name: `A`,
    printName: function () {
        console.log(this.name); // this === obj
    }
};
obj.printName();

建構函式中的this

function Foo(name) {
    this.name = name; // this === f
}
var f = new Foo(`can`);

事件函式中的this

document.getElementById(`div1`).onclick = function () {
    console.log(this); // this === <div id="div1">div1</div> 觸發當前事件的Element物件
};

call apply bind 中的this

function fn1(name, age) {
    console.log(name, age);
    console.log(this); // this === {x:100}
}

fn1.call({x: 100}, `can`, 22);
fn1.apply({x: 100}, [`can`, 22]);

var fn2 = function (name, age) {
    console.log(name, age);
    console.log(this); // this === {y:200}
}.bind({y: 200});
fn2(`can`, 22);

建立10個a標籤,點選的時候彈出來對應的序號

for (var i = 0; i < 10; i++) {
    (function (i) {
        // 函式作用域
        var a = document.createElement(`a`);
        a.innerHTML = i + `<br>`;
        a.addEventListener(`click`, function (e) {
            e.preventDefault();
            alert(i); // i 是自由變數,去父作用域尋找
        });
        document.body.appendChild(a);
    })(i);
}

作用域

ES5中,無塊級作用域

if (true) {
    var name = `can`;
}
console.log(name); // `can`

函式和全域性作用域

var b = 1;
function fn3() {
    var b = 2;
    console.log(`fn`, b); // 2
}
console.log(`global`, b); // 1
fn3();

自由變數

var a = 100;
function fn4() {
    var b = 200;
    // 當前作用域沒有定義的變數,即“自由變數”
    console.log(a); // 100  a 是自由變數,去父作用域尋找
    console.log(b); // 200
}
fn4();

一個函式的父級作用域,是它定義時的父級作用域,而不是執行時

作用域鏈,即自由變數的查詢

var a = 100;
function fn5() {
    var b = 200;
    function fn6() {
        var c = 300;
        console.log(a); // 100  a 是自由變數,去父作用域尋找
        console.log(b); // 200  b 是自由變數,去父作用域尋找
        console.log(c); // 300
    }
    fn6();
}
fn5();

閉包的使用場景

1.函式作為返回值

function fn1() {
    var a = 100;
    // 返回一個函式(函式作為返回值)
    return function () {
        console.log(a); // a是自由變數,去父作用域尋找
    }
}

// f1 得到一個函式
var f1 = fn1();
var a = 200;
f1(); // 100

2.函式作為引數傳遞

function fn1() {
    var a = 100;
    // 返回一個函式(函式作為返回值)
    return function () {
        console.log(a); // a是自由變數,去父作用域尋找
    }
}

// f1 得到一個函式
var f1 = fn1();

function fn2(fn) {
    var a = 200;
    fn();
}

fn2(f1); // 100

實際開發中閉包的應用

閉包實際應用中主要用於封裝變數,收斂許可權

function isFirstLoad() {
    var list = [];
    return function (id) {
        if (list.indexOf(id) > -1) {
            return false;
        } else {
            list.push(id);
            return true;
        }
    }
}
// 在 isFirstLoad 函式外面,不可能修改到 list 的值
var firstLoad = isFirstLoad();
firstLoad(10); // true
firstLoad(10); // false
firstLoad(20); // true

相關文章