摘要:函數(shù)是一等公民,是什么意思呢我來(lái)與大家探討一下,拋磚引玉。對(duì)于來(lái)說(shuō),函數(shù)可以賦值給變量,也可以作為函數(shù)參數(shù),還可以作為函數(shù)返回值,因此中函數(shù)是一等公民。也就是說(shuō),函數(shù)為第一公民是函數(shù)式編程的必要條件。
摘要: 聽起來(lái)很炫酷的一等公民是啥?
《JavaScript深入淺出》系列:
JavaScript深入淺出第1課:箭頭函數(shù)中的this究竟是什么鬼?
JavaScript深入淺出第2課:函數(shù)是一等公民是什么意思呢?
看到一篇講JavaScript歷史的文章里面提到:JavaScript借鑒Scheme語(yǔ)言,將函數(shù)提升到"一等公民"(first class citizen)的地位。
一等公民這個(gè)名字聽起來(lái)很高大上,但是也相當(dāng)晦澀,這個(gè)與翻譯也沒(méi)什么關(guān)系,因?yàn)?strong>first class citizen很多人包括我也不知所云。
JavaScript函數(shù)是一等公民,是什么意思呢?我來(lái)與大家探討一下,拋磚引玉。
一等公民的定義根據(jù)維基百科,編程語(yǔ)言中一等公民的概念是由英國(guó)計(jì)算機(jī)學(xué)家Christopher Strachey提出來(lái)的,時(shí)間則早在上個(gè)世紀(jì)60年代,那個(gè)時(shí)候還沒(méi)有個(gè)人電腦,沒(méi)有互聯(lián)網(wǎng),沒(méi)有瀏覽器,也沒(méi)有JavaScript。
大概很多人和我一樣,沒(méi)聽說(shuō)過(guò)Christopher Strachey,并且他也只是提出了一等公民的概念,沒(méi)有給出嚴(yán)格的定義。
關(guān)于一等公民,我找到一個(gè)權(quán)威的定義,來(lái)自于一本書《Programming Language Pragmatics》,這本書是很多大學(xué)的程序語(yǔ)言設(shè)計(jì)的教材。
In general, a value in a programming language is said to have ?rst-class status if it can be passed as a parameter, returned from a subroutine, or assigned into a variable.
也就是說(shuō),在編程語(yǔ)言中,一等公民可以作為函數(shù)參數(shù),可以作為函數(shù)返回值,也可以賦值給變量。
例如,字符串在幾乎所有編程語(yǔ)言中都是一等公民,字符串可以做為函數(shù)參數(shù),字符串可以作為函數(shù)返回值,字符串也可以賦值給變量。
對(duì)于各種編程語(yǔ)言來(lái)說(shuō),函數(shù)就不一定是一等公民了,比如Java 8之前的版本。
對(duì)于JavaScript來(lái)說(shuō),函數(shù)可以賦值給變量,也可以作為函數(shù)參數(shù),還可以作為函數(shù)返回值,因此JavaScript中函數(shù)是一等公民。
函數(shù)作為函數(shù)參數(shù)回調(diào)函數(shù)(callback)是JavaScript異步編程的基礎(chǔ),其實(shí)就是把函數(shù)作為函數(shù)參數(shù)。例如,大家常用的setTimeout函數(shù)的第一個(gè)參數(shù)就是函數(shù):
setTimeout(function() { console.log("Hello, Fundebug!"); }, 1000);
JavaScript函數(shù)作為函數(shù)參數(shù),或者說(shuō)回調(diào)函數(shù),作為實(shí)現(xiàn)異步的一種方式,大家都寫得多了,其實(shí)它還有其他應(yīng)用場(chǎng)景。
Array.prototype.sort()在對(duì)一些復(fù)雜數(shù)據(jù)結(jié)構(gòu)進(jìn)行排序時(shí),可以使用自定義的比較函數(shù)作為參數(shù):
var employees = [ { name: "Liu", age: 21 }, { name: "Zhang", age: 37 }, { name: "Wang", age: 45 }, { name: "Li", age: 30 }, { name: "zan", age: 55 }, { name: "Xi", age: 37 } ]; // 員工按照年齡排序 employees.sort(function(a, b) { return a.age - b.age; }); // 員工按照名字排序 employees.sort(function(a, b) { var nameA = a.name; var nameB = b.name; if (nameA < nameB) { return -1; } if (nameA > nameB) { return 1; } return 0; });
這樣寫看起來(lái)沒(méi)什么大不了的,但是對(duì)于JavaScript引擎來(lái)說(shuō)就省事多了,因?yàn)樗恍枰獮槊恳环N數(shù)據(jù)類型去實(shí)現(xiàn)一個(gè)排序API,它只需要實(shí)現(xiàn)一個(gè)排序API就夠了,至于數(shù)組元素大小怎么比較,交給用戶去定義,用戶如果非得說(shuō)2大于1,那也不是不可以。
換句話說(shuō),如果Array.prototype.sort()只能實(shí)現(xiàn)簡(jiǎn)單數(shù)據(jù)(比如Number與String)的排序的話,那它就太弱了,正因?yàn)榭梢允褂煤瘮?shù)作為參數(shù),使它的功能強(qiáng)大了很多。
順便提一下,實(shí)現(xiàn)一個(gè)Array.prototype.sort(),可不是什么簡(jiǎn)單的事情,大家可以看看V8是怎樣實(shí)現(xiàn)數(shù)組排序的。
將函數(shù)賦值給變量JavaScript是可以定義匿名函數(shù)的,當(dāng)我們定義有名字的函數(shù)時(shí),通常是這樣寫的:
function hello() { console.log("Hello, Fundebug!"); }
當(dāng)然,也可以將函數(shù)賦值給變量:
var hello = function() { console.log("Hello, Fundebug!"); }; console.log(typeof hello); // 打印 function
可知,hello變量的類型是"function"。
在其他的一些First-class function的定義中,還要求函數(shù)可以保存到其他數(shù)據(jù)結(jié)構(gòu),比如數(shù)組和對(duì)象中,這一點(diǎn)JavaScript也是支持的。
In?computer science, a?programming language?is said to have?first-class functions?if it treats?functions?as?first-class citizens. This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.
函數(shù)可以保存到Object中,就意味著函數(shù)成為了Object的方法。我在《JavaScript深入淺出第1課:箭頭函數(shù)中的this究竟是什么鬼?》中提過(guò),當(dāng)函數(shù)作為Object的方法被調(diào)用時(shí),它的this值就是該Object,這1點(diǎn)與Java等面向?qū)ο笳Z(yǔ)言是一致的。因此JavaScript在沒(méi)有Class之前,就在一定程度上是支持面向?qū)ο缶幊痰?,?dāng)然比較弱。
var person = { name: "Wang Lei", age: 40, greeting: function() { console.log(`Hello! My Name is ${this.name}.`); } }; console.log(person.age); // 打印 40 person.greeting(); // 打印 Hello! My Name is Wang Lei.函數(shù)作為函數(shù)返回值
通常來(lái)講,函數(shù)的返回值比較簡(jiǎn)單,比如數(shù)字、字符串、布爾值或者Object。由于JavaScript函數(shù)是第一公民,因此我們也可以在函數(shù)中返回函數(shù)。
function sayHello(message) { return function() { console.log(`Hello, ${message}`); }; } var sayHelloToFundebug = sayHello("Fundebug!"); var sayHelloToGoogle = sayHello("Google!"); sayHelloToFundebug(); // 打印Hello, Fundebug! sayHelloToGoogle(); // 打印Hello, Google!
當(dāng)我們調(diào)用sayHello函數(shù)時(shí),它返回值sayHelloToFundebug實(shí)際是一個(gè)函數(shù),我們需要調(diào)用所返回的sayHelloToFundebug函數(shù),它才會(huì)執(zhí)行,打印對(duì)應(yīng)的信息:"Hello, Fundebug!"。
我猜這個(gè)地方有人會(huì)抬杠,因?yàn)槭纠a沒(méi)有必要這么寫,因?yàn)橛懈?jiǎn)單的寫法:
function sayHello(message) { console.log(`Hello, ${message}`); } sayHello("Fundebug!"); // 打印Hello, Fundebug! sayHello("Google!"); // 打印Hello, Google!
但是這只是一個(gè)簡(jiǎn)單的示例,在一些復(fù)雜的實(shí)際場(chǎng)景中,在函數(shù)返回函數(shù)還是很有用的。下面給大家一個(gè)簡(jiǎn)單的示例。
我們Fundebug在微信小程序BUG監(jiān)控插件的時(shí)候,把不同API的定義拆分在不同的文件,但是這些API需要共享一些全局屬性,比如用戶的個(gè)性化配置。微信小程序是沒(méi)有全局變量window的,就算是網(wǎng)頁(yè)端有window其實(shí)最好也不要用,會(huì)污染全局作用域。這時(shí)候該怎么辦?給大家看看定義fundebug.test()是怎樣定義的吧:
function defineTestApi(config) { function testApi(name, message) { const event = { type: "test", apikey: config.apikey, name: name || "Test", message: message || "Hello, Fundebug!" }; sendToFundebug(event); } return testApi; }
我們使用了一個(gè)外層函數(shù)defineTestApi來(lái)共享全局配置對(duì)象config,函數(shù)中定義的testApi函數(shù)則通過(guò)return返回。
這里其實(shí)也用到了閉包,因?yàn)閐efineTestApi函數(shù)執(zhí)行結(jié)束之后,testApi函數(shù)仍然可以使用config變量,因此config變量的生命周期超越了defineTestApi函數(shù)。關(guān)于閉包的詳細(xì)介紹,我會(huì)在這個(gè)系列的后續(xù)文章中介紹。
因此,在函數(shù)中返回函數(shù),還是很有用的。
開發(fā)者對(duì)待每一個(gè)技術(shù)點(diǎn),比如閉包,應(yīng)該保持謙卑,不要覺(jué)得這個(gè)也沒(méi)有用,那個(gè)也沒(méi)有用,其實(shí)只是你還沒(méi)遇到使用場(chǎng)景而已。關(guān)于這一點(diǎn),大家可以看看我的博客《聊聊我的第一篇10萬(wàn)+,同時(shí)反駁某些評(píng)論》。
函數(shù)為第一公民是函數(shù)式編程的基礎(chǔ)函數(shù)為第一公民的3個(gè)特性我都介紹了,它們確實(shí)讓JavaScript更加強(qiáng)大,然后呢?JavaScript的騷操作大家見得多了,也不會(huì)覺(jué)得有什么神奇之處。
其實(shí),函數(shù)是第一公民,與大家都聽過(guò)的函數(shù)式編程有著密切的關(guān)系。
First-class functions are a necessity for the?functional programming?style, in which the use of?higher-order functions?is a standard practice.?
也就是說(shuō),函數(shù)為第一公民是函數(shù)式編程的必要條件。higher-order functions,即高階函數(shù),就是使用函數(shù)作為參數(shù)的函數(shù),它在函數(shù)式編程中很常見。
至于什么是函數(shù)式編程,不是我一句話能講清楚的,這可以一直聊到計(jì)算機(jī)的開山鼻祖圖靈。要知后事如何,請(qǐng)聽下回分解。
關(guān)于JS,我打算開始寫一個(gè)系列的博客,大家還有啥不太清楚的地方?不妨留言一下,我可以研究一下,然后再與大家分享一下。也大家歡迎添加我的個(gè)人微信(KiwenLau),我是Fundebug的技術(shù)負(fù)責(zé)人,一個(gè)對(duì)JS又愛又恨的程序員。
參考Javascript誕生記
Is JavaScript a (true) OOP language?
First-class functions in Java 8
《聊聊我的第一篇10萬(wàn)+,同時(shí)反駁某些評(píng)論》
關(guān)于FundebugFundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應(yīng)用實(shí)時(shí)BUG監(jiān)控。 自從2016年雙十一正式上線,F(xiàn)undebug累計(jì)處理了10億+錯(cuò)誤事件,付費(fèi)客戶有陽(yáng)光保險(xiǎn)、核桃編程、荔枝FM、掌門1對(duì)1、微脈、青團(tuán)社等眾多品牌企業(yè)。歡迎大家免費(fèi)試用!
版權(quán)聲明轉(zhuǎn)載時(shí)請(qǐng)注明作者 Fundebug以及本文地址:
https://blog.fundebug.com/201...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/104983.html
摘要:摘要是如何回收內(nèi)存的深入淺出系列深入淺出第課箭頭函數(shù)中的究竟是什么鬼深入淺出第課函數(shù)是一等公民是什么意思呢深入淺出第課什么是垃圾回收算法最近垃圾回收這個(gè)話題非?;穑蠹也荒茈S隨便便的扔垃圾了,還得先分類,這樣方便對(duì)垃圾進(jìn)行回收再利用。 摘要: JS是如何回收內(nèi)存的? 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數(shù)中的this究竟是什么鬼? Jav...
摘要:箭頭函數(shù)沒(méi)有自己的值,箭頭函數(shù)中所使用的來(lái)自于函數(shù)作用域鏈。使用箭頭函數(shù)打印對(duì)于內(nèi)層函數(shù),它本身并沒(méi)有值,其使用的來(lái)自作用域鏈,來(lái)自更高層函數(shù)的作用域。 《JavaScript 深入淺出》系列: JavaScript 深入淺出第 1 課:箭頭函數(shù)中的 this 究竟是什么鬼? JavaScript 深入淺出第 2 課:函數(shù)是一等公民是什么意思呢? 普通函數(shù)與箭頭函數(shù) 普通函數(shù)指的是...
摘要:所做的最重要的事情,就是對(duì)成千上萬(wàn)的網(wǎng)頁(yè)進(jìn)行排序,所以它存在的意義是基于網(wǎng)頁(yè)的。確實(shí)有很多非常成功的產(chǎn)品,比如,,,但是它們其實(shí)都是收購(gòu)來(lái)的。為什么呢因?yàn)橐龅綐O簡(jiǎn)主義,需要深刻思考用戶需求以及產(chǎn)品價(jià)值。 摘要: Chrome改變世界。 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數(shù)中的this究竟是什么鬼? JavaScript深入淺出第2課:...
摘要:摘要性能彪悍的引擎。深入淺出系列深入淺出第課箭頭函數(shù)中的究竟是什么鬼深入淺出第課函數(shù)是一等公民是什么意思呢深入淺出第課什么是垃圾回收算法深入淺出第課是如何工作的最近,生態(tài)系統(tǒng)又多了個(gè)非常硬核的項(xiàng)目。 摘要: 性能彪悍的V8引擎。 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數(shù)中的this究竟是什么鬼? JavaScript深入淺出第2課:函數(shù)是一...
摘要:最近在看,順便看了一些函數(shù)式編程,然后半個(gè)國(guó)慶假期就沒(méi)有了。最開始接觸函數(shù)式編程的時(shí)候,第一個(gè)接觸的概念就是高階函數(shù),和柯里化。所以我覺(jué)得最開始學(xué)習(xí)函數(shù)式編程最好先了解一些相關(guān)概念和思想會(huì)比較好。 最近在看Typescript,順便看了一些函數(shù)式編程,然后半個(gè)國(guó)慶假期就沒(méi)有了。做個(gè)筆記,分幾個(gè)部分寫吧。 最開始接觸函數(shù)式編程的時(shí)候,第一個(gè)接觸的概念就是高階函數(shù),和柯里化。咋一看,這不就...
閱讀 746·2021-11-22 13:52
閱讀 1553·2021-09-27 13:36
閱讀 2863·2021-09-24 09:47
閱讀 2224·2021-09-22 15:48
閱讀 3625·2021-09-22 15:39
閱讀 1493·2019-08-30 12:43
閱讀 2947·2019-08-29 18:39
閱讀 3222·2019-08-29 12:51