点击勘误issues (opens new window),哪吒感谢大家的阅读
在JavaScript中另一个基本概念是函数, 它允许你在一个代码块中存储一段用于处理单任务的代码,然后在任何你需要的时候用一个简短的命令来调用,而不是把相同的代码写很多次。 在本文中,我们将探索函数的基本概念,如基本语法、如何定义和调用、范围和参数。
# 我能在哪找到函数?
在 JavaScript中, 你将发现函数无处不在 。事实上, 到目前为止,我们一直在使用函数,只是我们还没很好的讨论它们。然而现在是时候了,让我们开始聊聊函数,并探索它们的语法。
几乎任何时候,只要你使用一个带有一对圆括号()的JavaScript结构,并且你不是在使用一个常见的比如for for循环,while或do…while循环,或者if语句这样的内置语言结构时,那么您就正在使用函数。
# 浏览器内置函数
当我们操作一个字符串的时候,例如:
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
2
3
4
5
6
或者当我们操作一个数组的时候:
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
2
3
4
5
6
或者当我们生成一个随机数时:
var myNumber = Math.random()
// the random() function generates a random
// number between 0 and 1, and returns that
// number
2
3
4
JavaScript有许多内置的函数,可以让您做很多有用的事情,而无需自己编写所有的代码。事实上, 许多你调用(运行或者执行的专业词语)浏览器内置函数时调用的代码并不是使用JavaScript来编写——大多数调用浏览器后台的函数的代码,是使用像C++这样更低级的系统语言编写的,而不是像JavaScript这样的web编程语言。
请记住,这些内置浏览器函数不是核心JavaScript语言的一部分——被定义为浏览器API的一部分,它建立在默认语言之上,以提供更多的功能(请参阅本课程的早期部分以获得更多的描述)。我们将在以后的模块中更详细地使用浏览器API。
# 函数与方法
程序员把函数称为对象方法(method)的一部分。你还不必了解JavaScript中已建构的对象在更深层次上是如何运作的——你可以等到下一小节,我们会教给你有关对象运作方式的一切。在我们继续前进之前,我们需要澄清一些有关方法和函数概念之间可能存在的误会——当你在网络上浏览相关信息的时候,你很可能会碰上这两个术语。
到目前为止我们所使用的内置代码同属于这两种形式:函数和方法。你可以在这里查看内置函数,内置对象以及其相关方法的完整列表。
严格说来,内置浏览器函数并不是函数——它们是方法。这听起来有点可怕和令人困惑,但不要担心 ——函数和方法在很大程度上是可互换的,至少在我们的学习阶段是这样的。
二者区别在于方法是在对象内定义的函数。浏览器内置函数(方法)和变量(称为属性)存储在结构化对象内,以使代码更加高效,易于处理。
# 自定义函数
您在过去的课程中还看到很多定制功能 - 在代码中定义的功能,而不是在浏览器中。每当您看到一个自定义名称后面都带有括号,那么您使用的是自定义函数.
自定义函数: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();
}
}
2
3
4
5
6
7
8
9
该函数在<canvas>
元素中绘制100个随机圆。每次我们想要这样做,我们可以使用这个函数来调用这个功能
draw();
而不是每次我们想重复一遍,都要写出所有的代码。函数可以包含任何您喜欢的代码 - 甚至可以从内部函数调用其他函数。以上函数例如调用random()函数三次,由以下代码定义:
function random(number) {
return Math.floor(Math.random()*number);
}
2
3
我们需要这个函数,因为浏览器的内置Math.random()
函数只生成一个0到1之间的随机十进制数。我们想要一个0到一个指定数字之间的随机整数。
# 调用函数
现在你可能很清楚这一点,但仅仅为了防止……,要在函数定义之后,实际使用它,你必须运行或调用它。这是通过将函数名包含在代码的某个地方,后跟圆括号来完成的。
function myFunction() {
alert('hello');
}
myFunction()
// calls the function once
2
3
4
5
6
# 匿名函数
到目前为止,我们刚刚创建了如下函数:
function myFunction() {
alert('hello');
}
2
3
但是您也可以创建一个没有名称的函数:
function() {
alert('hello');
}
2
3
这个函数叫做匿名函数 — 它没有函数名!
它也不会自己做任何事情。
你通常将匿名函数与事件处理程序一起使用, 例如,如果单击相关按钮,以下操作将在函数内运行代码:
var myButton = document.querySelector('button');
myButton.onclick = function() {
alert('hello');
}
2
3
4
5
上述示例将要求<button>
在页面上提供可用于选择并单击的元素。
您在整个课程中已经看到过这种结构了几次,您将在下一篇文章中了解更多信息并在其中使用。
你还可以将匿名函数分配为变量的值,例如:
var myGreeting = function() {
alert('hello');
}
2
3
现在可以使用以下方式调用此函数:
myGreeting();
有效地给变量一个名字;还可以将该函数分配为多个变量的值,例如:
var anotherGreeting = function() {
alert('hello');
}
2
3
现在可以使用以下任一方法调用此函数
myGreeting();
anotherGreeting();
2
但这只会令人费解,所以不要这样做!创建方法时,最好坚持下列形式:
function myGreeting() {
alert('hello');
}
2
3
您将主要使用匿名函数来运行负载的代码以响应事件触发(如点击按钮) - 使用事件处理程序。 再次,这看起来像这样:
myButton.onclick = function() {
alert('hello');
// I can put as much code
// inside here as I want
}
2
3
4
5
TIP
匿名函数也称为函数表达式。函数表达式与函数声明有一些区别。 函数声明会进行声明提升(declaration hoisting),而函数表达式不会。
# 函数参数
一些函数需要在调用它们时指定参数 ——这些参数值需要放在函数括号内,才能正确地完成其工作。
TIP
Note: 参数有时称为参数(arguments),属性(properties)或甚至属性(attributes)
例如,浏览器的内置Math.random()函数不需要任何参数。当被调用时,它总是返回0到1之间的随机数:
var myNumber = Math.random();
浏览器的内置字符串replace()函数需要两个参数:在主字符串中查找的子字符串,以及用以下替换该字符串的子字符串:
var myText = 'I am a string';
var newString = myText.replace('string', 'sausage');
2
TIP
Note:当您需要指定多个参数时,它们以逗号分隔。
还应该注意,有时参数不是必须的 —— 您不必指定它们。如果没有,该功能一般会采用某种默认行为。作为示例,数组 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'
2
3
4
5
如果没有包含参数来指定加入/分隔符,默认情况下会使用逗号
# 函数作用域和冲突
我们来谈一谈 scope即作用域 — 处理函数时一个非常重要的概念。当你创建一个函数时,函数内定义的变量和其他东西都在它们自己的单独的范围内, 意味着它们被锁在自己独立的隔间中, 不能被函数外的代码访问。
所有函数的最外层被称为全局作用域。 在全局作用域内定义的值可以在任意地方访问。
JavaScript由于各种原因而建立,但主要是由于安全性和组织性。有时您不希望变量可以在代码中的任何地方访问 - 您从其他地方调用的外部脚本可能会开始搞乱您的代码并导致问题,因为它们恰好与代码的其他部分使用了相同的变量名称,造成冲突。这可能是恶意的,或者是偶然的。
例如,假设您有一个HTML文件,它调用两个外部JavaScript文件,并且它们都有一个使用相同名称定义的变量和函数:
<!-- Excerpt from my HTML -->
<script src="first.js"></script>
<script src="second.js"></script>
<script>
greeting();
</script>
2
3
4
5
6
// first.js
let name = 'Chris';
function greeting() {
alert('Hello ' + name + ': welcome to our company.');
}
2
3
4
5
// second.js
let name = 'Zaptec';
function greeting() {
alert('Our company is called ' + name + '.');
}
2
3
4
5
这两个函数都使用 greeting() 形式调用,但是你只能访问到 first.js 文件的greeting()函数(第二个文件被忽视了)。另外,第二次尝试使用 let 关键字定义 name 变量导致了一个错误。