概述
函數(shù)是一個可以執(zhí)行代碼的對象。每個函數(shù)都是 Function 類型的實例,并且都與其他引用類型一樣具有屬性和方法。

每個 Function 的實例的 __proto__ 都指向了 Function.prototype(原型),函數(shù)原型(Function.prototype)的 __proto__ 指向了 Object.prototype(對象的原型)
定義函數(shù)
-
函數(shù)聲明
function name( param1[ , param2[, ...] ] ){ return }如果
return省略,那么瀏覽器將自動添加return undefined
function是關(guān)鍵字,它只能用來聲明函數(shù),而var則可以用來聲明7 種數(shù)據(jù)類型(number、string、boolean、null、undefined、symbol、 object)中的任意一種。 -
函數(shù)表達(dá)式
var x = function( param1[ , param2[, ...] ] ){ return }匿名函數(shù)需要賦給變量,之后才能調(diào)用。這個匿名函數(shù)稱為函數(shù)表達(dá)式
-
混合式
var fn = function name( param1[ , param2[, ...] ] ){ return }混合式中,函數(shù)名 name 只在函數(shù)體內(nèi)部有效,指代函數(shù)表達(dá)式本身,在函數(shù)體外部無效。
-
Function 構(gòu)造函數(shù)
最后一個參數(shù)被始終看成函數(shù)體,而前面的參數(shù)則枚舉了新函數(shù)的參數(shù)。var fn = new Function( 'param1'[ , 'param2'[,' ...'] ] , functionBoby ){ return }例子:
let n = 1; let fn = new Function( 'x' , 'y' , 'return x + ' + n + ' + y' ); fn( 1,2 ) //4,此時的 n 是1 -
箭頭函數(shù)(ES6)
(param1[ , param2[, ...] ])=> { return }
形參和實參
- parameters ==> 形參,定義時的占位符
- arguments ==> 實參,調(diào)用時傳入的參數(shù)
說明
- 函數(shù)會在執(zhí)行完 return 語句之后停止并立即退出,因此,位于 return 語句之后的任何代碼都永遠(yuǎn)不會執(zhí)行。
- 沒有傳遞值的命名參數(shù)將自動被賦值 undefined
- 所有參數(shù)傳遞的都是值,不可能通過引用傳遞參數(shù)
- 驗證函數(shù),使用
typeof xxx === 'function'
函數(shù)聲明和函數(shù)表達(dá)式
- 函數(shù)聲明
瀏覽器解析器會率先讀取函數(shù)聲明(聲明前置),并使其在執(zhí)行任何代碼之前可用 - 函數(shù)表達(dá)式
必須要等到瀏覽器解析器執(zhí)行到它所在的代碼行,才會被解析執(zhí)行
聲明前置
先聲明變量,后聲明 function,如果 function 名字和 var 聲明名字一樣
function 優(yōu)先。如果賦值就按照賦值的類型。

API
自身 API

-
name==> 返回函數(shù)實例的名稱

-
length==> 指明函數(shù)的形參個數(shù)
Function.prototype API

-
call()==> 調(diào)用一個函數(shù), 其具有一個指定的this值和分別地提供的參數(shù)(參數(shù)的列表)
作用:-
改變 this 值
改變 this 值 - 操作參數(shù)
語法:
fun.call(this, arg1, arg2, ...)參數(shù):
-
this :函數(shù)運(yùn)行時的指定的 this 值。
-
非嚴(yán)格模式下,若 this 為 null 或者 undefined ,則 this 自動指向全局對象(global[ window ]),同時值為原始值(數(shù)字,字符串,布爾值)的 this 會指向該原始值的自動包裝對象。
非嚴(yán)格模式 -
嚴(yán)格模式下,若 this 為 undefined ,則 this === undefined
嚴(yán)格模式
此處 call 的時候指明了 this === 1,例子不明顯,應(yīng)該 fn.call(),此時便可以打印出 undefined
-
-
arguments :偽數(shù)組,元素為函數(shù)的參數(shù)
- 函數(shù)中的參數(shù)在內(nèi)部是用一個偽數(shù)組(
arguments)來表示,在函數(shù)內(nèi)部通過arguments對象來訪問參數(shù)從而獲取傳遞給函數(shù)的每一個參數(shù)。 -
arguments可以被用作被調(diào)用對象的所有未指定的參數(shù)。可以使用arguments來把所有的參數(shù)傳遞給被調(diào)用對象。 -
arguments對象的長度是由傳入的參數(shù)個數(shù)決定的,不是由定義函數(shù)時的命名參數(shù)的個數(shù)決定的。
- 函數(shù)中的參數(shù)在內(nèi)部是用一個偽數(shù)組(
-
-
apply()==> 調(diào)用一個函數(shù), 其具有一個指定的this值,以及作為一個數(shù)組(或類似數(shù)組的對象)提供的參數(shù)。
作用:和 call() 一樣- 改變 this 值
- 操作參數(shù)
語法:
fun.apply(this, arguments)使用實例:
-
實例1:不關(guān)心 this ,所以將 this 傳為 null
apply 實例1 -
實例2:關(guān)心 this ,this === arr1
Apply 實例2
說明:
call()和apply()的區(qū)別
apply()與call()作用相同,不同之處在于提供參數(shù)的方式。
apply()使用參數(shù)數(shù)組,可以將數(shù)組里的元素拆分傳入?yún)?shù)
call()使用一組參數(shù)列表,一個一個傳遞參數(shù)
apply可以使用數(shù)組字面量,也可以使用arguments對 象,arguments是一個函數(shù)的局部變量。
-
bind()==> 創(chuàng)建一個新的函數(shù), 當(dāng)被調(diào)用時,將其this關(guān)鍵字設(shè)置為提供的值,返回新創(chuàng)建的函數(shù)。在調(diào)用新函數(shù)時,在任何提供之前提供一個給定的參數(shù)序列。
作用:- 切換上下文(this)
- 科里化
語法:
fun.bind( this [ , arg1 [ , arg2 [ , ... ] ] ] )使用實例:
- 實例1:基本用法
bind 基本用法 - 實例2:實際應(yīng)用
bind 實際應(yīng)用 - 實例3:利用 this + apply 實現(xiàn) bind
重構(gòu) bind
new
- 創(chuàng)建一個新對象 obj ,對象
__proto__指向構(gòu)造函數(shù)的prototype對象. __proto__ === 構(gòu)造函數(shù).prototype - 調(diào)用 call()/apply()/bind()
Call Stack(調(diào)用棧)
棧是一種數(shù)據(jù)結(jié)構(gòu),特點是先進(jìn)后出,js 代碼執(zhí)行時都遵循 Call Stack 的規(guī)則。如果壓棧太多會導(dǎo)致錯誤(Stack Overflow[ 棧溢出 ])
作用域 和 作用域鏈
scope(范圍、視野、眼界)
如果在函數(shù)作用域中沒有寫 var 直接寫 a = 3
1. 優(yōu)先賦值
2. 沿著作用域鏈尋找 a 的聲明
3. 若作用域鏈中沒有 a 的聲明,則聲明全局變量并賦值
變量提升
將作用域鏈中首先要做變量提升
例子1:
var a = 1;
function fn(){
console.log( a );
var a = 2;
}
fn.call(); // a === undefined
例子2:
var a = 1;
function fn1(){
console.log( a ); // a === undefined
var a = 2;
fn2.call();
}
function fn2(){
console.log( a ); // a === 1
}
fn1.call();
例子3:
var obj = { name: ' obj ' }
function fn1(){
function fn2(){
console.log( this ); // this === window
}
fn2.call();
}
fn1.call( obj );
例子4:
var liTags = document.querySelectAll( ' li ' );
for( let i = 0, len = liTags.length; i < len; i++ ){
liTags[ i ].onclick = function(){
console.log( i ) // 打印出的 i 都是最后的數(shù)字
}
}
this 值
this 的調(diào)用主要有兩種方式,一種是函數(shù)(function),另一種是作為對象的方法(methods)
判斷 this 值就看函數(shù)怎么被調(diào)用,之后轉(zhuǎn)化為 call 形式
例子1:作為對象的方法調(diào)用
function fn(){
console.log(this)
}
let obj = {
a: 'a',
fn: fn
}
obj.fn() // this === obj
例子2:作為對象屬性的深層次嵌套
function fn(){
console.log(this)
}
let obj = {
a: 'a',
wrapper: {
b: 'b',
fn: fn
}
}
obj.wrapper.fn() // this === { b: 'b', fn: fn } === obj.wrapper
例子3:
let obj = {
a: 'a',
fn () {
console.log(fn)
}
}
function fncb(cb){
cb()
}
fncb(obj.fn) // this === 全局變量
閉包
如果一個函數(shù)使用了它范圍外的值,那么這個函數(shù) + 這個變量就叫做閉包。

立即調(diào)用函數(shù)
window下有很多全局屬性

當(dāng)定義變量的時候,有可能不小心覆蓋了 window 的全局屬性,為了避免覆蓋 window 上的屬性,可以:
- 不用全局屬性
- 用局部變量
- 使用立即調(diào)用函數(shù),從而使用局部變量
() => { }.call() () => { }() - 但是如果像上述寫法,瀏覽器會報錯,所以要這么寫:
(() => { }.call() ) (() => { } ).call() -() => { }.call() +() => { }.call() !() => { }.call() ~() => { }.call() { let } // let 作用域在塊級作用域中
套路
如果同一個函數(shù)被多次聲明,后面的聲明就會覆蓋前面的聲明
遞歸:函數(shù)調(diào)用自身
JavaScript將函數(shù)看做一種值,函數(shù)只是一個可以執(zhí)行的值
-
當(dāng)函數(shù)表達(dá)式和函數(shù)聲明同時有的時候
function 不能在條件語句中聲明函數(shù)(變量提升,是的 if 無效)
函數(shù)執(zhí)行時所在的作用域,是定義時的作用域,而不是調(diào)用時所在的作用域。
參數(shù)傳值傳遞,內(nèi)存中體現(xiàn)原始值的拷貝。
參數(shù)傳址傳遞(復(fù)雜類型的值),內(nèi)存中體現(xiàn)原始值的地址,唯一一種不改變參數(shù)的是替換掉整個參數(shù),這時不會影響到原始值(添加屬性或方法會影響到原始值)如果有同名的參數(shù),則取最后出現(xiàn)的那個值
eval命令的作用是,將字符串當(dāng)作語句執(zhí)行。eval沒有自己的作用域,都在當(dāng)前作用域內(nèi)執(zhí)行,因此可能會修改當(dāng)前作用域的變量的值,造成安全問題。為了防止這種風(fēng)險,JavaScript 規(guī)定,如果使用嚴(yán)格模式,eval內(nèi)部聲明的變量,不會影響到外部作用域








