目錄
- JavaScript 函式定義
- 自呼叫函式
- 箭頭函式
- JavaScript 函式引數
- 引數規則
- 默認引數
- ES6 函式可以自帶引數
- arguments 物件
- JavaScript 函式呼叫
- this 關鍵字
- 呼叫 JavaScript 函式
- 作為一個函式呼叫
- 函式作為方法呼叫
- 使用建構式呼叫函式
- 作為函式方法呼叫函式
- JavaScript 閉包
- 全域變數
- JavaScript 內嵌函式
- JavaScript 閉包
- 實體決議
- 博客參考
JavaScript 函式定義
JavaScript 使用關鍵字 function 定義函式,
函式可以通過宣告定義,也可以是一個運算式,
函式宣告
function functionName(parameters) {
執行的代碼
}
函式宣告后不會立即執行,會在我們需要的時候呼叫到,
分號是用來分隔可執行JavaScript陳述句,
由于函式宣告不是一個可執行陳述句,所以不以分號結束,
函式運算式
JavaScript 函式可以通過一個運算式定義,
函式運算式可以存盤在變數中:
var x = function (a, b) {return a * b};
在函式運算式存盤在變數后,變數也可作為一個函式使用:
var x = function (a, b) {return a * b};
var z = x(4, 3);
以上函式實際上是一個 匿名函式 (函式沒有名稱),
函式存盤在變數中,不需要函式名稱,通常通過變數名來呼叫,
上述函式以分號結尾,因為它是一個執行陳述句,
Function() 建構式
在以上實體中,我們了解到函式通過關鍵字 function 定義,
函式同樣可以通過內置的 JavaScript 函式構造器(Function())定義,
var myFunction = function (a, b) {return a * b};
var x = myFunction(4, 3);
在 JavaScript 中,很多時候,你需要避免使用 new 關鍵字,
函式提升(Hoisting)
在之前的教程中我們已經了解了 “hoisting(提升)”,
提升(Hoisting)是 JavaScript 默認將當前作用域提升到前面去的的行為,
提升(Hoisting)應用在變數的宣告與函式的宣告,
因此,函式可以在宣告之前呼叫:
myFunction(5);
function myFunction(y) {
return y * y;
}
使用運算式定義函式時無法提升,
自呼叫函式
函式運算式可以 “自呼叫”,
自呼叫運算式會自動呼叫,
如果運算式后面緊跟 () ,則會自動呼叫,
不能自呼叫宣告的函式,
通過添加括號,來說明它是一個函式運算式:
以上函式實際上是一個 匿名自我呼叫的函式 (沒有函式名),
函式可作為一個值使用
function myFunction(a, b) {
return a * b;
}
var x = myFunction(4, 3);
JavaScript 函式可作為運算式使用:
function myFunction(a, b) {
return a * b;
}
var x = myFunction(4, 3) * 2;
函式是物件
在 JavaScript 中使用 typeof 運算子判斷函式型別將回傳 “function” ,
但是JavaScript 函式描述為一個物件更加準確,
JavaScript 函式有 屬性 和 方法,
arguments.length 屬性回傳函式呼叫程序接收到的引數個數:
toString() 方法將函式作為一個字串回傳:
函式定義作為物件的屬性,稱之為物件方法,
函式如果用于創建新的物件,稱之為物件的建構式,
箭頭函式
ES6 新增了箭頭函式,
箭頭函式運算式的語法比普通函式運算式更簡潔,
(引數1, 引數2, …, 引數N) => { 函式宣告 }
(引數1, 引數2, …, 引數N) => 運算式(單一)
// 相當于:(引數1, 引數2, …, 引數N) =>{ return 運算式; }
沒有引數的函式應該寫成一對圓括號:
() => {函式宣告}
- 有的箭頭函式都沒有自己的 this, 不適合定義一個 物件的方法,
- 當我們使用箭頭函式的時候,箭頭函式會默認幫我們系結外層 this的值,所以在箭頭函式中 this 的值和外層的 this 是一樣的,
- 箭頭函式是不能提升的,所以需要在使用之前定義,
- 使用 const比使用 var 更安全,因為函式運算式始終是一個常量,
- 如果函式部分只是一個陳述句,則可以省略 return 關鍵字和大括號{},這樣做是一個比較好的習慣:
JavaScript 函式引數
JavaScript 函式對引數的值沒有進行任何的檢查,
functionName(parameter1, parameter2, parameter3) {
// 要執行的代碼……
}
函式顯式引數在函式定義時列出,
函式隱式引數在函式呼叫時傳遞給函式真正的值,
引數規則
JavaScript 函式定義顯式引數時沒有指定資料型別,
JavaScript 函式對隱式引數沒有進行型別檢測,
JavaScript 函式對隱式引數的個數沒有進行檢測,
默認引數
ES5 中如果函式在呼叫時未提供隱式引數,引數會默認設定為: undefined
有時這是可以接受的,但是建議最好為引數設定一個默認值:
實體(ES5)
function myFunction(x, y) {
if (y === undefined) {
y = 0;
}
}
或者,更簡單的方式:
實體(ES5)
function myFunction(x, y) {
y = y || 0;
}
如果y已經定義 , y || 回傳 y, 因為 y 是 true, 否則回傳 0, 因為 undefined 為 false,
如果函式呼叫時設定了過多的引數,引數將無法被參考,因為無法找到對應的引數名, 只能使用 arguments 物件來呼叫,
ES6 函式可以自帶引數
ES6 支持函式帶有默認引數,就判斷 undefined 和 || 的操作:
實體(ES6)
function myFunction(x, y = 10) {
// 如果不傳入引數 y ,則其默認值為 10
return x + y;
}
// 輸出 2
document.getElementById("demo1").innerHTML = myFunction(0, 2) ;
// 輸出 15, y 引數的默認值
document.getElementById("demo2").innerHTML = myFunction(5);
arguments 物件
JavaScript 函式有個內置的物件 arguments 物件,
argument 物件包含了函式呼叫的引數陣列,
通過這種方式你可以很方便的找到最大的一個引數的值:
x = findMax(1, 123, 500, 115, 44, 88);
function findMax() {
var i, max = arguments[0];
if(arguments.length < 2) return max;
for (i = 0; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
通過值傳遞引數
在函式中呼叫的引數是函式的隱式引數,
JavaScript 隱式引數通過值來傳遞:函式僅僅只是獲取值,
如果函式修改引數的值,不會修改顯式引數的初始值(在函式外定義),
隱式引數的改變在函式外是不可見的,
通過物件傳遞引數
在JavaScript中,可以參考物件的值,
因此我們在函式內部修改物件的屬性就會修改其初始的值,
修改物件屬性可作用于函式外部(全域變數),
修改物件屬性在函式外是可見的,
JavaScript 函式呼叫
JavaScript 函式有 4 種呼叫方式,
每種方式的不同在于 this 的初始化,
this 關鍵字
一般而言,在Javascript中,this指向函式執行時的當前物件,
注意 this 是保留關鍵字,你不能修改 this 的值,
呼叫 JavaScript 函式
在之前的章節中我們已經學會了如何創建函式,
函式中的代碼在函式被呼叫后執行,
作為一個函式呼叫
function myFunction(a, b) {
return a * b;
}
myFunction(10, 2); // myFunction(10, 2) 回傳 20
以上函式不屬于任何物件,但是在 JavaScript 中它始終是默認的全域物件,
在 HTML 中默認的全域物件是 HTML 頁面本身,所以函式是屬于 HTML 頁面,
在瀏覽器中的頁面物件是瀏覽器視窗(window 物件),以上函式會自動變為 window 物件的函式,
myFunction() 和 window.myFunction() 是一樣的:
function myFunction(a, b) {
return a * b;
}
window.myFunction(10, 2); // window.myFunction(10, 2) 回傳 20
這是呼叫 JavaScript 函式常用的方法, 但不是良好的編程習慣
全域變數,方法或函式容易造成命名沖突的bug,
全域物件
當函式沒有被自身的物件呼叫時 this 的值就會變成全域物件,
在 web 瀏覽器中全域物件是瀏覽器視窗(window 物件),
該實體回傳 this 的值是 window 物件:
function myFunction() {
return this;
}
myFunction(); // 回傳 window 物件 [object Window]
函式作為全域物件呼叫,會使 this 的值成為全域物件,
使用 window 物件作為一個變數容易造成程式崩潰,
函式作為方法呼叫
在 JavaScript 中你可以將函式定義為物件的方法,
以下實體創建了一個物件 (myObject), 物件有兩個屬性 (firstName 和 lastName), 及一個方法 (fullName):
var myObject = {
firstName:"John",
lastName: "Doe",
fullName: function () {
return this.firstName + " " + this.lastName;
}
}
myObject.fullName(); // 回傳 "John Doe"
fullName 方法是一個函式,函式屬于物件, myObject 是函式的所有者,
this物件,擁有 JavaScript 代碼,實體中 this 的值為 myObject 物件,
測驗以下!修改 fullName 方法并回傳 this 值:
var myObject = {
firstName:"John",
lastName: "Doe",
fullName: function () {
return this;
}
}
myObject.fullName(); // 回傳 [object Object] (所有者物件)
函式作為物件方法呼叫,會使得 this 的值成為物件本身,
使用建構式呼叫函式
如果函式呼叫前使用了 new 關鍵字, 則是呼叫了建構式,
這看起來就像創建了新的函式,但實際上 JavaScript 函式是重新創建的物件:
// 建構式:
function myFunction(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
}
// This creates a new object
var x = new myFunction("John","Doe");
x.firstName; // 回傳 "John"
建構式的呼叫會創建一個新的物件,新物件會繼承建構式的屬性和方法,
建構式中 this 關鍵字沒有任何的值,
this 的值在函式呼叫實體化物件(new object)時創建,
作為函式方法呼叫函式
在 JavaScript 中, 函式是物件,JavaScript 函式有它的屬性和方法,
call() 和 apply() 是預定義的函式方法, 兩個方法可用于呼叫函式,兩個方法的第一個引數必須是物件本身,
function myFunction(a, b) {
return a * b;
}
myObject = myFunction.call(myObject, 10, 2); // 回傳 20
function myFunction(a, b) {
return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray); // 回傳 20
兩個方法都使用了物件本身作為第一個引數, 兩者的區別在于第二個引數: apply傳入的是一個引數陣列,也就是將多個引陣列合成為一個陣列傳入,而call則作為call的引數傳入(從第二個引數開始),
在 JavaScript 嚴格模式(strict mode)下, 在呼叫函式時第一個引數會成為 this 的值, 即使該引數不是一個物件,
在 JavaScript 非嚴格模式(non-strict mode)下, 如果第一個引數的值是 null 或 undefined, 它將使用全域物件替代,
通過 call() 或 apply() 方法你可以設定 this 的值, 且作為已存在物件的新方法呼叫,
this 是 JavaScript 語言的一個關鍵字,
它代表函式運行時,自動生成的一個內部物件,只能在函式內部使用,比如:
function test() {
this.x = 1;
}
隨著函式使用場合的不同,this 的值會發生變化,但是有一個總的原則,那就是this指的是,呼叫函式的那個物件,
作為函式方法呼叫函式時,此函式執行了相當于 java 中靜態函式的功能,
<script>
var myObject, myArray;
myObject={
name: "hahaha ",
hsk: "en"
};
function myFunction(a, b) {
alert(this);
return this.name +this.hsk;
}
myArray = [10, 2]
myObject = myFunction.apply(myObject, myArray);
document.getElementById("demo").innerHTML = myObject;
</script>
可以用的頻率較高的函式作這樣的設定,為物件執行相關操作,
JavaScript 閉包
JavaScript 變數可以是區域變數或全域變數,
私有變數可以用到閉包,
全域變數
函式可以訪問由函式內部定義的變數,如:
function myFunction() {
var a = 4;
return a * a;
}
函式也可以訪問函式外部定義的變數,如:
var a = 4;
function myFunction() {
return a * a;
}
后面一個實體中, a 是一個 全域 變數,
在web頁面中全域變數屬于 window 物件,
全域變數可應用于頁面上的所有腳本,
在第一個實體中, a 是一個 區域 變數,
區域變數只能用于定義它函式內部,對于其他的函式或腳本代碼是不可用的,
全域和區域變數即便名稱相同,它們也是兩個不同的變數,修改其中一個,不會影響另一個的值,
變數宣告時如果不使用 var 關鍵字,那么它就是一個全域變數,即便它在函式內定義,
變數生命周期
全域變數的作用域是全域性的,即在整個JavaScript程式中,全域變數處處都在,
而在函式內部宣告的變數,只在函式內部起作用,這些變數是區域變數,作用域是區域性的;函式的引數也是區域性的,只在函式內部起作用,
計數器困境
設想下如果你想統計一些數值,且該計數器在所有函式中都是可用的,
你可以使用全域變數,函式設定計數器遞增:
var counter = 0;
function add() {
return counter += 1;
}
add();
add();
add();
// 計數器現在為 3
計數器數值在執行 add() 函式時發生變化,
但問題來了,頁面上的任何腳本都能改變計數器,即便沒有呼叫 add() 函式,
如果我在函式內宣告計數器,如果沒有呼叫函式將無法修改計數器的值:
function add() {
var counter = 0;
return counter += 1;
}
add();
add();
add();
// 本意是想輸出 3, 但事與愿違,輸出的都是 1 !
以上代碼將無法正確輸出,每次我呼叫 add() 函式,計數器都會設定為 1,
JavaScript 內嵌函式可以解決該問題,
JavaScript 內嵌函式
所有函式都能訪問全域變數,
實際上,在 JavaScript 中,所有函式都能訪問它們上一層的作用域,
JavaScript 支持嵌套函式,嵌套函式可以訪問上一層的函式變數,
該實體中,內嵌函式 plus() 可以訪問父函式的 counter 變數:
function add() {
var counter = 0;
function plus() {counter += 1;}
plus();
return counter;
}
如果我們能在外部訪問 plus() 函式,這樣就能解決計數器的困境,
我們同樣需要確保 counter = 0 只執行一次,
我們需要閉包,
JavaScript 閉包
還記得函式自我呼叫嗎?該函式會做什么?
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// 計數器為 3
實體決議
變數 add 指定了函式自我呼叫的回傳字值,
自我呼叫函式只執行一次,設定計數器為 0,并回傳函式運算式,
add變數可以作為一個函式使用,非常棒的部分是它可以訪問函式上一層作用域的計數器,
這個叫作 JavaScript 閉包,它使得函式擁有私有變數變成可能,
計數器受匿名函式的作用域保護,只能通過 add 方法修改,
閉包是一種保護私有變數的機制,在函式執行時形成私有的作用域,保護里面的私有變數不受外界干擾,直觀的說就是形成一個不銷毀的堆疊環境,
博客參考
JS閉包的理解
閉包,看這一篇就夠了——帶你看透閉包的本質,百發百中
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/262063.html
標籤:其他
