新聞中心
本文基于: Bilibili - 自由的加百利
成都創(chuàng)新互聯(lián)公司從2013年創(chuàng)立,先為太平等服務(wù)建站,太平等地企業(yè),進行企業(yè)商務(wù)咨詢服務(wù)。為太平企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
前置條件:
- 需掌握函數(shù)的編寫、傳參、返回、調(diào)用
- 理解作用域、掌握定時器的用法
- 知道引用類型和基本數(shù)據(jù)類型的區(qū)別
- 知道函數(shù)也是引用類型
- 聽說過同步異步的概念
- 了解類和對象的關(guān)系
匿名函數(shù)
來看一下一個函數(shù)的基本屬性:
匿名函數(shù)的自運行
我們可以將一個普通函數(shù)去掉它的名字,這樣就成功的創(chuàng)建了一個匿名函數(shù),并且編譯器不會報錯。
那么這個函數(shù)既然沒有名字,我們又該怎么調(diào)用它呢?這時只需要使用一個小括號包裹住整個函數(shù),再在函數(shù)體的末尾添加一個小括號就可以在創(chuàng)建函數(shù)之后立即執(zhí)行這個函數(shù)。
這種寫法,也叫作 匿名函數(shù)的自運行
其與直接在外部書寫函數(shù)體內(nèi)部的語句相比,優(yōu)點就是不會造成變量污染,會在匿名函數(shù)內(nèi)形成一個 封閉的作用域
小括號的作用
在匿名函數(shù)的外部加上一個小括號,實際的作用是 將該函數(shù)的聲明變成了一個優(yōu)先計算的表達式
( function(){...} )()
而表達式的運算結(jié)果就是這個 匿名函數(shù) 本身。拿到了函數(shù)本身之后,就可以在其后面加上一個小括號來調(diào)用它了。
把函數(shù)變成表達式?
既然小括號的作用是將函數(shù)的聲明變成表達式,那么在函數(shù)周圍加上運算符會不會有同樣的效果呢?
+function(){...}()
!function(){...}()
~function(){...}()
void function(){...}()
delete function(){...}()
以上的幾種寫法都可以成功執(zhí)行匿名函數(shù),而且使用 +function(){...}()
這種方式執(zhí)行函數(shù)自運行的效率是最高的。
遞歸函數(shù)
遞歸函數(shù) 是指一個函數(shù)直接或間接的調(diào)用自身,并在特定的情況下結(jié)束并放回運行結(jié)果
這里我們舉一個 階乘 的例子:
function F(N) {
return N * F(N - 1);
}
表面看上去,這個函數(shù)可以接收一個參數(shù),并計算出這個數(shù)的階乘。但是仔細想想就會發(fā)現(xiàn)不對勁,當(dāng) N = 1
時函數(shù)并沒有停止自身的繼續(xù)傳遞,也就是說這個函數(shù)沒有停止條件,最終便會陷入一個死循環(huán)。結(jié)果就是 會在某一時刻,大量的函數(shù)將內(nèi)存空間占滿導(dǎo)致內(nèi)存溢出。
也就是說我們上面寫的這個函數(shù),只有 遞 沒有 歸
改造遞歸
我們嘗試改變一下上面的 遞歸函數(shù)
首先要弄清楚,我們需要計算的是一個數(shù) 它的階乘是多少。計算一個數(shù)字的階乘便是讓這個數(shù)每次乘以比他自身小 1 的數(shù),直到乘到1。(說得不是很清楚,大家自行理解)
那么關(guān)鍵點就在于這個 直到
我們不能讓它無止境的傳遞下去,在上面的例子中,參與遞歸的 N
為 1 時還在繼續(xù)向內(nèi)傳遞,0, -1, -2, -3...
我們所要做的就是當(dāng)函數(shù)傳遞到 N = 1
時停止向內(nèi)傳遞,直接返回 1 自身,將其自己交給外部的函數(shù)來調(diào)用,代碼更改如下:
function F(N) {
if (N == 1) return 1;
return N * F(N - 1);
}
上面 if
語句的作用是:當(dāng) N 為 1 時,直接返回 1
這時運行一下就會發(fā)現(xiàn),函數(shù)不報錯了,而且也得到了我們想要的結(jié)果。
回調(diào)函數(shù)
回調(diào)函數(shù),并不是指一種特殊的函數(shù),而是指函數(shù)的使用方式
看一下下面的代碼:
function f1(){
console.log(111);
}
function f2(){
console.log(222);
}
f1();
f2();
輸出結(jié)果的順序自然是先輸出 111,再輸出 222
但是如果我們給 f1()
添加一個定時器呢?
function f1(){
setTimeout(function(){
console.log(111);
}, 1000)
}
function f2(){
console.log(222);
}
f1();
f2();
這時便會先輸出 222,一秒后輸出 111。這種含有異步操作的函數(shù)就被稱為 異步函數(shù) ,異步函數(shù)最大的特點就是 后續(xù)的代碼不需要排隊,異步函數(shù)時可以和后續(xù)的代碼并行的。f1()
就是一個典型的異步函數(shù),你無法知道 f1()
和 f2()
哪一個會先結(jié)束。
回調(diào)函數(shù)引出
那么在有異步函數(shù)的情況下,如果我希望先輸出 111,再輸出222,要怎么做呢?
目前看來,唯一的辦法是 把函數(shù) f2()
放在 f1()
的內(nèi)部調(diào)用
function f1(){
setTimeout(function(){
console.log(111);
f2();
}, 1000)
}
function f2(){
console.log(222);
}
f1();
假設(shè)有這樣一個場景,項目組里有小白、小黃、小綠三個人,有一個工具函數(shù) getToken()
function getToken(){
//異步函數(shù)......
}
它是一個異步函數(shù),大家都在使用這個函數(shù)完成自己的業(yè)務(wù),并且每個人都希望在 getToken()
結(jié)束后執(zhí)行自己的代碼,于是它們將函數(shù)寫成了下面這樣:
但是這種寫法顯然是錯誤的,因為異步函數(shù)保證不了函數(shù)的執(zhí)行順序。那么現(xiàn)在只能想辦法將自己所寫的函數(shù)放在異步函數(shù)內(nèi)部,才有機會在其后面執(zhí)行。
首先,我們給 getToken()
函數(shù)增加一個參數(shù) callback
function getToken(callback){
//異步函數(shù)......
}
之后,三個人的代碼就可以改成這樣:
把自己的函數(shù)傳進去,最后在 getToken()
的最后調(diào)用這個 callback
function getToken(callback){
//異步函數(shù)......
callback();
}
現(xiàn)在,所有人的代碼都會在異步函數(shù)最后執(zhí)行,這極大的提高了代碼的可復(fù)用性,降低了開發(fā)維護的成本。
這種函數(shù)調(diào)用的方式就叫回調(diào)
字面意思就是:把自己的函數(shù)交給別人,回頭再調(diào)。
構(gòu)造函數(shù)
- 這一節(jié)需要理解 什么是面向?qū)ο?/li>
一個函數(shù)除了可以被當(dāng)作函數(shù),還可以被當(dāng)作
class
function fn(){
}
let obj = new fn();
console.log( typeof obj );
我們可以直接使用 new
關(guān)鍵字來聲明一個對象,這個時候,我們就說 fn()
是一個構(gòu)造函數(shù)
那么 fn()
明明是一個空函數(shù),這個對象是怎么來的呢?
構(gòu)造函數(shù)的執(zhí)行流程
問題的關(guān)鍵就在于這個 new
關(guān)鍵字。當(dāng)你調(diào)用函數(shù)時在前面加上了 new
關(guān)鍵字,瀏覽器就會啟動 構(gòu)造函數(shù) 的執(zhí)行流程:
function fn(){
this = {}
// 創(chuàng)建一個空對象,將其保存在this關(guān)鍵字中
...... //your code
return this;
}
let obj = new fn();
當(dāng)然了,上面部分代碼是不可見的。一個函數(shù)到底是普通函數(shù)還是構(gòu)造函數(shù),取決于你來怎么使用它。
但是通常,按照習(xí)慣,我們會將構(gòu)造函數(shù)的首字母大寫,普通函數(shù)的首字母小寫。也就是說,如果你看到一個函數(shù)的首字母是大寫的,在絕大多數(shù)的時候,它不應(yīng)該被直接調(diào)用。
function User() {
......
}
let user = User(); ×
let user = new User(); √
在最新版的 JavaScript
已經(jīng)支持了 class
關(guān)鍵字,你可以像 Java
一樣定義一個類,并通過構(gòu)造方法來生成對象。
閉包函數(shù)
function a(){
let x = 1;
function b(){
console.log(x);
}
}
函數(shù) b()
是一個定義在函數(shù) a()
內(nèi)部的函數(shù),所以其可以訪問到變量 x
,變量 x
相對于函數(shù) b()
來說就是一個全局變量。
如果我們把函數(shù) b()
作為函數(shù) a()
的返回值:
function a(){
let x = 1;
return function b(){
console.log(x);
}
}
let c = a();
c();
我們已知,函數(shù) c()
就是函數(shù) b()
,有由于函數(shù) c()
是全局變量,因此,相當(dāng)于在全局范圍調(diào)用了函數(shù) b()
,打破了函數(shù) b()
只能在局部使用的限制,最終我們打印出了變量 x
在這里,函數(shù) a()
所形成的作用域,叫做 閉包,函數(shù) b()
被稱作 閉包函數(shù)
函數(shù)的柯里化
這一節(jié)來源于知乎:https://zhuanlan.zhihu.com/p/#:~:text=函數(shù)柯里化,就是,后,才執(zhí)行原函數(shù)
function add(a, b) {
return a + b
}
function curry(fn) {
return function (a) {
return function (b) {
return fn(a, b)
}
}
}
let fn = curry(add)(1)(2)
網(wǎng)站名稱:關(guān)于 JS 函數(shù)的一切
標(biāo)題路徑:http://fisionsoft.com.cn/article/dsojpsd.html