Functions — reusable blocks of code

2018-05-15 17:26 更新
先決條件: 基本的計(jì)算機(jī)素養(yǎng),對(duì)HTML和CSS的基本了解, JavaScript第一步。
目的: 了解JavaScript函數(shù)背后的基本概念。

我在哪里找到功能?

在JavaScript中,你會(huì)發(fā)現(xiàn)函數(shù)無處不在。 事實(shí)上,到目前為止,我們一直使用函數(shù)直到整個(gè)過程; 我們只是沒有在說他們非常。 現(xiàn)在是時(shí)候,我們開始明確地談?wù)摵瘮?shù),并真正地探索他們的語法。

幾乎任何時(shí)候,你使用一個(gè)JavaScript結(jié)構(gòu),它具有一對(duì)括號(hào) - () - 并且使用一個(gè)常見的內(nèi)置語言結(jié)構(gòu), a href ="/ en-US / Learn / JavaScript / Building_blocks / Looping_code#The_standard_for_loop"> for循環(huán), "> while或do ... while循環(huán),或 if ... else語句 >,你正在使用一個(gè)函數(shù)。

內(nèi)置瀏覽器功能

我們?cè)诒菊n程中使用了很多內(nèi)置到瀏覽器中的功能。 每次我們處理一個(gè)文本字符串,例如:

var myText = 'I am a string';
var newString = myText.replace('string', 'sausage');
console.log(newString);
// the replace() string function takes a string,
// replaces one substring with another, and returns
// a new string with the replacement made

或者每次我們操縱數(shù)組:

var myArray = ['I', 'love', 'chocolate', 'frogs'];
var madeAString = myArray.join(' ');
console.log(madeAString);
// the join() function takes an array, joins
// all the array items together into a single
// string, and returns this new string

或者每次我們生成一個(gè)隨機(jī)數(shù):

var myNumber = Math.random()
// the random() function generates a random
// number between 0 and 1, and returns that
// number

...我們正在使用一個(gè)函數(shù)!

注意:您可以隨意在瀏覽器的JavaScript控制臺(tái)中輸入這些行,以便在需要時(shí)重新熟悉自己的功能。

JavaScript語言有許多內(nèi)置函數(shù),允許你做有用的事情,而不必自己編寫所有的代碼。 實(shí)際上,當(dāng)您調(diào)用(用于運(yùn)行或執(zhí)行)的內(nèi)置瀏覽器函數(shù)時(shí),您調(diào)用的某些代碼無法用JavaScript編寫 - 其中許多函數(shù)都是調(diào)用零件 的后臺(tái)瀏覽器代碼,這是寫在很大程度上低級(jí)系統(tǒng)語言,如C ++,而不是像JavaScript這樣的Web語言。

請(qǐng)記住,一些內(nèi)置的瀏覽器函數(shù)不是核心JavaScript語言的一部分 - 一些被定義為瀏覽器API的一部分,它建立在默認(rèn)語言之上以提供更多的功能(參考 en-US / Learn / JavaScript / First_steps / What_is_JavaScript#So_what_can_it_really_do">我們課程的這個(gè)早期部分了解更多說明)。 我們將在后面的模塊中更詳細(xì)地討論使用瀏覽器API。

函數(shù)與方法

在我們繼續(xù)之前我們需要清除一件事 - 技術(shù)上來說,內(nèi)置的瀏覽器函數(shù)不是函數(shù) - 它們是方法。 這聽起來有點(diǎn)可怕和混亂,但不要擔(dān)心 - 字的功能和方法在很大程度上是可互換的,至少對(duì)于我們的目的,在這個(gè)階段在你的學(xué)習(xí)。

區(qū)別在于方法是在對(duì)象內(nèi)定義的函數(shù)。 內(nèi)置瀏覽器函數(shù)(方法)和變量(稱為屬性)存儲(chǔ)在結(jié)構(gòu)化對(duì)象中,以使代碼更高效,更易于處理。

你不需要了解結(jié)構(gòu)化JavaScript對(duì)象的內(nèi)部工作 - 你可以等到我們后面的模塊教會(huì)你所有關(guān)于對(duì)象的內(nèi)部工作,以及如何創(chuàng)建自己的。 現(xiàn)在,我們只是想清除方法與功能的任何可能的混淆 - 當(dāng)您查看Web上可用的相關(guān)資源時(shí),您可能會(huì)遇到這兩個(gè)術(shù)語。

自定義功能

在過去的課程中,您還看到了很多自定義函數(shù) - 在代碼中定義的函數(shù),而不是瀏覽器中的函數(shù)。 任何時(shí)候,你看到一個(gè)自定義名稱后面帶括號(hào),你正在使用一個(gè)自定義函數(shù)。 在我們的 random-canvas-circles.html 示例(另請(qǐng)參閱完整的 ="external">源代碼),我們?cè)?a href="/webstart/Building_blocks/Looping_code">循環(huán)文章中添加了一個(gè)自定義 draw() 看起來像這樣:

function draw() {
  ctx.clearRect(0,0,WIDTH,HEIGHT);
  for (var i = 0; i < 100; i++) {
    ctx.beginPath();
    ctx.fillStyle = 'rgba(255,0,0,0.5)';
    ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
    ctx.fill();
  }
}

此函數(shù)在 繪制圖形和動(dòng)畫。"> < canvas> 元素。 每次我們想要這樣做,我們可以調(diào)用這個(gè)函數(shù)

draw();

而不是每次我們想重復(fù)它時(shí)再次寫出所有的代碼。 函數(shù)可以包含任何你喜歡的代碼 - 你甚至可以從函數(shù)內(nèi)部調(diào)用其他函數(shù)。 上述函數(shù)例如調(diào)用 random()函數(shù)三次,它由以下代碼定義:

function random(number) {
  return Math.floor(Math.random()*number);
}

我們需要此功能,因?yàn)闉g覽器的內(nèi)置 Math.random() / a>函數(shù)只生成0和1之間的隨機(jī)十進(jìn)制數(shù)。我們想要一個(gè)介于0和指定數(shù)之間的隨機(jī)整數(shù)。

調(diào)用函數(shù)

你現(xiàn)在可能清楚這一點(diǎn),但是為了防止...在定義后真正使用一個(gè)函數(shù),你必須運(yùn)行或調(diào)用它。 這是通過在代碼中包含函數(shù)的名稱,后跟括號(hào)來完成的。

function myFunction() {
  alert('hello');
}

myFunction()
// calls the function once

匿名函數(shù)

您可能會(huì)看到以稍微不同的方式定義和調(diào)用的函數(shù)。 到目前為止,我們剛剛創(chuàng)建了一個(gè)像這樣的函數(shù):

function myFunction() {
  alert('hello');
}

但是你也可以創(chuàng)建一個(gè)沒有名字的函數(shù):

function() {
  alert('hello');
}

這稱為匿名函數(shù) - 它沒有名稱! 它也不會(huì)自己做任何事情。 你通常使用一個(gè)匿名函數(shù)和一個(gè)事件處理程序,例如下面的代碼將在函數(shù)中運(yùn)行相關(guān)按鈕被點(diǎn)擊時(shí):

var myButton = document.querySelector('button');

myButton.onclick = function() {
  alert('hello');
}

上面的例子要求有一個(gè) "> < button> 元素,以選擇并點(diǎn)擊。 您已經(jīng)在整個(gè)課程中看過這種結(jié)構(gòu)幾次,您將在下一篇文章中了解更多并了解它的使用。

您還可以將匿名函數(shù)指定為變量的值,例如:

var myGreeting = function() {
  alert('hello');
}

此函數(shù)現(xiàn)在可以使用:

myGreeting();

這有效地給變量名稱; 您還可以將函數(shù)分配為多個(gè)變量的值,例如:

var anotherGreeting = function() {
  alert('hello');
}

此函數(shù)現(xiàn)在可以使用

myGreeting();
anotherGreeting();

但這只是混亂,所以不要這樣做! 當(dāng)創(chuàng)建函數(shù)時(shí),最好堅(jiān)持這種形式:

function myGreeting() {
  alert('hello');
}

您將主要使用匿名函數(shù)來運(yùn)行一個(gè)代碼負(fù)載,以響應(yīng)事件觸發(fā)(如點(diǎn)擊按鈕)使用事件處理程序。 同樣,這看起來像這樣:

myButton.onclick = function() {
  alert('hello');
  // I can put as much code
  // inside here as I want
}

功能參數(shù)

某些函數(shù)在調(diào)用它們時(shí)需要指定參數(shù) - 這些是需要包含在函數(shù)括號(hào)內(nèi)的值,它需要正確完成其工作。

注意:參數(shù)有時(shí)稱為參數(shù),屬性,甚至屬性。

例如,瀏覽器的內(nèi)置 Math.random() a>函數(shù)不需要任何參數(shù)。 當(dāng)調(diào)用時(shí),它總是返回一個(gè)0和1之間的隨機(jī)數(shù):

var myNumber = Math.random();

然而,瀏覽器的內(nèi)置字符串 replace()函數(shù)需要 兩個(gè)參數(shù) - 要在主字符串中查找的子字符串,以及用以下代替該字符串的子字符串:

var myText = 'I am a string';
var newString = myText.replace('string', 'sausage');

注意:當(dāng)您需要指定多個(gè)參數(shù)時(shí),它們之間用逗號(hào)分隔。

還應(yīng)該注意,有時(shí)參數(shù)是可選的 - 您不必指定它們。 如果你不這樣做,函數(shù)通常會(huì)采用某種默認(rèn)行為。 例如,數(shù)組 join()函數(shù)的參數(shù)為 可選的:

var myArray = ['I', 'love', 'chocolate', 'frogs'];
var madeAString = myArray.join(' ');
// returns 'I love chocolate frogs'
var madeAString = myArray.join();
// returns 'I,love,chocolate,frogs'

如果不包括用于指定加入/定界字符的參數(shù),則默認(rèn)使用逗號(hào)。

功能范圍和沖突

讓我們來談?wù)?a rel="external nofollow" target="_blank" target="_blank"> 值和表達(dá)式是"可見"或可以引用的上下文。 如果變量或其他表達(dá)式不是"在當(dāng)前作用域中",則它不可用。 范圍也可以在層次結(jié)構(gòu)中分層,以便子范圍可以訪問父范圍,反之亦然。\'scope - 處理函數(shù)時(shí)非常重要的概念。 當(dāng)創(chuàng)建一個(gè)函數(shù)時(shí),函數(shù)內(nèi)部定義的變量和其他東西都在它們自己的范圍內(nèi),這意味著它們被鎖定在它們自己的獨(dú)立區(qū)域中,從其他函數(shù)內(nèi)部無法訪問, 功能。

所有函數(shù)外的頂層稱為全局范圍。 在全局范圍中定義的值可以從代碼中的任何地方訪問。

JavaScript是這樣設(shè)置的各種原因 - 但主要是因?yàn)榘踩徒M織。 有時(shí)你不希望變量可以從代碼中的任何地方訪問 - 外部腳本,你從其他地方調(diào)用可能開始混亂你的代碼,并導(dǎo)致問題,因?yàn)樗麄兣銮墒褂孟嗤淖兞棵Q與代碼的其他部分 ,引起沖突。 這可能是惡意地,或只是偶然。

JavaScript是這樣設(shè)置的各種原因 - 但主要是因?yàn)榘踩徒M織。 有時(shí)你不希望變量可以從代碼中的任何地方訪問 - 外部腳本,你從其他地方調(diào)用可能開始混亂你的代碼,并導(dǎo)致問題,因?yàn)樗麄兣銮墒褂孟嗤淖兞棵Q與代碼的其他部分 ,引起沖突。 這可能是惡意地,或只是偶然。...

<!-- Excerpt from my HTML -->
<script src="first.js"></script>
<script src="second.js"></script>
<script>
  greeting();
</script>
// first.js
var name = 'Chris';
function greeting() {
  alert('Hello ' + name + ': welcome to our company.');
}
// second.js
var name = 'Zaptec';
function greeting() {
  alert('Our company is called ' + name + '.');
}

你想調(diào)用的兩個(gè)函數(shù)都叫 greeting(),但你只能訪問 second.js 文件的 greeting 在源代碼中應(yīng)用于HTML,因此其變量和函數(shù)覆蓋了 first.js 中的變量和函數(shù)。

注意:您可以看到此示例 >在GitHub上運(yùn)行(另見 源代碼)。

在函數(shù)中保留代碼的一部分可以避免這樣的問題,并且被認(rèn)為是最佳實(shí)踐。

它有點(diǎn)像一個(gè)動(dòng)物園。 獅子,斑馬,老虎和企鵝被保存在自己的外殼中,并且只能訪問其外殼內(nèi)的東西 - 與功能范圍相同。 如果他們能夠進(jìn)入其他機(jī)箱,會(huì)出現(xiàn)問題。 最好的情況是,不同的動(dòng)物在不熟悉的棲息地感覺真的不舒服 - 獅子或老虎會(huì)在企鵝的水和冰冷的領(lǐng)域感到可怕。 在最壞的情況下,獅子和老虎可能會(huì)嘗試吃企鵝!

動(dòng)物園管理員就像全球范圍 - 他或她有鑰匙進(jìn)入每個(gè)外殼,補(bǔ)充食物,往往生病的動(dòng)物等。

主動(dòng)學(xué)習(xí):使用范圍

讓我們看一個(gè)真實(shí)的例子來展示范圍。

  1. First, make a local copy of our function-scope.html example. This contains two functions called a() and b(), and three variables — x, y, and z — two of which are defined inside the functions, and one in the global scope. It also contains a third function called output(), which takes a single parameter and outputs it in a paragraph on the page.
  2. Open the example up in a browser and in your text editor.
  3. Open the JavaScript console in your browser developer tools. In the JavaScript console, enter the following command:
    output(x);
    You should see the value of variable x output to the screen.
  4. Now try entering the following in your console
    output(y);
    output(z);
    Both of these should return an error along the lines of "ReferenceError: y is not defined". Why is that? Because of function scope — y and z are locked inside the a() and b() functions, so output() can't access them when called from the global scope.
  5. However, what about when it's called from inside another function? Try editing a() and b() so they look like this:
    function a() {
      var y = 2;
      output(y);
    }
    
    function b() {
      var z = 3;
      output(z);
    }
    Save the code and reload it in your browser, then try calling the a() and b() functions from the JavaScript console:
    a();
    b();
    You should see the y and z values output in the page. This works fine, as the output() function is being called inside the other functions — in the same scope as the variables it is printing are defined in, in each case. output() itself is available from anywhere, as it is defined in the global scope.
  6. Now try updating your code like this:
    function a() {
      var y = 2;
      output(x);
    }
    
    function b() {
      var z = 3;
      output(x);
    }
    Save and reload again, and try this again in your JavaScript console:
    a();
    b();
    Both the a() and b() call should output the value of x — 1. These work fine because even though the output() calls are not in the same scope as x is defined in, x is a global variable so is available inside all code, everywhere.
  7. Finally, try updating your code like this:
    function a() {
      var y = 2;
      output(z);
    }
    
    function b() {
      var z = 3;
      output(y);
    }
    Save and reload again, and try this again in your JavaScript console:
    a();
    b();
    This time the a() and b() calls will both return that annoying "ReferenceError: z is not defined" error — this is because the output() calls and the variables they are trying to print are not defined inside the same function scopes — the variables are effectively invisible to those function calls.

注意:相同的范圍規(guī)則不適用于循環(huán)(例如 for(){...} )和條件塊(例如 if 。} ) - 他們看起來很相似,但是他們不一樣的東西! 小心不要讓這些困惑。

注意:參考錯(cuò)誤:"x"未定義 錯(cuò)誤是您最常遇到的錯(cuò)誤之一。 如果你得到這個(gè)錯(cuò)誤,并且你確定你有一個(gè)定義的變量,檢查它在什么范圍。

    函數(shù)內(nèi)部函數(shù)

    請(qǐng)記住,你可以從任何地方,甚至在另一個(gè)函數(shù)中調(diào)用一個(gè)函數(shù)。 這通常用來保持代碼整潔 - 如果你有一個(gè)大的復(fù)雜的函數(shù),它是更容易理解,如果你把它分解成幾個(gè)子函數(shù):

    function myBigFunction() {
      var myValue;
    
      subFunction1();
      subFunction2();
      subFunction3();
    }
    
    function subFunction1() {
      console.log(myValue);
    }
    
    function subFunction2() {
      console.log(myValue);
    }
    
    function subFunction3() {
      console.log(myValue);
    }
    

    只需確保在函數(shù)內(nèi)部使用的值正確的范圍。 上面的例子會(huì)拋出一個(gè)錯(cuò)誤 ReferenceError:MyValue未定義,因?yàn)殡m然 myValue 變量在與函數(shù)調(diào)用相同的作用域中定義, 函數(shù)定義 - 調(diào)用函數(shù)時(shí)運(yùn)行的實(shí)際代碼。 要使這個(gè)工作,你必須傳遞的值作為參數(shù)的函數(shù),像這樣:

    function myBigFunction() {
      var myValue = 1;
          
      subFunction1(myValue);
      subFunction2(myValue);
      subFunction3(myValue);
    }
    
    function subFunction1(value) {
      console.log(value);
    }
    
    function subFunction2(value) {
      console.log(value);
    }
    
    function subFunction3(value) {
      console.log(value);
    }

    結(jié)論

    本文探討了函數(shù)背后的基本概念,為下一個(gè)函數(shù)鋪平了道路,讓我們實(shí)際操作,并介紹了構(gòu)建自己的自定義函數(shù)的步驟。

    也可以看看

      以上內(nèi)容是否對(duì)您有幫助:
      在線筆記
      App下載
      App下載

      掃描二維碼

      下載編程獅App

      公眾號(hào)
      微信公眾號(hào)

      編程獅公眾號(hào)