Js-w3school(2020.2.9)【js函數(展開部分)】

三十二、js函數(展開部分)

(一)函數定義
1.JavaScript 函數是通過 function 關鍵詞定義的。您可以使用函數聲明或函數表達式。

function functionName(parameters) {
   要執行的代碼
}

被聲明的函數不會直接執行。它們被“保存供稍後使用”,將在稍後執行,當它們被調用時。
由於函數聲明不是可執行的語句,以分號結尾並不常見。

JavaScript 函數也可以使用表達式來定義。
函數表達式可以在變量中存儲:

var x = function (a, b) {return a * b};

2.Function() 構造器
函數也可以通過名爲 Function() 的內建 JavaScript 函數構造器來定義。

var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);

也可以不用

var myFunction = function (a, b) {return a * b};
var x = myFunction(4, 3);

3.函數提升
Hoisting 應用於變量聲明和函數聲明。
正因如此,JavaScript 函數能夠在聲明之前被調用

myFunction(5);
 function myFunction(y) {
     return y * y;
}

但是使用表達式定義的函數不會被提升。
3. 自調用函數
函數表達式可以作爲“自調用”。
自調用表達式是自動被調用(開始)的,在不進行調用的情況下。
函數表達式會自動執行,假如表達式後面跟着 ()。

(function () {
    var x = "Hello!!";      //我會調用我自己
})();

4.函數可用作值
JavaScript 函數可被用作值,JavaScript 函數可用在表達式中

function myFunction(a, b) {
    return a * b;
}
var x = myFunction(4, 3) * 2;

5.函數是對象
JavaScript 中的 typeof 運算符會爲函數返回 “function”。
JavaScript 函數都有屬性和方法。
(1)arguments.length 會返回函數被調用時收到的參數數目:

function myFunction(a, b) {
    return arguments.length;
}

(2)toString() 方法以字符串返回函數:

function myFunction(a, b) {
    return a * b;
}
var txt = myFunction.toString();
//function myFunction(a, b) { return a * b; }

6.爲創建新對象而設計的函數,被稱爲對象構造函數(對象構造器)
7. 箭頭函數(es6)
箭頭函數允許使用簡短的語法來編寫函數表達式。
您不需要 function 關鍵字、return 關鍵字和花括號。
形式:(參數)=>返回值

// ES5
var x = function(x, y) {
  return x * y;
}
// ES6
const x = (x, y) => x * y;

箭頭函數沒有自己的 this。它們不適合定義對象方法。
箭頭函數未被提升。它們必須在使用前進行定義。
使用 const 比使用 var 更安全,因爲函數表達式始終是常量值。
如果函數是單個語句,則只能省略 return 關鍵字和大括號。

const x = (x, y) => {
	var a=2;
	return (x**a)+(y**a);
};
document.getElementById("demo").innerHTML = x(1, 5);//26

IE11 或更早的版本不支持箭頭函數。
(二)函數參數
1.函數參數定義

functionName(parameter1, parameter2, parameter3) {
    要執行的代碼
}

函數參數(parameter)指的是在函數定義中列出的名稱。
函數參數(argument)指的是傳遞到函數或由函數接收到的真實值。
2. 參數規則:無類型、無數量
JavaScript 函數定義不會爲參數(parameter)規定數據類型。
JavaScript 函數不會對所傳遞的參數(argument)實行類型檢查。
JavaScript 函數不會檢查所接收參數(argument)的數量。
3. 如果調用參數時省略了參數(少於被聲明的數量),則丟失的值被設置爲:undefined。
4. 如果函數調用的參數太多(超過聲明),則可以使用 arguments 對象(數組)來達到這些參數。

x = sumAll(1, 123, 500, 115, 44, 88);
function sumAll() {//求合函數
    var i, sum = 0;
    for (i = 0; i < arguments.length; i++) {
        sum += arguments[i];
    }
    return sum;
}

5.參數傳值,對象傳址
JavaScript 參數通過值傳遞:函數只知道值,而不是參數的位置。
如果函數改變了參數的值,它不會改變參數的原始值。
在 JavaScript 中,對象引用是值。
正因如此,對象的行爲就像它們通過引用來傳遞:
如果函數改變了對象屬性,它也改變了原始值。
(三)函數調用
1.在函數被定義時,函數內部的代碼不會執行。在函數被調用時,函數內部的代碼會被執行。
2.函數形式調用

function myFunction(a, b) {
    return a * b;
}
myFunction(10, 2);           // 將返回 20

2.作爲方法調用

var myObject = {
    firstName:"Bill",
    lastName: "Gates",
    fullName: function () {
        return this.firstName + " " + this.lastName;
    }
}
myObject.fullName();         // 將返回 "Bill Gates"

3.函數構造器調用
如果函數調用的前面是 new 關鍵字,那麼這是一個構造函數調用。

// 這是函數構造器:
function myFunction(arg1, arg2) {
    this.firstName = arg1;
    this.lastName  = arg2;
}
// 創建了一個新對象:
var x = new myFunction("Bill", "Gates");
x.firstName;                             // 會返回 "Bill"

(四)函數Call、Apply方法重用
1.call() 方法,您可以編寫能夠在不同對象上使用的方法。
call() 方法是預定義的 JavaScript 方法。它可以用來調用所有者對象作爲參數的方法。通過 call(),您能夠使用屬於另一個對象的方法。

var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
person.fullName.call(person1, "Seattle", "USA");
//理解爲person1.fullName("Seattle", "USA")

2.通過 apply() 方法,您能夠編寫用於不同對象的方法。apply() 方法與 call() 方法非常相似,但是接受參數的方式不同
person.fullName.apply(person1, [“Oslo”, “Norway”]);
call()分別接收參數,apply()以數組形式接收參數
3.由於 JavaScript 數組沒有 max() 方法,因此您可以應用 Math.max() 方法。

Math.max.apply(null, [1,2,3]); // 也會返回 3
Math.max.apply(Math, [1,2,3]); // 也會返回 3
Math.max.apply(" ", [1,2,3]); // 也會返回 3
Math.max.apply(0, [1,2,3]); // 也會返回 3

4.apply實現數組合並

Array.prototype.push.apply(arr1,arr2);

5.在 JavaScript 嚴格模式下,如果 apply() 方法的第一個參數不是對象,則它將成爲被調用函數的所有者(對象)。非嚴格模式下,第一個參數值被指定爲 null 或 undefined 時this值會自動替換爲指向全局對象
(五)函數閉包
1.JavaScript 變量屬於本地或全局作用域。全局變量能夠通過閉包實現局部(私有)。
2. JavaScript 嵌套函數
所有函數都有權訪問全局作用域。
事實上,在 JavaScript 中,所有函數都有權訪問它們“上面”的作用域。
JavaScript 支持嵌套函數。嵌套函數可以訪問其上的作用域。

function add() {
    var counter = 0;
    function plus() {counter += 1;}
    plus();     
    return counter; 
}

3.閉包

·閉包其實是在函數內部定義一個函數。
·閉包在使用的時候不會釋放外部的引用,閉包函數內部的值會得到保留。
·閉包裏面的匿名函數,讀取變量的順序,先讀取本地變量,再讀取父函數的局部變量。
·對於閉包外部無法引用它內部的變量,因此在函數內部創建的變量執行完後會立刻釋放資源,不污染全局對象。
·閉包使用的時候要考慮到內存泄漏,因爲不釋放外部引用,但是合理的使用閉包是內存使用不是內存泄漏。

(1)自調用函數

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
add();
add();
add();
// 計數器目前是 3 

該函數在自定義時候首先執行一次並初始化counter爲0。這個自調用函數只運行一次。它設置計數器爲零(0),並返回函數表達式。
add 成爲了函數,它能夠訪問父作用域中的計數器。
計數器被這個匿名函數的作用域保護,並且只能使用 add 函數來修改。
這被稱爲 JavaScript 閉包。它使函數擁有“私有”變量成爲可能。
閉包指的是有權訪問父作用域的函數,即使在父函數關閉之後。
(2)for循環案例

var arrays = [];

for (var i=0; i<3; i++) {
    arrays.push(function() {
        console.log('>>> ' + i); //all are 3
    });
}

上面的這段代碼,剛看了代碼一定會以爲陸續打印出1,2,3,實際輸出的是3,3,3,出現這種情況的原因是匿名函數保存的是引用
當for循環結束的時候,i已經變成3了,所以打印的時候變成3。出現這種情況的解決辦法是利用閉包解決問題。

for (var i=0; i<3; i++) {
    (function(n) {
        tasks.push(function() {
            console.log('>>> ' + n);
        });
    })(i);
}

閉包裏的匿名函數,讀取變量的順序,先讀取本地變量,再讀取父函數的局部變量,如果找不到去全局裏面搜索,i作爲局部變量存到閉包裏面,所以調整後的代碼可以能正常打印1,2,3。

for(var i=0;i<3;i++){
    !function(i){
        addEventListener('click',function(){
           alert(i);//1,2,3
        });
    }
}

!就是爲了能省略一個字符……轉化成表達式

// 這麼寫會報錯,因爲這是一個函數定義:
function() {}()

// 常見的(多了一對括號),調用匿名函數:
(function() {})()

// 但在前面加上一個布爾運算符(只多了一個感嘆號),就是表達式了,將執行後面的代碼,也就合法實現調用
!function() {}()

(3)封裝

var person = function(){
    //變量作用域爲函數內部,外部無法訪問
    var name = "default";
       
    return {
       getName : function(){
           return name;
       },
       setName : function(newName){
           name = newName;
       }
    }
}();
print(person.name);//直接訪問,結果爲undefined
print(person.getName());
person.setName("kaola");
print(person.getName());

得到結果如下:

undefined
default
kaola
(4)
一道JS前端閉包面試題解析
求出程序輸出

function fun(n,o){
    console.log(o);
    return {
        fun:function(m){
            return fun(m,n);
        }
    }
}
var a=fun(0);// undefined,因爲第二個參數未定義
a.fun(1); //0
a.fun(2); //0
a.fun(3); //0
var b=fun(0).fun(1).fun(2).fun(3);//undefined 0 1 2,參數[0,] [1,0] [2,1] [3,2]
b.fun(1);//3
var c=fun(0).fun(1);// undefined 0,並且放置[1,0]
c.fun(2);//1,c創建時候傳入[1,0],使n=1,(m=2,n=1),閉包調用o輸出1
c.fun(3);//1,解釋同上(m=3,n=1)

var a=...開始的都是函數閉包創建fun類對象,之後的a.fun()是調用return{}裏面的fun方法

本題詳細分析見另帖https://blog.csdn.net/mus123/article/details/104235166

發佈了53 篇原創文章 · 獲贊 4 · 訪問量 2206
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章