Functions — reusable blocks of code
先决条件: | 基本的计算机素养,对HTML和CSS的基本了解, JavaScript第一步。 |
---|---|
目的: | 了解JavaScript函数背后的基本概念。 |
我在哪里找到功能?
在JavaScript中,你会发现函数无处不在。 事实上,到目前为止,我们一直使用函数直到整个过程; 我们只是没有在说他们非常。 现在是时候,我们开始明确地谈论函数,并真正地探索他们的语法。
几乎任何时候,你使用一个JavaScript结构,它具有一对括号 - ()
- 并且不使用一个常见的内置语言结构, a href ="/ en-US / Learn / JavaScript / Building_blocks / Looping_code#The_standard_for_loop"> for循环, "> while或do ... while循环,或 if ... else语句 >,你正在使用一个函数。
内置浏览器功能
我们在本课程中使用了很多内置到浏览器中的功能。 每次我们处理一个文本字符串,例如:
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
或者每次我们操纵数组:
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
或者每次我们生成一个随机数:
var myNumber = Math.random() // the random() function generates a random // number between 0 and 1, and returns that // number
...我们正在使用一个函数!
注意:您可以随意在浏览器的JavaScript控制台中输入这些行,以便在需要时重新熟悉自己的功能。
JavaScript语言有许多内置函数,允许你做有用的事情,而不必自己编写所有的代码。 实际上,当您调用(用于运行或执行)的内置浏览器函数时,您调用的某些代码无法用JavaScript编写 - 其中许多函数都是调用零件 的后台浏览器代码,这是写在很大程度上低级系统语言,如C ++,而不是像JavaScript这样的Web语言。
请记住,一些内置的浏览器函数不是核心JavaScript语言的一部分 - 一些被定义为浏览器API的一部分,它建立在默认语言之上以提供更多的功能(参考 en-US / Learn / JavaScript / First_steps / What_is_JavaScript#So_what_can_it_really_do">我们课程的这个早期部分了解更多说明)。 我们将在后面的模块中更详细地讨论使用浏览器API。
函数与方法
在我们继续之前我们需要清除一件事 - 技术上来说,内置的浏览器函数不是函数 - 它们是方法。 这听起来有点可怕和混乱,但不要担心 - 字的功能和方法在很大程度上是可互换的,至少对于我们的目的,在这个阶段在你的学习。
区别在于方法是在对象内定义的函数。 内置浏览器函数(方法)和变量(称为属性)存储在结构化对象中,以使代码更高效,更易于处理。
你不需要了解结构化JavaScript对象的内部工作 - 你可以等到我们后面的模块教会你所有关于对象的内部工作,以及如何创建自己的。 现在,我们只是想清除方法与功能的任何可能的混淆 - 当您查看Web上可用的相关资源时,您可能会遇到这两个术语。
自定义功能
在过去的课程中,您还看到了很多自定义函数 - 在代码中定义的函数,而不是浏览器中的函数。 任何时候,你看到一个自定义名称后面带括号,你正在使用一个自定义函数。 在我们的 random-canvas-circles.html 示例(另请参阅完整的 ="external">源代码),我们在循环文章中添加了一个自定义 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(); } }
此函数在 绘制图形和动画。"> < canvas>
元素。 每次我们想要这样做,我们可以调用这个函数
draw();
而不是每次我们想重复它时再次写出所有的代码。 函数可以包含任何你喜欢的代码 - 你甚至可以从函数内部调用其他函数。 上述函数例如调用 random()
函数三次,它由以下代码定义:
function random(number) { return Math.floor(Math.random()*number); }
我们需要此功能,因为浏览器的内置 Math.random() / a>函数只生成0和1之间的随机十进制数。我们想要一个介于0和指定数之间的随机整数。
调用函数
你现在可能清楚这一点,但是为了防止...在定义后真正使用一个函数,你必须运行或调用它。 这是通过在代码中包含函数的名称,后跟括号来完成的。
function myFunction() { alert('hello'); } myFunction() // calls the function once
匿名函数
您可能会看到以稍微不同的方式定义和调用的函数。 到目前为止,我们刚刚创建了一个像这样的函数:
function myFunction() { alert('hello'); }
但是你也可以创建一个没有名字的函数:
function() { alert('hello'); }
这称为匿名函数 - 它没有名称! 它也不会自己做任何事情。 你通常使用一个匿名函数和一个事件处理程序,例如下面的代码将在函数中运行相关按钮被点击时:
var myButton = document.querySelector('button'); myButton.onclick = function() { alert('hello'); }
上面的例子要求有一个 "> < button>
元素,以选择并点击。 您已经在整个课程中看过这种结构几次,您将在下一篇文章中了解更多并了解它的使用。
您还可以将匿名函数指定为变量的值,例如:
var myGreeting = function() { alert('hello'); }
此函数现在可以使用:
myGreeting();
这有效地给变量名称; 您还可以将函数分配为多个变量的值,例如:
var anotherGreeting = function() { alert('hello'); }
此函数现在可以使用
myGreeting(); anotherGreeting();
但这只是混乱,所以不要这样做! 当创建函数时,最好坚持这种形式:
function myGreeting() { alert('hello'); }
您将主要使用匿名函数来运行一个代码负载,以响应事件触发(如点击按钮)使用事件处理程序。 同样,这看起来像这样:
myButton.onclick = function() { alert('hello'); // I can put as much code // inside here as I want }
功能参数
某些函数在调用它们时需要指定参数 - 这些是需要包含在函数括号内的值,它需要正确完成其工作。
注意:参数有时称为参数,属性,甚至属性。
例如,浏览器的内置 Math.random() a>函数不需要任何参数。 当调用时,它总是返回一个0和1之间的随机数:
var myNumber = Math.random();
然而,浏览器的内置字符串 replace()函数需要 两个参数 - 要在主字符串中查找的子字符串,以及用以下代替该字符串的子字符串:
var myText = 'I am a string'; var newString = myText.replace('string', 'sausage');
注意:当您需要指定多个参数时,它们之间用逗号分隔。
还应该注意,有时参数是可选的 - 您不必指定它们。 如果你不这样做,函数通常会采用某种默认行为。 例如,数组 join()函数的参数为 可选的:
var myArray = ['I', 'love', 'chocolate', 'frogs']; var madeAString = myArray.join(' '); // returns 'I love chocolate frogs' var madeAString = myArray.join(); // returns 'I,love,chocolate,frogs'
如果不包括用于指定加入/定界字符的参数,则默认使用逗号。
功能范围和冲突
让我们来谈谈 值和表达式是"可见"或可以引用的上下文。 如果变量或其他表达式不是"在当前作用域中",则它不可用。 范围也可以在层次结构中分层,以便子范围可以访问父范围,反之亦然。\'scope - 处理函数时非常重要的概念。 当创建一个函数时,函数内部定义的变量和其他东西都在它们自己的范围内,这意味着它们被锁定在它们自己的独立区域中,从其他函数内部无法访问, 功能。
所有函数外的顶层称为全局范围。 在全局范围中定义的值可以从代码中的任何地方访问。
JavaScript是这样设置的各种原因 - 但主要是因为安全和组织。 有时你不希望变量可以从代码中的任何地方访问 - 外部脚本,你从其他地方调用可能开始混乱你的代码,并导致问题,因为他们碰巧使用相同的变量名称与代码的其他部分 ,引起冲突。 这可能是恶意地,或只是偶然。
JavaScript是这样设置的各种原因 - 但主要是因为安全和组织。 有时你不希望变量可以从代码中的任何地方访问 - 外部脚本,你从其他地方调用可能开始混乱你的代码,并导致问题,因为他们碰巧使用相同的变量名称与代码的其他部分 ,引起冲突。 这可能是恶意地,或只是偶然。...
<!-- 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 + '.'); }
你想调用的两个函数都叫 greeting()
,但你只能访问 second.js
文件的 greeting
在源代码中应用于HTML,因此其变量和函数覆盖了 first.js
中的变量和函数。
注意:您可以看到此示例 >在GitHub上运行(另见 源代码)。
在函数中保留代码的一部分可以避免这样的问题,并且被认为是最佳实践。
它有点像一个动物园。 狮子,斑马,老虎和企鹅被保存在自己的外壳中,并且只能访问其外壳内的东西 - 与功能范围相同。 如果他们能够进入其他机箱,会出现问题。 最好的情况是,不同的动物在不熟悉的栖息地感觉真的不舒服 - 狮子或老虎会在企鹅的水和冰冷的领域感到可怕。 在最坏的情况下,狮子和老虎可能会尝试吃企鹅!
动物园管理员就像全球范围 - 他或她有钥匙进入每个外壳,补充食物,往往生病的动物等。
主动学习:使用范围
让我们看一个真实的例子来展示范围。
- First, make a local copy of our function-scope.html example. This contains two functions called
a()
andb()
, and three variables —x
,y
, andz
— two of which are defined inside the functions, and one in the global scope. It also contains a third function calledoutput()
, which takes a single parameter and outputs it in a paragraph on the page. - Open the example up in a browser and in your text editor.
- 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 variablex
output to the screen. - 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
andz
are locked inside thea()
andb()
functions, sooutput()
can't access them when called from the global scope. - However, what about when it's called from inside another function? Try editing
a()
andb()
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 thea()
andb()
functions from the JavaScript console:a(); b();
You should see they
andz
values output in the page. This works fine, as theoutput()
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. - 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 thea()
andb()
call should output the value of x — 1. These work fine because even though theoutput()
calls are not in the same scope asx
is defined in,x
is a global variable so is available inside all code, everywhere. - 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 thea()
andb()
calls will both return that annoying "ReferenceError: z is not defined" error — this is because theoutput()
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.
注意:相同的范围规则不适用于循环(例如 for(){...}
)和条件块(例如 if
。} ) - 他们看起来很相似,但是他们不一样的东西! 小心不要让这些困惑。
注意:参考错误:"x"未定义 错误是您最常遇到的错误之一。 如果你得到这个错误,并且你确定你有一个定义的变量,检查它在什么范围。
函数内部函数
请记住,你可以从任何地方,甚至在另一个函数中调用一个函数。 这通常用来保持代码整洁 - 如果你有一个大的复杂的函数,它是更容易理解,如果你把它分解成几个子函数:
function myBigFunction() { var myValue; subFunction1(); subFunction2(); subFunction3(); } function subFunction1() { console.log(myValue); } function subFunction2() { console.log(myValue); } function subFunction3() { console.log(myValue); }
只需确保在函数内部使用的值正确的范围。 上面的例子会抛出一个错误 ReferenceError:MyValue未定义
,因为虽然 myValue
变量在与函数调用相同的作用域中定义, 函数定义 - 调用函数时运行的实际代码。 要使这个工作,你必须传递的值作为参数的函数,像这样:
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); }
结论
本文探讨了函数背后的基本概念,为下一个函数铺平了道路,让我们实际操作,并介绍了构建自己的自定义函数的步骤。
也可以看看
- Functions detailed guide — covers some advanced features not included here.
- Functions reference
- Default parameters, Arrow functions — advanced concept references