JS系列 -- Function 淺析

概述

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

Function

每個 Function 的實例的 __proto__ 都指向了 Function.prototype(原型),函數(shù)原型(Function.prototype)的 __proto__ 指向了 Object.prototype(對象的原型)

定義函數(shù)

  1. 函數(shù)聲明

    function name( param1[ , param2[, ...] ] ){
        return
    }
    

    如果 return 省略,那么瀏覽器將自動添加 return undefined
    function 是關(guān)鍵字,它只能用來聲明函數(shù),而 var 則可以用來聲明7 種數(shù)據(jù)類型(number、string、boolean、null、undefined、symbol、 object)中的任意一種。

  2. 函數(shù)表達(dá)式

    var x = function( param1[ , param2[, ...] ] ){
        return 
    }
    

    匿名函數(shù)需要賦給變量,之后才能調(diào)用。這個匿名函數(shù)稱為函數(shù)表達(dá)式

  3. 混合式

    var fn = function name( param1[ , param2[, ...] ] ){
        return 
    }
    

    混合式中,函數(shù)名 name 只在函數(shù)體內(nèi)部有效,指代函數(shù)表達(dá)式本身,在函數(shù)體外部無效。

  4. 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
    
  5. 箭頭函數(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

自身屬性 API
  • name ==> 返回函數(shù)實例的名稱
fn.name
  • length ==> 指明函數(shù)的形參個數(shù)

Function.prototype API

Function.prototype API
  • call() ==> 調(diào)用一個函數(shù), 其具有一個指定的this值和分別地提供的參數(shù)(參數(shù)的列表)
    作用:

    1. 改變 this 值


      改變 this 值
    2. 操作參數(shù)

    語法:

    fun.call(this, arg1, arg2, ...)
    

    參數(shù):

    1. 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

    2. 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ù)決定的。
  • apply() ==> 調(diào)用一個函數(shù), 其具有一個指定的this值,以及作為一個數(shù)組(或類似數(shù)組的對象)提供的參數(shù)。
    作用:和 call() 一樣

    1. 改變 this 值
    2. 操作參數(shù)

    語法:

    fun.apply(this, arguments)
    

    使用實例:

    1. 實例1:不關(guān)心 this ,所以將 this 傳為 null

      apply 實例1

    2. 實例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ù)序列。
    作用:

    1. 切換上下文(this)
    2. 科里化

    語法:

    fun.bind( this [ , arg1 [ , arg2 [ , ... ] ] ] )
    

    使用實例:

    1. 實例1:基本用法
      bind 基本用法
    2. 實例2:實際應(yīng)用
      bind 實際應(yīng)用
    3. 實例3:利用 this + apply 實現(xiàn) bind
      重構(gòu) bind

new

  1. 創(chuàng)建一個新對象 obj ,對象 __proto__ 指向構(gòu)造函數(shù)的 prototype
    對象. __proto__ === 構(gòu)造函數(shù).prototype
    
  2. 調(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下有很多全局屬性


window

當(dāng)定義變量的時候,有可能不小心覆蓋了 window 的全局屬性,為了避免覆蓋 window 上的屬性,可以:

  1. 不用全局屬性
  2. 用局部變量
  3. 使用立即調(diào)用函數(shù),從而使用局部變量
    () => { }.call()
    () => { }()
    
  4. 但是如果像上述寫法,瀏覽器會報錯,所以要這么寫:
    (() => { }.call() )
    (() => { } ).call()
    -() => { }.call()
    +() => { }.call() 
    !() => { }.call()
    ~() => { }.call()
    { let  }  // let 作用域在塊級作用域中
    

套路

  1. 如果同一個函數(shù)被多次聲明,后面的聲明就會覆蓋前面的聲明

  2. 遞歸:函數(shù)調(diào)用自身

  3. JavaScript將函數(shù)看做一種值,函數(shù)只是一個可以執(zhí)行的值

  4. 當(dāng)函數(shù)表達(dá)式函數(shù)聲明同時有的時候

    function

  5. 不能在條件語句中聲明函數(shù)(變量提升,是的 if 無效)

  6. 函數(shù)執(zhí)行時所在的作用域,是定義時的作用域,而不是調(diào)用時所在的作用域。

  7. 參數(shù)傳值傳遞,內(nèi)存中體現(xiàn)原始值的拷貝
    參數(shù)傳址傳遞(復(fù)雜類型的值),內(nèi)存中體現(xiàn)原始值的地址,唯一一種不改變參數(shù)的是替換掉整個參數(shù),這時不會影響到原始值(添加屬性或方法會影響到原始值)

  8. 如果有同名的參數(shù),則取最后出現(xiàn)的那個值

  9. eval命令的作用是,將字符串當(dāng)作語句執(zhí)行。eval沒有自己的作用域,都在當(dāng)前作用域內(nèi)執(zhí)行,因此可能會修改當(dāng)前作用域的變量的值,造成安全問題。為了防止這種風(fēng)險,JavaScript 規(guī)定,如果使用嚴(yán)格模式eval內(nèi)部聲明的變量,不會影響到外部作用域

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,264評論 0 38
  • 1.函數(shù)參數(shù)的默認(rèn)值 (1).基本用法 在ES6之前,不能直接為函數(shù)的參數(shù)指定默認(rèn)值,只能采用變通的方法。
    趙然228閱讀 847評論 0 0
  • 工廠模式類似于現(xiàn)實生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實現(xiàn)同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 8,141評論 2 17
  • 三、閉包和高階函數(shù) 3.1 閉包 3.1.1 變量的作用域 所謂變量的作用域,就是變量的有效范圍。通過作用域的劃分...
    梁同學(xué)de自言自語閱讀 1,557評論 0 6
  • 單例模式 適用場景:可能會在場景中使用到對象,但只有一個實例,加載時并不主動創(chuàng)建,需要時才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,321評論 1 10

友情鏈接更多精彩內(nèi)容