摘要:當控制流遇到這樣的語句時,它立即跳出當前函數并將返回的值賦給調用該函數的代碼。函數聲明不是常規的從上到下的控制流的一部分。該函數調用控制臺的來完成它的工作,然后將控制流返回到第行。
來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項目原文:Functions
譯者:飛龍
協議:CC BY-NC-SA 4.0
自豪地采用谷歌翻譯
部分參考了《JavaScript 編程精解(第 2 版)》
人們認為計算機科學是天才的藝術,但是實際情況相反,只是許多人在其它人基礎上做一些東西,就像一面由石子壘成的墻。
高德納
函數是 JavaScript 編程的面包和黃油。 將一段程序包裝成值的概念有很多用途。 它為我們提供了方法,用于構建更大程序,減少重復,將名稱和子程序關聯,以及將這些子程序相互隔離。
函數最明顯的應用是定義新詞匯。 用散文創造新詞匯通常是不好的風格。 但在編程中,它是不可或缺的。
以英語為母語的典型成年人,大約有 2 萬字的詞匯量。 很少有編程語言內置了 2 萬個命令。而且,可用的詞匯的定義往往比人類語言更精確,因此靈活性更低。 因此,我們通常會引入新的概念,來避免過多重復。
定義函數函數定義是一個常規綁定,其中綁定的值是一個函數。 例如,這段代碼定義了square,來引用一個函數,它產生給定數字的平方:
const square = function(x) { return x * x; }; console.log(square(12)); // → 144
函數使用以關鍵字function起始的表達式創建。 函數有一組參數(在本例中只有x)和一個主體,它包含調用該函數時要執行的語句。 以這種方式創建的函數的函數體,必須始終包在花括號中,即使它僅包含一個語句。
一個函數可以包含多個參數,也可以不含參數。在下面的例子中,makeNoise函數中沒有包含任何參數,而power則使用了兩個參數:
var makeNoise = function() { console.log("Pling!"); }; makeNoise(); // → Pling! const power = function(base, exponent) { let result = 1; for (let count = 0; count < exponent; count++) { result *= base; } return result; }; console.log(power(2, 10)); // → 1024
有些函數會產生一個值,比如power和square,有些函數不會,比如makeNoise,它的唯一結果是副作用。 return語句決定函數返回的值。 當控制流遇到這樣的語句時,它立即跳出當前函數并將返回的值賦給調用該函數的代碼。 不帶表達式的return關鍵字,會導致函數返回undefined。 沒有return語句的函數,比如makeNoise,同樣返回undefined。
函數的參數行為與常規綁定相似,但它們的初始值由函數的調用者提供,而不是函數本身的代碼。
綁定和作用域每個綁定都有一個作用域,它是程序的一部分,其中綁定是可見的。 對于在任何函數或塊之外定義的綁定,作用域是整個程序 - 您可以在任何地方引用這種綁定。它們被稱為全局的。
但是為函數參數創建的,或在函數內部聲明的綁定,只能在該函數中引用,所以它們被稱為局部綁定。 每次調用該函數時,都會創建這些綁定的新實例。 這提供了函數之間的一些隔離 - 每個函數調用,都在它自己的小世界(它的局部環境)中運行,并且通??梢栽诓恢廊汁h境中發生的事情的情況下理解。
用let和const聲明的綁定,實際上是它們的聲明所在的塊的局部對象,所以如果你在循環中創建了一個,那么循環之前和之后的代碼就不能“看見”它。JavaScript 2015 之前,只有函數創建新的作用域,因此,使用var關鍵字創建的舊式綁定,在它們出現的整個函數中內都可見,或者如果它們不在函數中,在全局作用域可見。
let x = 10; if (true) { let y = 20; var z = 30; console.log(x + y + z); // → 60 } // y is not visible here console.log(x + z); // → 40
每個作用域都可以“向外查看”它周圍的作用域,所以示例中的塊內可以看到x。 當多個綁定具有相同名稱時例外 - 在這種情況下,代碼只能看到最內層的那個。 例如,當halve函數中的代碼引用n時,它看到它自己的n,而不是全局的n。
const halve = function(n) { return n / 2; } let n = 10; console.log(halve(100)); // → 50 console.log(n); // → 10嵌套作用域
JavaScript 不僅區分全局和局部綁定。 塊和函數可以在其他塊和函數內部創建,產生多層局部環境。
例如,這個函數(輸出制作一批鷹嘴豆泥所需的配料)的內部有另一個函數:
const hummus = function(factor) { const ingredient = function(amount, unit, name) { let ingredientAmount = amount * factor; if (ingredientAmount > 1) { unit += "s"; } console.log(`${ingredientAmount} ${unit} ${name}`); }; ingredient(1, "can", "chickpeas"); ingredient(0.25, "cup", "tahini"); ingredient(0.25, "cup", "lemon juice"); ingredient(1, "clove", "garlic"); ingredient(2, "tablespoon", "olive oil"); ingredient(0.5, "teaspoon", "cumin"); };
ingredient函數中的代碼,可以從外部函數中看到factor綁定。 但是它的局部綁定,比如unit或ingredientAmount,在外層函數中是不可見的。
簡而言之,每個局部作用域也可以看到所有包含它的局部作用域。 塊內可見的綁定集,由這個塊在程序文本中的位置決定。 每個局部作用域也可以看到包含它的所有局部作用域,并且所有作用域都可以看到全局作用域。 這種綁定可見性方法稱為詞法作用域。
作為值的函數函數綁定通常只充當程序特定部分的名稱。 這樣的綁定被定義一次,永遠不會改變。 這使得容易混淆函數和名稱。
let launchMissiles = function(value) { missileSystem.launch("now"); }; if (safeMode) { launchMissiles = function() {/* do nothing */}; }
在第 5 章中,我們將會討論一些高級功能:將函數類型的值傳遞給其他函數。
符號聲明創建函數綁定的方法稍短。 當在語句開頭使用function關鍵字時,它的工作方式不同。
function square(x) { return x * x; }
這是函數聲明。 該語句定義了綁定square并將其指向給定的函數。 寫起來稍微容易一些,并且在函數之后不需要分號。
這種形式的函數定義有一個微妙之處。
console.log("The future says:", future()); function future() { return "You"ll never have flying cars"; }
前面的代碼可以執行,即使在函數定義在使用它的代碼下面。 函數聲明不是常規的從上到下的控制流的一部分。 在概念上,它們移到了其作用域的頂部,并可被該作用域內的所有代碼使用。 這有時是有用的,因為它以一種看似有意義的方式,提供了對代碼進行排序的自由,而無需擔心在使用之前必須定義所有函數。
箭頭函數函數的第三個符號與其他函數看起來有很大不同。 它不使用function關鍵字,而是使用由等號和大于號組成的箭頭(=>)(不要與大于等于運算符混淆,該運算符寫做>=)。
const power = (base, exponent) => { let result = 1; for (let count = 0; count < exponent; count++) { result *= base; } return result; };
箭頭出現在參數列表后面,然后是函數的主體。 它表達了一些東西,類似“這個輸入(參數)產生這個結果(主體)”。
如果只有一個參數名稱,則可以省略參數列表周圍的括號。 如果主體是單個表達式,而不是大括號中的塊,則表達式將從函數返回。 所以這兩個square的定義是一樣的:
const square1 = (x) => { return x * x; }; const square2 = x => x * x;
當一個箭頭函數沒有參數時,它的參數列表只是一組空括號。
const horn = () => { console.log("Toot"); };
在語言中沒有很好的理由,同時擁有箭頭函數和函數表達式。 除了我們將在第 6 章中討論的一個小細節外,他們實現相同的東西。 在 2015 年增加了箭頭函數,主要是為了能夠以簡短的方式編寫小函數表達式。 我們將在第 5 章中使用它們。
調用棧控制流經過函數的方式有點復雜。 讓我們仔細看看它。 這是一個簡單的程序,它執行了一些函數調用:
function greet(who) { console.log("Hello " + who); } greet("Harry"); console.log("Bye");
這個程序的執行大致是這樣的:對greet的調用使控制流跳轉到該函數的開始(第 2 行)。 該函數調用控制臺的console.log來完成它的工作,然后將控制流返回到第 2 行。 它到達greet函數的末尾,所以它返回到調用它的地方,這是第 4 行。 之后的一行再次調用console.log。 之后,程序結束。
我們可以使用下圖表示出控制流:
not in function in greet in console.log in greet not in function in console.log not in function
由于函數在返回時必須跳回調用它的地方,因此計算機必須記住調用發生處上下文。 在一種情況下,console.log完成后必須返回greet函數。 在另一種情況下,它返回到程序的結尾。
計算機存儲此上下文的地方是調用棧。 每次調用函數時,當前上下文都存儲在此棧的頂部。 當函數返回時,它會從棧中刪除頂部上下文,并使用該上下文繼續執行。
存儲這個棧需要計算機內存中的空間。 當棧變得太大時,計算機將失敗,并顯示“棧空間不足”或“遞歸太多”等消息。 下面的代碼通過向計算機提出一個非常困難的問題來說明這一點,這個問題會導致兩個函數之間的無限的來回調用。 相反,如果計算機有無限的棧,它將會是無限的。 事實上,我們將耗盡空間,或者“把棧頂破”。
function chicken() { return egg(); } function egg() { return chicken(); } console.log(chicken() + " came first."); // → ??可選參數
下面的代碼可以正常執行:
function square(x) { return x * x; } console.log(square(4, true, "hedgehog")); // → 16
我們定義了square,只帶有一個參數。 然而,當我們使用三個參數調用它時,語言并不會報錯。 它會忽略額外的參數并計算第一個參數的平方。
JavaScript 對傳入函數的參數數量幾乎不做任何限制。如果你傳遞了過多參數,多余的參數就會被忽略掉,而如果你傳遞的參數過少,遺漏的參數將會被賦值成undefined。
該特性的缺點是你可能恰好向函數傳遞了錯誤數量的參數,但沒有人會告訴你這個錯誤。
優點是這種行為可以用于使用不同數量的參數調用一個函數。 例如,這個minus函數試圖通過作用于一個或兩個參數,來模仿-運算符:
function minus(a, b) { if (b === undefined) return -a; else return a - b; } console.log(minus(10)); // → -10 console.log(minus(10, 5)); // → 5
如果你在一個參數后面寫了一個=運算符,然后是一個表達式,那么當沒有提供它時,該表達式的值將會替換該參數。
例如,這個版本的power使其第二個參數是可選的。 如果你沒有提供或傳遞undefined,它將默認為 2,函數的行為就像square。
function power(base, exponent = 2) { let result = 1; for (let count = 0; count < exponent; count++) { result *= base; } return result; } console.log(power(4)); // → 16 console.log(power(2, 6)); // → 64
在下一章當中,我們將會了解如何獲取傳遞給函數的整個參數列表。我們可以借助于這種特性來實現函數接收任意數量的參數。比如console.log就利用了這種特性,它可以用來輸出所有傳遞給它的值。
console.log("C", "O", 2); // → C O 2閉包
函數可以作為值使用,而且其局部綁定會在每次函數調用時重新創建,由此引出一個值得我們探討的問題:如果函數已經執行結束,那么這些由函數創建的局部綁定會如何處理呢?
下面的示例代碼展示了這種情況。代碼中定義了函數wrapValue,該函數創建了一個局部綁定localVariable,并返回一個函數,用于訪問并返回局部綁定localVariable。
function wrapValue(n) { let local = n; return () => local; } let wrap1 = wrapValue(1); let wrap2 = wrapValue(2); console.log(wrap1()); // → 1 console.log(wrap2()); // → 2
這是允許的并且按照您的希望運行 - 綁定的兩個實例仍然可以訪問。 這種情況很好地證明了一個事實,每次調用都會重新創建局部綁定,而且不同的調用不能覆蓋彼此的局部綁定。
這種特性(可以引用封閉作用域中的局部綁定的特定實例)稱為閉包。 引用來自周圍的局部作用域的綁定的函數稱為(一個)閉包。 這種行為不僅可以讓您免于擔心綁定的生命周期,而且還可以以創造性的方式使用函數值。
我們對上面那個例子稍加修改,就可以創建一個可以乘以任意數字的函數。
function multiplier(factor) { return number => number * factor; } let twice = multiplier(2); console.log(twice(5)); // → 10
由于參數本身就是一個局部綁定,所以wrapValue示例中顯式的local綁定并不是真的需要。
考慮這樣的程序需要一些實踐。 一個好的心智模型是,將函數值看作值,包含他們主體中的代碼和它們的創建環境。 被調用時,函數體會看到它的創建環境,而不是它的調用環境。
這個例子調用multiplier并創建一個環境,其中factor參數綁定了 2。 它返回的函數值,存儲在twice中,會記住這個環境。 所以當它被調用時,它將它的參數乘以 2。
遞歸一個函數調用自己是完全可以的,只要它沒有經常這樣做以致溢出棧。 調用自己的函數被稱為遞歸函數。 遞歸允許一些函數以不同的風格編寫。 舉個例子,這是power的替代實現:
function power(base, exponent) { if (exponent == 0) { return 1; } else { return base * power(base, exponent - 1); } } console.log(power(2, 3)); // → 8
這與數學家定義冪運算的方式非常接近,并且可以比循環變體將該概念描述得更清楚。 該函數以更小的指數多次調用自己以實現重復的乘法。
但是這個實現有一個問題:在典型的 JavaScript 實現中,它大約比循環版本慢三倍。 通過簡單循環來運行,通常比多次調用函數開銷低。
速度與優雅的困境是一個有趣的問題。 您可以將其視為人性化和機器友好性之間的權衡。 幾乎所有的程序都可以通過更大更復雜的方式加速。 程序員必須達到適當的平衡。
在power函數的情況下,不雅的(循環)版本仍然非常簡單易讀。 用遞歸版本替換它沒有什么意義。 然而,通常情況下,一個程序處理相當復雜的概念,為了讓程序更直接,放棄一些效率是有幫助的。
擔心效率可能會令人分心。 這又是另一個讓程序設計變復雜的因素,當你做了一件已經很困難的事情時,擔心的額外事情可能會癱瘓。
因此,總是先寫一些正確且容易理解的東西。 如果您擔心速度太慢 - 通常不是這樣,因為大多數代碼的執行不足以花費大量時間 - 您可以事后進行測量并在必要時進行改進。
遞歸并不總是循環的低效率替代方法。 遞歸比循環更容易解決解決一些問題。 這些問題通常是需要探索或處理幾個“分支”的問題,每個“分支”可能再次派生為更多的分支。
考慮這個難題:從數字 1 開始,反復加 5 或乘 3,就可以產生無限數量的新數字。 你會如何編寫一個函數,給定一個數字,它試圖找出產生這個數字的,這種加法和乘法的序列?
例如,數字 13 可以通過先乘 3 然后再加 5 兩次來到達,而數字 15 根本無法到達。
使用遞歸編碼的解決方案如下所示:
function findSolution(target) { function find(current, history) { if (current == target) { return history; } else if (current > target) { return null; } else { return find(current + 5, `(${history} + 5)`) || find(current * 3, `(${history} * 3)`); } } return find(1, "1"); } console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)
需要注意的是該程序并不需要找出最短運算序列,只需要找出任何一個滿足要求的序列即可。
如果你沒有看到它的工作原理,那也沒關系。 讓我們瀏覽它,因為它是遞歸思維的很好的練習。
內層函數find進行實際的遞歸。 它有兩個參數:當前數字和記錄我們如何到達這個數字的字符串。 如果找到解決方案,它會返回一個字符串,顯示如何到達目標。 如果從這個數字開始找不到解決方案,則返回null。
為此,該函數執行三個操作之一。 如果當前數字是目標數字,則當前歷史記錄是到達目標的一種方式,因此將其返回。 如果當前的數字大于目標,則進一步探索該分支是沒有意義的,因為加法和乘法只會使數字變大,所以它返回null。 最后,如果我們仍然低于目標數字,函數會嘗試從當前數字開始的兩個可能路徑,通過調用它自己兩次,一次是加法,一次是乘法。 如果第一次調用返回非null的東西,則返回它。 否則,返回第二個調用,無論它產生字符串還是null。
為了更好地理解函數執行過程,讓我們來看一下搜索數字 13 時,find函數的調用情況:
find(1, "1") find(6, "(1 + 5)") find(11, "((1 + 5) + 5)") find(16, "(((1 + 5) + 5) + 5)") too big find(33, "(((1 + 5) + 5) * 3)") too big find(18, "((1 + 5) * 3)") too big find(3, "(1 * 3)") find(8, "((1 * 3) + 5)") find(13, "(((1 * 3) + 5) + 5)") found!
縮進表示調用棧的深度。 第一次調用find時,它首先調用自己來探索以(1 + 5)開始的解決方案。 這一調用將進一步遞歸,來探索每個后續的解,它產生小于或等于目標數字。 由于它沒有找到一個命中目標的解,所以它向第一個調用返回null。 那里的||操作符會使探索(1 * 3)的調用發生。 這個搜索的運氣更好 - 它的第一次遞歸調用,通過另一個遞歸調用,命中了目標數字。 最內層的調用返回一個字符串,并且中間調用中的每個“||”運算符都會傳遞該字符串,最終返回解決方案。
添加新函數這里有兩種常用的方法,將函數引入到程序中。
首先是你發現自己寫了很多次非常相似的代碼。 我們最好不要這樣做。 擁有更多的代碼,意味著更多的錯誤空間,并且想要了解程序的人閱讀更多資料。 所以我們選取重復的功能,為它找到一個好名字,并把它放到一個函數中。
第二種方法是,你發現你需要一些你還沒有寫的功能,這聽起來像是它應該有自己的函數。 您將首先命名該函數,然后您將編寫它的主體。 在實際定義函數本身之前,您甚至可能會開始編寫使用該函數的代碼。
給函數起名的難易程度取決于我們封裝的函數的用途是否明確。對此,我們一起來看一個例子。
我們想編寫一個打印兩個數字的程序,第一個數字是農場中牛的數量,第二個數字是農場中雞的數量,并在數字后面跟上Cows和Chickens用以說明,并且在兩個數字前填充 0,以使得每個數字總是由三位數字組成。
007 Cows 011 Chickens
這需要兩個參數的函數 - 牛的數量和雞的數量。 讓我們來編程。
function printFarmInventory(cows, chickens) { let cowString = String(cows); while (cowString.length < 3) { cowString = "0" + cowString; } console.log(`${cowString} Cows`); let chickenString = String(chickens); while (chickenString.length < 3) { chickenString = "0" + chickenString; } console.log(`${chickenString} Chickens`); } printFarmInventory(7, 11);
在字符串表達式后面寫.length會給我們這個字符串的長度。 因此,while循環在數字字符串前面加上零,直到它們至少有三個字符的長度。
任務完成! 但就在我們即將向農民發送代碼(連同大量發票)時,她打電話告訴我們,她也開始飼養豬,我們是否可以擴展軟件來打印豬的數量?
當然沒有問題。但是當再次復制粘貼這四行代碼的時候,我們停了下來并重新思考。一定還有更好的方案來解決我們的問題。以下是第一種嘗試:
function printZeroPaddedWithLabel(number, label) { let numberString = String(number); while (numberString.length < 3) { numberString = "0" + numberString; } console.log(`${numberString} ${label}`); } function printFarmInventory(cows, chickens, pigs) { printZeroPaddedWithLabel(cows, "Cows"); printZeroPaddedWithLabel(chickens, "Chickens"); printZeroPaddedWithLabel(pigs, "Pigs"); } printFarmInventory(7, 11, 3);
這種方法解決了我們的問題!但是printZeroPaddedWithLabel這個函數并不十分恰當。它把三個操作,即打印信息、數字補零和添加標簽放到了一個函數中處理。
這一次,我們不再將程序當中重復的代碼提取成一個函數,而只是提取其中一項操作。
function zeroPad(number, width) { let string = String(number); while (string.length < width) { string = "0" + string; } return string; } function printFarmInventory(cows, chickens, pigs) { console.log(`${zeroPad(cows, 3)} Cows`); console.log(`${zeroPad(chickens, 3)} Chickens`); console.log(`${zeroPad(pigs, 3)} Pigs`); } printFarmInventory(7, 16, 3);
名為zeroPad的函數具有很好的名稱,使讀取代碼的人更容易弄清它的功能。 而且這樣的函數在更多的情況下是有用的,不僅僅是這個特定程序。 例如,您可以使用它來幫助打印精確對齊的數字表格。
我們的函數應該包括多少功能呢?我們可以編寫一個非常簡單的函數,只支持將數字擴展成 3 字符寬。也可以編寫一個復雜通用的數字格式化系統,可以處理分數、負數、小數點對齊和使用不同字符填充等。
一個實用原則是不要故作聰明,除非你確定你會需要它。 為你遇到的每一個功能編寫通用“框架”是很誘人的。 控制住那種沖動。 你不會完成任何真正的工作 - 你只會編寫你永遠不會使用的代碼。
函數及其副作用我們可以將函數分成兩類:一類調用后產生副作用,而另一類則產生返回值(當然我們也可以定義同時產生副作用和返回值的函數)。
在農場案例當中,我們調用第一個輔助函數printZeroPaddedWithLabel來產生副作用,打印一行文本信息。而在第二個版本中有一個zeroPad函數,我們調用它來產生返回值。第二個函數比第一個函數的應用場景更加廣泛,這并非偶然。相比于直接產生副作用的函數,產生返回值的函數則更容易集成到新的環境當中使用。
純函數是一種特定類型的,生成值的函數,它不僅沒有副作用,而且也不依賴其他代碼的副作用,例如,它不讀取值可能會改變的全局綁定。 純函數具有令人愉快的屬性,當用相同的參數調用它時,它總是產生相同的值(并且不會做任何其他操作)。 這種函數的調用,可以由它的返回值代替而不改變代碼的含義。 當你不確定純函數是否正常工作時,你可以通過簡單地調用它來測試它,并且知道如果它在當前上下文中工作,它將在任何上下文中工作。 非純函數往往需要更多的腳手架來測試。
盡管如此,我們也沒有必要覺得非純函數就不好,然后將這類函數從代碼中刪除。副作用常常是非常有用的。比如說,我們不可能去編寫一個純函數版本的console.log,但console.log依然十分實用。而在副作用的幫助下,有些操作則更易、更快實現,因此考慮到運算速度,有時候純函數并不可取。
本章小結本章教你如何編寫自己的函數。 當用作表達式時,function關鍵字可以創建一個函數值。 當作為一個語句使用時,它可以用來聲明一個綁定,并給它一個函數作為它的值。 箭頭函數是另一種創建函數的方式。
// Define f to hold a function value const f = function(a) { console.log(a + 2); }; // Declare g to be a function function g(a, b) { return a * b * 3.5; } // A less verbose function value let h = a => a % 3;
理解函數的一個關鍵方面是理解作用域。 每個塊創建一個新的作用域。 在給定作用域內聲明的參數和綁定是局部的,并且從外部看不到。 用var聲明的綁定行為不同 - 它們最終在最近的函數作用域或全局作用域內。
將程序執行的任務分成不同的功能是有幫助的。 你不必重復自己,函數可以通過將代碼分組成一些具體事物,來組織程序。
習題 最小值前一章介紹了標準函數Math.min,它可以返回參數中的最小值。我們現在可以構建相似的東西。編寫一個函數min,接受兩個參數,并返回其最小值。
// Your code here. console.log(min(0, 10)); // → 0 console.log(min(0, -10)); // → -10遞歸
我們已經看到,%(取余運算符)可以用于判斷一個數是否是偶數,通過使用% 2來檢查它是否被 2 整除。這里有另一種方法來判斷一個數字是偶數還是奇數:
0是偶數
1是奇數
對于其他任何數字N,其奇偶性與N–2相同。
定義對應此描述的遞歸函數isEven。 該函數應該接受一個參數(一個正整數)并返回一個布爾值。
使用 50 與 75 測試該函數。想想如果參數為 –1 會發生什么以及產生相應結果的原因。請你想一個方法來修正該問題。
// Your code here. console.log(isEven(50)); // → true console.log(isEven(75)); // → false console.log(isEven(-1)); // → ??字符計數
你可以通過編寫"string"[N],來從字符串中得到第N個字符或字母。 返回的值將是只包含一個字符的字符串(例如"b")。 第一個字符的位置為零,這會使最后一個字符在string.length - 1。 換句話說,含有兩個字符的字符串的長度為2,其字符的位置為 0 和 1。
編寫一個函數countBs,接受一個字符串參數,并返回一個數字,表示該字符串中有多少個大寫字母"B"。
接著編寫一個函數countChar,和countBs作用一樣,唯一區別是接受第二個參數,指定需要統計的字符(而不僅僅能統計大寫字母"B")。并使用這個新函數重寫函數countBs。
// Your code here. console.log(countBs("BBC")); // → 2 console.log(countChar("kakkerlak", "k")); // → 4
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105026.html
摘要:來源編程精解中文第三版翻譯項目原文譯者飛龍協議自豪地采用谷歌翻譯部分參考了編程精解第版,這是一本關于指導電腦的書。在可控的范圍內編寫程序是編程過程中首要解決的問題。我們可以用中文來描述這些指令將數字存儲在內存地址中的位置。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項目原文:Introduction 譯者:飛龍 協議:CC BY-NC-SA 4.0 自豪地...
摘要:相反,當響應指針事件時,它會調用創建它的代碼提供的回調函數,該函數將處理應用的特定部分?;卣{函數可能會返回另一個回調函數,以便在按下按鈕并且將指針移動到另一個像素時得到通知。它們為組件構造器的數組而提供。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項目原文:Project: A Pixel Art Editor 譯者:飛龍 協議:CC BY-NC-SA 4...
摘要:為了運行包裹的程序,可以將這些值應用于它們。在瀏覽器中,輸出出現在控制臺中。在英文版頁面上運行示例或自己的代碼時,會在示例之后顯示輸出,而不是在瀏覽器的控制臺中顯示。這被稱為條件執行。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項目原文:Program Structure 譯者:飛龍 協議:CC BY-NC-SA 4.0 自豪地采用谷歌翻譯 部分參考了《J...
摘要:在本例中,使用屬性指定鏈接的目標,其中表示超文本鏈接。您應該認為和元數據隱式出現在示例中,即使它們沒有實際顯示在文本中。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項目原文:JavaScript and the Browser 譯者:飛龍 協議:CC BY-NC-SA 4.0 自豪地采用谷歌翻譯 部分參考了《JavaScript 編程精解(第 2 版)》 ...
摘要:來源編程精解中文第三版翻譯項目原文譯者飛龍協議自豪地采用谷歌翻譯部分參考了編程精解第版確定編程語言中的表達式含義的求值器只是另一個程序。若文本不是一個合法程序,解析器應該指出錯誤。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項目原文:Project: A Programming Language 譯者:飛龍 協議:CC BY-NC-SA 4.0 自豪地采用...
閱讀 1081·2021-11-22 14:56
閱讀 1525·2019-08-30 15:55
閱讀 3366·2019-08-30 15:45
閱讀 1659·2019-08-30 13:03
閱讀 2872·2019-08-29 18:47
閱讀 3337·2019-08-29 11:09
閱讀 2645·2019-08-26 18:36
閱讀 2618·2019-08-26 13:55