Javascript 函數的認知

由事件驅動的或者當它被調用時執行的可重複使用的代碼塊javascript

函數聲明與函數表達式

  • 函數聲明是解釋器會首先讀取,並使其在執行任何代碼以前可用;
  • 函數表達式,則必須等到解釋器執行到它所在的代碼行,纔會被真正解析;
// function.js
console.log(add1(1, 2)); // 3
console.log(add2(1, 2)); // Uncaught ReferenceError: Cannot access 'add2' before initialization

//函數聲明
function add1(a, b){
    return a + b;
}

//函數表達式
const add2 = function add(a, b){
    return a + b;
}
複製代碼

arguments 對象

內置對象,用於獲取函數參數和參數長度等。java

// factorialArguments.js
const factorialArguments = function(num) {
    if(num <= 1){
        return 1;
    } else {
        return num * arguments.callee(num - 1);
    }
}
console.log('5的階乘', factorialArguments(5));

// factorial.js
const factorial = function f(num) {
    if(num <= 1) {
        return 1;
    } else {
        return num * f(num - 1);
    }
}
console.log('5的階乘', factorial(5));
複製代碼

call 和 apply

// callAndApply.js
const sum = function sum(a, b) {
    return a + b;
}

const sumByCall = function (a, b) {
    return sum.call(this, a, b);
}

const sumByApply = function (a, b) {
    return sum.apply(this, [a, b]);
}

const sumByApplyArguments = function (a, b) {
    return sum.apply(this, arguments);
}
console.log('sum 求和', sum(2, 7));
console.log('sumByCall 求和', sumByCall(2, 7));
console.log('sumByApply 求和', sumByApply(2, 7));
console.log('sumByApplyArguments 求和', sumByApplyArguments(2, 7));
複製代碼

區別: apply 傳遞參數是數組傳遞,call 是一個一個傳遞git

參數傳遞

  • 參數是值就按值傳遞;
  • 參數是對象,就按對象的引用地址傳遞;

按值傳遞

// transmitByValue.js
const transmitByValue = function (str) {
    str += ',我是 pr';
    return str;
}

const str = '你好';
console.log('str before', str); // str before 你好
console.log('str by transmitByValue', transmitByValue(str)); // str by transmitByValue 你好,我是 pr
console.log('str after', str); // str after 你好
複製代碼

按地址傳遞

// transmitByAddress.js
const transmitByAddress = function (str) {
    str.words += ',我是 pr';
    return str;
}

const str = {
    words: '你好'
};
console.log('str before', str.words); // str before 你好
console.log('str by transmitByAddress', transmitByAddress(str)); // str by transmitByAddress { words: '你好,我是 pr' }
console.log('str after', str.words); // str after 你好,我是 pr
複製代碼

閉包和匿名函數

  • 匿名函數,顧名思義就是沒有名字的函數
  • 閉包是一個函數,它能夠訪問一個函數做用域裏的變量。因爲閉包做用域返回的局部變量資源不會馬上被銷燬回收,因此就會佔用(吃)更多內存,性能就會降低,因此非必用可不用。
// closureHasAnonymousFunction.js
const closure = function () {
    let num = 10;
    return function () {
        return ++num;
    }
}

const closureAlias = closure();
console.log(closureAlias()); // 11
console.log(closureAlias()); // 12
console.log(closureAlias()); // 13
console.log('-----');
// closure()()外層函數每次都執行,即 num 每次都初始化
console.log(closure()()); // 11
console.log(closure()()); // 11
console.log(closure()()); // 11
複製代碼

解析github

  • 使用局部變量實現累加功能;
  • 定義函數 closure,返回一個匿名函數造成閉包;
  • 將 closure() 賦給 closureAlias,此時函數 closureAlias 會初始化 num,值爲匿名函數;
  • 執行 closureAlias;

閉包中的 this

this 對象是在運行時基於函數的執行環境而綁定的,若是全局那就是 window 或 global(Node.js),在對象內部就是這個對象。閉包運行時屬於 window,由於不屬於這個對象的屬性或方法。數組

// closureHasThis.js
var obj = {
    name: 'say',
    say () {
        return function () {
            return this;
        };
    }
}

console.log(obj.say()()); // 指向 Window
複製代碼

1.對象冒充方式解決閉包

// closureHasThis1.js
var obj = {
    name: 'say',
    say () {
        return function () {
            return this;
        };
    }
}

console.log(obj.say().call(obj));
複製代碼

2.賦值方式解決app

// closureHasThis2.js
var obj = {
    name: 'say',
    say () {
        var that = this;
        return function () {
            return that;
        };
    }
}

console.log(obj.say()());
複製代碼

閉包解決循環問題

這個例子在 ES6 的 let 關鍵字中介紹過。函數

循環中的每一個迭代器在運行時都會給本身捕獲一個 i 的副本,根據做用域的工做原理,循環中的 10 個函數雖然是在各個迭代器中分別定義的,但它們都會被封閉在全局做用域中,實際只有一個 i,結果每次都會輸出 10。oop

// loop.js
for (var i = 0; i <= 9; i++) {
    setTimeout(function () {
        console.log(i);
    });
}
複製代碼

1.使用閉包,在每一個循環迭代中加個閉包做用域post

// loopHasClosure.js
for (var i = 0; i <= 9; i++) {
    (function () {
        console.log(i);
    })(i);
}
複製代碼

2.使用 let

// loopHasLet.js
for (let i = 0; i <= 9; i++) {
    console.log(i);
}
複製代碼

本次代碼 Github

你能夠...

相關文章
相關標籤/搜索