你真的懂函數嗎?

函數聲明方式

匿名函數

function後面直接跟括號,中間沒有函數名的就是匿名函數。javascript

let fn = function() {
    console.log('我是fn')
}
let fn2 = fn
console.log(fn.name) //fn
console.log(fn2.name)//fn,fn和fn2指向的是同一個function。

具名函數

function後面有函數名字的,不是直接跟括號的的就是具名函數。
若是把一個具名函數賦值給一個變量,那麼這個具名函數的做用域就不是window了。html

let fn = function fn1() {
  console.log('function')
}
console.log(fn.name) //fn1
console.log(fn1,name) // ReferenceError: fn1 is not defined

箭頭函數

箭頭函數是es6知識點,具備如下幾個特色:java

  1. 若是隻有一個參數,能夠省略小括號。
  2. 若是有至少有兩個參數,必須加小括號。
  3. 若是函數體只有一句話能夠省略花括號,而且這一句做爲返回值return。
  4. 若是函數體至少有兩句必須加上花括號。
  5. 箭頭函數裏面是沒有this的。
let fn = e => e+1
console.log(fn(1)) //2

let fn1 = (i,y) => i+y
console.log(fn1(2,3)) //5

let fn2 = (i,y) => {
  i+=1;
  y+=2;
  return i+y
}
console.log(fn2(5,6)) //13

詞法做用域(靜態做用域)

靜態做用域又叫作詞法做用域,採用詞法做用域的變量叫詞法變量。詞法變量有一個在編譯時靜態肯定的做用域。詞法變量的做用域能夠是一個函數或一段代碼,該變量在這段代碼區域內可見(visibility);在這段區域之外該變量不可見(或沒法訪問)。詞法做用域裏,取變量的值時,會檢查函數定義時的文本環境,捕捉函數定義時對該變量的綁定。

詞法做用域:變量的做用域是在定義時決定而不是執行時決定,也就是說詞法做用域取決於源碼,經過靜態分析就能肯定,所以詞法做用域也叫作靜態做用域。 with和eval除外,因此只能說JS的做用域機制很是接近詞法做用域(Lexical scope)。es6

經過詞法做用域樹能判斷變量指向關係,可是不能判定變量的值,變量的值還須要根據執行順序進一步做出判斷,看一下例子:ajax

由於JavaScript採用的是詞法做用域,bian'liang的做用域基於函數建立的位置,跟調用時的位置無關。數組

var i = 1,
    j = 2,
    k = 3;

function a(o, p, x, q) {
    var x = 4;
    alert(i);

    function b(r, s) {
        var i = 11,
            y = 5;
        alert(i);

        function c(t) {
            var z = 6;
            alert(i);
        };
        
        var d = function() {
            alert(y);
        };
        c(60);
        d();
    };
    b(40, 50);
}
a(10, 20, 30); //1 11 11 5
/**
* 模擬創建一棵語法分析樹,存儲function內的變量和方法
*/
var SyntaxTree = {
        // 全局對象在語法分析樹中的表示
    window: {
        variables:{
            i:{ value:1},
            j:{ value:2},
            k:{ value:3}
        },
        functions:{
            a: this.a
        }
    },

    a:{
        variables:{
            x:'undefined'
        },
        functions:{
            b: this.b
        },
        scope: this.window
    },

    b:{
        variables:{
                 i:'undefined'
            y:'undefined'
        },
        functions:{
            c: this.c,
            d: this.d
        },
        scope: this.a
    },

    c:{
        variables:{
            z:'undefined'
        },
        functions:{},
        scope: this.b
    },

    d:{
        variables:{},
        functions:{},
        scope: {
           scope: this.b
        }
    }
};
/**
* 活動對象:函數執行時建立的活動對象列表
*/
let ActiveObject = {
    window: {
        variables: {
            i: { value: 1 }
            j: { value: 2 }
            k: { value: 3 }
        },
        functions: {
                a: this.a
        }
    }

    a: {
        variables: {
            x: { vale: 4 },
            functions: {
                b: this.b
            },
            scope: this.window,
            params: {
                o: { value: 10 },
                p: { value: 20 },
                x: this.variables.x
                q: { vale: 'undefined' }
            },
            arguments: [this.params.o, this.params.p, this.params.x]
        }
    }

    b: {
        variables: {
            i: { vale: 11 },
            y: { vale: 5 },
        },
        functions: {
            c: this.c,
            d: this.d
        },
        params: {
            r: { value: 40 }
            s: { value: 50 }
        },
        arguments: [this.params.r, this.params.scope]
        scope: this.a
    }


    c: {
        variables: {
            z: { value: 6 },
            functions: {},
            params: {
                t: { value: 60 }
            },
            arguments: [this.params.t]
            scope: this.b
        }
    }

    d: {
        variables: {},
        functions: {},
        params: {},
        arguments: []
        this.scope: this.b
    }

}

call stack

進入call stack 時的一些規則:app

  1. 函數的全部形參(若是咱們是在函數執行上下文中)異步

    • 由名稱和對應值組成的一個變量對象的屬性被建立;沒有傳遞對應參數的話,那麼由名稱和 undefined 值組成的一種變量對象的屬性也將被建立。
  2. 全部函數聲明(FunctionDeclaration, FD)ide

    • 由名稱和對應值(函數對象(function-object))組成一個變量對象的屬性被建立;若是變量對象已經存在相同名稱的屬性,則徹底替換這個屬性。
  3. 全部變量聲明(var, VariableDeclaration)函數

    • 由名稱和對應值(undefined)組成一個變量對象的屬性被建立;若是變量名稱跟已經聲明的形式參數或函數相同,則變量聲明不會干擾已經存在的這類屬性。
/*
* example1:形參
*/
function test(a, b) {
    /*
    var a = 10
    var b = undefined
    根據規則1,在進入執行上下文時會自動對形參聲明而且賦值。
    */ 
  console.log(a)
  var c = 10;
  function d() {}
  var e = function _e() {};
  (function x() {});
}
test(10); // 10
/*
* example2:函數聲明
*/
function test(a, b) {
  console.log(a)
  function a() {}
  var e = function _e() {};
}
test(10); // ƒ a() {} .根據規則2,進入執行上下文會自動聲明形參而且賦值,可是同名的函數聲明會替換這個變量。

function test(a, b) {
  console.log(a)
  var a = 30;
  var a = function _e() {};
}
test(10); // 10 .根據規則2,進入執行上下文會自動聲明形參而且賦值,可是同名的函數聲明會替換這個變量。
/*
* example3:變量聲明
*/
console.log(foo);//會打印出foo函數,根據規則3,同名的變量聲明不會干擾函數聲明和形參

 function foo(){
    console.log("foo");
}

var foo = 1;

this和arguments

函數調用

在es5中,函數有四種調用方式:

1. fn(p1,p2)
2. obj.fn(p1,p2)
3. fn.call(context,p1,p2)
4. fn.apply(context,p1,p2)

第三和第四種纔是正常的js函數調用方式,其餘兩種就是語法糖。

fn(p1,p2)     等價於 fn.call(undefined,p1,p2) 等價於 fn.apply(context,[p1,p2])
obj.fn(p1,p2) 等價於 obj.fn.call(obj,p1,p2)   等價於 obj.fn.apply(obj,[p1,p2])
若是你傳的 context 就 null 或者 undefined,那麼 window 對象就是默認的 context(嚴格模式下默認 context 是 undefined)

this是什麼??

this是call的第一個參數!!!!

var obj = {
  foo: function(){
    console.log(this)
  }
}

var bar = obj.foo
obj.foo() // 打印出的 this 是 obj
bar() // 打印出的 this 是 window
obj.foo() 至關於 obj.foo.call(obj) 也就至關於把函數名前面的做爲call的第一個參數,也就是this,若是沒有就是window。
bar() 至關於 bar.call(undefined)

在執行函數的時候,this是隱藏的一個參數,且必須是一個對象,若是不是,js是自動把它轉爲對象。

function fn() {
    console.log(this)
    console.log(arguments)
}
fn.call(1,2,3) // Number {1}  [2,3]

arguments

arguments是僞數組它相似於Array,但除了length屬性和索引元素以外沒有任何Array屬性。
call和apply裏面除了第一個參數以外的都是arguments,若是arguments的個數少建議使用call,使用apply也能夠,若是不肯定就使用apply。
使用一下方法吧arguments轉爲真正的數組:

var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);

// ES2015
const args = Array.from(arguments);
const args = [...arguments]

bind

MDN 官方文檔對 bind() 的定義:

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

大概意思就是,bind會返回一個新的函數(並無的調用原來的函數),這個新函數會call原來的函數,call的參數由你決定。看例子:

this.x = 9;
        var module = {
          x: 81,
          getX: function() { return this.x; }
        };
        
        var retrieveX = module.getX;
        var boundGetX = retrieveX.bind(module);
        boundGetX(); // 81

retrieveX.bind(module)返回了一個新的函數boundGetX,而後調用這個新的函數的時候,把這個函數裏面的this綁定到了module對象上,因此this.x就至關於module.x也就是等於81.

柯里化

在計算機科學中,柯里化(英語:Currying),又譯爲卡瑞化或加里化,是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。這個技術由克里斯托弗·斯特雷奇以邏輯學家哈斯凱爾·加里命名的,儘管它是Moses Schönfinkel和戈特洛布·弗雷格發明的。

說的明白一點就是,給函數傳遞一部分參數,讓它返回一個函數去處理其餘參數,舉個例子,求三個數之和:

let addOne = function add(x) {
  return function(y) {
    return function(z) {
      return x+y+z
    }
  }
}

let one = addOne(3)
console.log(one)//ƒ (y) {return function (z) {return x + y + z}}
let two = one(4)
console.log(two)//ƒ (z) {return x + y + z}
let three = two(5)
console.log(three)//12

javascript函數柯里化--詳細說明連接

高階函數

在數學和計算機科學中,高階函數是至少知足下列一個條件的函數:

  1. 接受一個或多個函數做爲輸入
  2. 輸出一個函數

舉一些高階函數的例子:

/*
*接受一個或多個函數做爲輸入
*/
1. Array.prototype.filter()
2. Array.prototype.forEach()
3. Array.prototype.reduce()
4. Array.prototype.map()
5. Array.prototype.find()
6. Array.prototype.every()
/*
*輸出一個函數
*/
1. fn.bind(args)

回調函數

函數A做爲參數(函數引用)傳遞到另外一個函數B中,而且這個函數B執行函數A。咱們就說函數A叫作回調函數。若是沒有名稱(函數表達式),就叫作匿名回調函數。

名詞形式:被當作參數的函數就是回調
動詞形式:調用這個回調
注意回調跟異步沒有任何關係

回調函數的使用場合

  1. 資源加載:動態加載js文件後執行回調,加載iframe後執行回調,ajax操做回調,圖片加載完成執行回調,AJAX等等。
  2. DOM事件及Node.js事件基於回調機制(Node.js回調可能會出現多層回調嵌套的問題)。
  3. setTimeout的延遲時間爲0,這個hack常常被用到,settimeout調用的函數其實就是一個callback的體現
  4. 鏈式調用:鏈式調用的時候,在賦值器(setter)方法中(或者自己沒有返回值的方法中)很容易實現鏈式調用,而取值器(getter)相對來講很差實現鏈式調用,由於你須要取值器返回你須要的數據而不是this指針,若是要實現鏈式方法,能夠用回調函數來實現。
  5. setTimeout、setInterval的函數調用獲得其返回值。因爲兩個函數都是異步的,即:他們的調用時序和程序的主流程是相對獨立的,因此沒有辦法在主體裏面等待它們的返回值,它們被打開的時候程序也不會停下來等待,不然也就失去了setTimeout及setInterval的意義了,因此用return已經沒有意義,只能使用callback。callback的意義在於將timer執行的結果通知給代理函數進行及時處理。

回調函數的傳遞

傳遞的方式有兩種,函數引用和函數表達式。

$.get('myhtmlpage.html', myCallBack);//這是對的
$.get('myhtmlpage.html', myCallBack('foo', 'bar'));//這是錯的,那麼要帶參數呢?
$.get('myhtmlpage.html', function(){//帶參數的使用函數表達式
myCallBack('foo', 'bar');
});

箭頭函數與es5的函數主要區別

箭頭函數的主要區別在this,箭頭函數是沒有this這個概念的,看例子:

setTimeout(function(a){
  console.log(this) //這個this指的是{name:'Jack'}
  setTimeout(function(a){
    console.log(this) //這個this指的是window,由於沒有bind,調用setTimeout的是window
  },1000)
}.bind({name:'Jack'}),1000)
setTimeout(function(a){
  console.log(this) //這個this指的是{name:'Jack'}
  setTimeout(function(a){
    console.log(this) //這個this指的是{name: "Jack"},由於bind了外面的this也就是{name: "Jack"}
  },1000)
}.bind({name:'Jack'}),1000)
setTimeout(function(a){
  console.log(this) //這個this指的是{name:'Jack'}
  setTimeout(a=>console.log(this),1000)//這個this指的是{name:'Jack'},由於箭頭函數沒有this的概念,它指的this就是外面的this,也就是{name:'Jack'}
}.bind({name:'Jack'}),1000)

至此基本上說了js的全部函數內容,只是簡單舉個例子,更深刻的研究還須要看一些其餘大佬的博客哦~~~~

相關文章
相關標籤/搜索