codecamp

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上运行(另见 源代码)。

在函数中保留代码的一部分可以避免这样的问题,并且被认为是最佳实践。

它有点像一个动物园。 狮子,斑马,老虎和企鹅被保存在自己的外壳中,并且只能访问其外壳内的东西 - 与功能范围相同。 如果他们能够进入其他机箱,会出现问题。 最好的情况是,不同的动物在不熟悉的栖息地感觉真的不舒服 - 狮子或老虎会在企鹅的水和冰冷的领域感到可怕。 在最坏的情况下,狮子和老虎可能会尝试吃企鹅!

动物园管理员就像全球范围 - 他或她有钥匙进入每个外壳,补充食物,往往生病的动物等。

主动学习:使用范围

让我们看一个真实的例子来展示范围。

  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.

注意:相同的范围规则不适用于循环(例如 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);
    }

    结论

    本文探讨了函数背后的基本概念,为下一个函数铺平了道路,让我们实际操作,并介绍了构建自己的自定义函数的步骤。

    也可以看看

      循环吧代码
      Build your own function
      温馨提示
      下载编程狮App,免费阅读超1000+编程语言教程
      取消
      确定
      目录
      CSS

      关闭

      MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }