本文为学习笔记,参考Javascript.info,菜鸟等文章编写。
跳转连接
文章目录
JavaScript 简介
本体概述
JavaScript 是一门 跨平台、面向对象 的 脚本语言,最初被创建的目的是“使网页更生动”。
JavaScript 的 作用
- 使网页可交互(例如拥有复杂的动画,可点击的按钮,通俗的菜单等)
- 在服务端执行,例如 Node.js,可以在网页上添加更多功能,不仅仅是下载文件(例如在多台电脑之间的协同合作)
- 可以在任意搭载了 JavaScript 引擎 的设备中执行。
- 这意味着,在浏览器中,JavaScript 可以改变网页(DOM)的外观与样式。同样地,在服务器上,Node.js 中的 JavaScript 可以对浏览器上编写的代码发出的客户端请求做出响应。
JavaScript 引擎 / JavaScript 虚拟机
- V8 —— Chrome、Opera 和 Edge 中的 JavaScript 引擎。
- SpiderMonkey —— Firefox 中的 JavaScript 引擎。
- ……还有其他一些代号,像 “Chakra” 用于 IE,“JavaScriptCore”、“Nitro” 和 “SquirrelFish” 用于 Safari,等等。
现代的 JavaScript 是一种“安全的”编程语言
它不提供对内存或 CPU 的底层访问,因为它最初是为浏览器创建的,不需要这些功能。
为了用户的(信息)安全,在浏览器中的 JavaScript 的能力是受限的。目的是防止恶意网页获取用户私人信息或损害用户数据。
-
网页中的 JavaScript 不能读、写、复制和执行硬盘上的任意文件。它没有直接访问操作系统的功能。
现代浏览器允许 JavaScript 做一些文件相关的操作,但是这个操作是受到限制的。仅当用户做出特定的行为,JavaScript 才能操作这个文件。例如,用户把文件“拖放”到浏览器中,或者通过
<input>
标签选择了文件。有很多与相机/麦克风和其它设备进行交互的方式,但是这些都需要获得用户的明确许可。
-
不同的标签页/窗口之间通常互不了解。有时候,也会有一些联系,例如一个标签页通过 JavaScript 打开的另外一个标签页。但即使在这种情况下,如果两个标签页打开的不是同一个网站(域名、协议或者端口任一不相同的网站),它们都不能相互通信。
这就是所谓的“同源策略”。为了解决“同源策略”问题,两个标签页必须 都 包含一些处理这个问题的特定的 JavaScript 代码,并均允许数据交换。
-
JavaScript 可以轻松地通过互联网与当前页面所在的服务器进行通信。但是从其他网站/域的服务器中接收数据的能力被削弱了。尽管可以,但是需要来自远程服务器的明确协议(在 HTTP header 中)。这也是为了用户的信息安全。
浏览器中 JavaScript 的功能
- 在网页中添加新的 HTML,修改网页已有内容和网页的样式。
- 响应用户的行为,响应鼠标的点击,指针的移动,按键的按动。
- 向远程服务器发送网络请求,下载和上传文件。
- 获取或设置 cookie,向访问者提出问题或发送消息。
- 记住客户端的数据(“本地存储”)。
手册与规范
JavaScript 的标准化组织是 ECMA——这个欧洲信息与通信系统标准化协会提供基于 Javascript 的标准化方案(ECMA 原先是欧洲计算机制造商协会的首字母缩写)。这种标准化版本的 JavaScript 被称作 ECMAScript,在所有支持该标准的应用中以相同的方式工作。公司可以使用开放标准语言来开发他们自己的 JavaScript 实现版本。ECMAScript 标准在 ECMA-262 规范中进行文档化。
ECMA-262 规范 包含了大部分深入的、详细的、规范化的关于 JavaScript 的信息。这份规范明确地定义了这门语言。
但正因其规范化,对于新手来说难以理解。所以,如果你需要关于这门语言细节最权威的信息来源,这份规范就很适合你(去阅读)。但它并不适合日常使用。
每年都会发布一个新版本的规范。最新的规范草案请见 https://tc39.es/ecma262/。
想了解最新最前沿的功能,包括“即将纳入规范的”(所谓的 “stage 3”),请看这里的提案 https://github.com/tc39/proposals。
MDN(Mozilla)JavaScript 索引 是一个带有用例和其他信息的主要的手册。它是一个获取关于个别语言函数、方法等深入信息的很好的信息来源。JavaScript 参考 - JavaScript | MDN (mozilla.org)
开发者控制台
为了发现错误并获得一些与脚本相关且有用的信息,浏览器内置了“开发者工具”。
通常,开发者倾向于使用 Chrome 或 Firefox 进行开发,因为它们有最好的开发者工具。一些其它的浏览器也提供开发者工具,有时还具有一些特殊的功能,通常它们都是在“追赶” Chrome 或 Firefox。
在浏览器中按下 F12 键打开开发者工具(如果使用 Mac,按Cmd+Opt+J)
在错误信息的下方,有个 >
标志。它代表“命令行”,在“命令行”中,我们可以输入 JavaScript 命令,按下 Enter 来执行。
JavaScript 基础知识
"script” 标签
我们几乎可以使用 <script>
标签将 JavaScript 程序插入到 HTML 文档的任何位置,当浏览器遇到 <script>
标签,代码会自动运行。
外部脚本
如果你有大量的 JavaScript 代码,我们可以将它放入一个单独的文件。
脚本文件可以通过 src
特性(attribute)添加到 HTML 文件中。
<script src="/path/to/script.js"></script>
这里,/path/to/script.js
是脚本文件从网站根目录开始的绝对路径。当然也可以提供当前页面的相对路径。例如,src ="script.js"
,就像 src="./script.js"
,表示当前文件夹中的 "script.js"
文件。
也可以提供一个完整的 URL 地址
一般来说,只有最简单的脚本才嵌入到 HTML 中。更复杂的脚本存放在单独的文件中。
使用独立文件的好处是浏览器会下载它,然后将它保存到浏览器的 缓存 中。
之后,其他页面想要相同的脚本就会从缓存中获取,而不是下载它。所以文件实际上只会下载一次。
这可以节省流量,并使得页面(加载)更快。
如果设置了 src
特性,script
标签内容将会被忽略。一个单独的 <script>
标签不能同时有 src
特性和内部包裹的代码。必须进行选择,要么使用外部的 <script src="…">
,要么使用正常包裹代码的 <script>
。
代码结构
语句
语句是执行行为(action)的语法结构和命令。
分号
我们可以在代码中编写任意数量的语句。语句之间可以使用分号进行分割。
当存在换行符(line break)时,在大多数情况下可以省略分号。JavaScript 将换行符理解成“隐式”的分号。这也被称为 自动分号插入。
存在 JavaScript 无法确定是否真的需要自动插入分号的情况,这种情况下发生的错误是很难被找到和解决。
即使语句被换行符分隔了,我们依然建议在它们之间加分号。这个规则被社区广泛采用。大部分时候可以省略分号,但是最好不要省略分号。
注释
单行注释以两个正斜杠字符 //
开始。
多行注释以一个正斜杠和星号开始 /*
并以一个星号和正斜杠结束 */
。
现代模式,“use strict”
长久以来,JavaScript 不断向前发展且并未带来任何兼容性问题。新的特性被加入,旧的功能也没有改变。
这么做有利于兼容旧代码,但缺点是 JavaScript 创造者的任何错误或不完善的决定也将永远被保留在 JavaScript 语言中。
这种情况一直持续到 2009 年 ECMAScript 5 (ES5) 的出现。ES5 规范增加了新的语言特性并且修改了一些已经存在的特性。为了保证旧的功能能够使用,大部分的修改是默认不生效的。你需要一个特殊的指令 —— "use strict"
来明确地激活这些特性。
"use strict"
或者 'use strict'
处于脚本文件的顶部时,则整个脚本文件都将以“现代”模式进行工作。
"use strict";
// 代码以现代模式工作
...
请确保
"use strict"
出现在脚本的最顶部,否则严格模式可能无法启用。
没有类似于
"no use strict"
这样的指令可以使程序返回默认模式。进入了严格模式之后没有办法取消。
当你使用 开发者控制台 运行代码时,请注意它默认是不启动 use strict
的。
有时,当 use strict
会对代码产生一些影响时,你会得到错误的结果。
在控制台中启用
use strict
首先,你可以尝试搭配使用 Shift+Enter 按键去输入多行代码,然后将
use strict
放在代码最顶部,就像这样:'use strict'; <Shift+Enter 换行> // ...你的代码 <按下 Enter 以运行>
在大部分浏览器中都有效,像 Firefox 和 Chrome。
如果依然不行,例如你使用的是旧版本的浏览器,那么有一种很丑但可靠的启用
use strict
的方法。将你的代码放在这样的包装器中:(function() { 'use strict'; // ...你的代码... })()
现代 JavaScript 支持 “class” 和 “module” —— 高级语言结构(本教程后续章节会讲到),它们会自动启用 use strict
。因此,如果我们使用它们,则无需添加 "use strict"
指令。
当代码全都写在了 class 和 module 中时,则可以将 "use strict";
这行代码省略掉。
变量
变量 是数据的“命名存储”。
变量声明
在 JavaScript 中创建一个变量,我们需要用到 let
关键字。
let message; // 定义变量
let message = 'Hello!'; // 定义变量,并且赋值
let user = 'John', age = 25, message = 'Hello';
当值改变的时候,之前的数据就被从变量中删除了
一个变量应该只被声明一次。对同一个变量进行重复声明会触发 error。
变量命名
JavaScript 的变量命名有两个限制:
- 变量名称必须仅包含字母、数字、符号
$
和_
。 - 首字符必须非数字。
如果命名包括多个单词,通常采用驼峰式命名法(camelCase)。也就是,单词一个接一个,除了第一个单词,其他的每个单词都以大写字母开头:myVeryLongName
。
保留字
保留字列表 这张表中的保留字无法用作变量命名,因为它们被用于编程语言本身。
常量
声明一个常数(不变)变量,可以使用 const
而非 let
:
const day = '18.04.1982';
在较老的脚本中,存在另一个关键字 var
var
声明与 let
相似。大部分情况下,我们可以用 let
代替 var
或者 var
代替 let
,都能达到预期的效果
“var” 没有块级作用域
用 var
声明的变量在代码块外也是可见的
if (true) {
var test1 = true; // 使用 "var" 而不是 "let"
let test2 = true; // 使用 "let"
}
alert(test1); // true,变量在 if 结束后仍存在
alert(test2); // ReferenceError: test is not defined 变量在 if 结束后不存在
“var” 允许重新声明
使用 var
,我们可以重复声明一个变量,不管多少次都行。如果我们对一个已经声明的变量使用 var
,这条新的声明语句会被忽略
“var” 声明的变量,可以在其声明语句前被使用
当函数开始的时候,就会处理 var
声明(脚本启动对应全局变量)。
换言之,var
声明的变量会在函数开头被定义,与它在代码中定义的位置无关(这里不考虑定义在嵌套函数中的情况)。
数据类型
JavaScript 中的值都具有特定的类型。
在 JavaScript 中有 8 种基本的数据类型(译注:7 种原始类型和 1 种引用类型)。
我们可以将任何类型的值存入变量。(例如,一个变量可以在前一刻是个字符串,下一刻就存储一个数字)
Number 类型
数字可以有很多操作,比如,乘法 *
、除法 /
、加法 +
、减法 -
等等。
除了常规的数字,还包括所谓的“特殊数值(“special numeric values”)”也属于这种类型:Infinity
、-Infinity
和 NaN
。
Infinity
代表数学概念中的 无穷大 ∞。是一个比任何数字都大的特殊值。我们可以通过除以 0 来得到
NaN
代表一个计算错误。它是一个不正确的或者一个未定义的数学操作所得到的结果,比如运算中包含字符串。NaN是粘性的。任何对 NaN的进一步数学运算都会返回 NaN。
数学运算是安全的
在 JavaScript 中做数学运算是安全的。我们可以做任何事:除以 0,将非数字字符串视为数字,等等。
脚本永远不会因为一个致命的错误(“死亡”)而停止。最坏的情况下,我们会得到
NaN
的结果。
BigInt 类型
在 JavaScript 中,“number” 类型无法安全地表示大于 (253-1)(即 9007199254740991),或小于 -(253-1) 的整数。
可以通过将 n
附加到整数字段的末尾来创建 BigInt
值。
// 尾部的 "n" 表示这是一个 BigInt 类型
const bigInt = 1234567890123456789012345678901234567890n;
BigInt 类型 是最近被添加到 JavaScript 语言中的,可能在较旧的浏览器中存在兼容问题
String 类型
JavaScript 中的字符串必须被括在引号里。
- 双引号:
"Hello"
. - 单引号:
'Hello'
. - 反引号:`Hello`.
双引号和单引号都是“简单”引用,在 JavaScript 中两者几乎没有什么差别。
反引号是 功能扩展 引号。它们允许我们通过将变量和表达式包装在 ${…}
中,来将它们嵌入到字符串中。
let name = "John";
// 嵌入一个变量
alert( `Hello, ${name}!` ); // Hello, John!
// 嵌入一个表达式
alert( `the result is ${1 + 2}` ); // the result is 3
Boolean 类型
boolean 类型仅包含两个值:true
和 false
。
null 值
特殊的 null
值不属于上述任何一种类型。
它构成了一个独立的类型,只包含 null
值
undefined 值
特殊值 undefined
和 null
一样自成类型。
undefined
的含义是 未被赋值
。
如果一个变量已被声明,但未被赋值,那么它的值就是 undefined
从技术上讲,可以显式地将 undefined
赋值给变量,但是不建议这样做。
Object 类型和 Symbol 类型
其他所有的数据类型都被称为“原始类型”,因为它们的值只包含一个单独的内容(字符串、数字或者其他)。相反,object
则用于储存数据集合和更复杂的实体。
symbol
类型用于创建对象的唯一标识符。我们在这里提到 symbol
类型是为了完整性,但我们要在学完 object
类型后再学习它。
typeof 运算符
typeof
运算符返回参数的类型。当我们想要分别处理不同类型值的时候,或者想快速进行数据类型检验时,非常有用。
typeof undefined // "undefined"
typeof 0 // "number"
typeof 10n // "bigint"
typeof true // "boolean"
typeof "foo" // "string"
typeof Symbol("id") // "symbol"
typeof Math // "object" (1)
typeof null // "object" (2)
typeof alert // "function" (3)
额外的说明:
Math
是一个提供数学运算的内建object
。typeof null
的结果为"object"
。这是官方承认的typeof
的错误,这个问题来自于 JavaScript 语言的早期阶段,并为了兼容性而保留了下来。null
绝对不是一个object
。null
有自己的类型,它是一个特殊值。typeof
的行为在这里是错误的。typeof alert
的结果是"function"
,因为alert
在 JavaScript 语言中是一个函数。 JavaScript 语言中没有一个特别的 “function” 类型。函数隶属于object
类型。但是typeof
会对函数区分对待,并返回"function"
。这也是来自于 JavaScript 语言早期的问题。从技术上讲,这种行为是不正确的,但在实际编程中却非常方便。
typeof(x)
语法
typeof
是一个操作符,不是一个函数。这里的括号不是typeof
的一部分。它是数学运算分组的括号。通常,这样的括号里包含的是一个数学表达式,例如
(2 + 2)
,但这里它只包含一个参数(x)
。从语法上讲,它们允许在typeof
运算符和其参数之间不打空格,有些人喜欢这样的风格。
交互
alert
alert
会显示一条信息,并等待用户按下 “OK”。
弹出的这个带有信息的小窗口被称为 模态窗。“modal” 意味着用户不能与页面的其他部分(例如点击其他按钮等)进行交互,直到他们处理完窗口。在上面示例这种情况下 —— 直到用户点击“确定”按钮。
prompt
prompt
函数接收两个参数:
result = prompt(title, [default]);
浏览器会显示一个带有文本消息的模态窗口,还有 input 框和确定/取消按钮。
-
title
显示给用户的文本
-
default
可选的第二个参数,指定 input 框的初始值。
语法中的方括号
[...]
上述语法中
default
周围的方括号表示该参数是可选的,不是必需的。
访问者可以在提示输入栏中输入一些内容,然后按“确定”键。然后我们在 result
中获取该文本。或者他们可以按取消键或按 Esc 键取消输入,然后我们得到 null
作为 result
。
prompt
将返回用户在 input
框内输入的文本,如果用户取消了输入,则返回 null
。
//举例
let age = prompt('How old are you?', 100);
alert(`You are ${age} years old!`); // You are 100 years old!
confirm
语法:
result = confirm(question);
confirm
函数显示一个带有 question
以及确定和取消两个按钮的模态窗口。
点击确定返回 true
,点击取消返回 false
。
//举例
let isBoss = confirm("Are you the boss?");
alert( isBoss ); // 如果“确定”按钮被按下,则显示 true
类型转换
大多数情况下,运算符和函数会自动将赋予它们的值转换为正确的类型。在某些情况下,我们需要将值显式地转换为我们期望的类型。
字符串转换 String(value)
数字型转换 Number(value)
在算术函数和表达式中,会自动进行 number 类型转换。当我们从 string 类型源(如文本表单)中读取一个值,但期望输入一个数字时,通常需要进行显式转换。
值 | 转换number 类型规则 |
---|---|
undefined | NaN |
null | 0 |
true 和 false | 1 and 0 |
string | 去掉首尾空白字符(空格、换行符 \n 、制表符 \t 等)后的纯数字字符串中含有的数字。如果剩余字符串为空,则转换结果为 0 。否则,将会从剩余字符串中“读取”数字。当类型转换出现 error 时返回 NaN 。 |
布尔型转换 Boolean(value)
布尔转换规则:
- 直观上为“空”的值(如
0
、空字符串、null
、undefined
和NaN
)将变为false
。 - 其他值变成
true
。
值的比较
数学运算符:
- 大于 / 小于:
a > b
,a < b
。 - 大于等于 / 小于等于:
a >= b
,a <= b
。 - 检查两个值的相等:
a == b
,请注意双等号==
表示相等性检查,而单等号a = b
表示赋值。 - 检查两个值不相等:不相等在数学中的符号是
≠
,但在 JavaScript 中写成a != b
。
比较运算符返回布尔值:
true
—— 表示“yes(是)”,“correct(正确)”或“the truth(真)”。false
—— 表示“no(否)”,“wrong(错误)”或“not the truth(非真)”。
比较字符串的大小:
JavaScript 会使用“字典(dictionary)”或“词典(lexicographical)”顺序进行判定。换言之,字符串是按字符(母)逐个进行比较的。
- 首先比较两个字符串的首位字符大小。
- 如果一方字符较大(或较小),则该字符串大于(或小于)另一个字符串。算法结束。
- 否则,如果两个字符串的首位字符相等,则继续取出两个字符串各自的后一位字符进行比较。
- 重复上述步骤进行比较,直到比较完成某字符串的所有字符为止。
- 如果两个字符串的字符同时用完,那么则判定它们相等,否则未结束(还有未比较的字符)的字符串更大。
不同类型间的比较:
JavaScript 会首先将其转化为数字(number)再判定大小。
alert( '2' > 1 ); // true,字符串 '2' 会被转化为数字 2
alert( '01' == 1 ); // true,字符串 '01' 会被转化为数字 1
alert( true == 1 ); // true
alert( false == 0 ); // true
严格相等
因为在比较不同类型的值时,处于相等判断符号 ==
两侧的值会先被转化为数字,普通的相等性检查 ==
存在问题(例如不能区分出 0
和 false
)
严格相等运算符 ===
在进行比较时不会做任何的类型转换。
同样的,与“不相等”符号 !=
类似,“严格不相等”表示为 !==
。
对 null 和 undefined 进行比较
当使用严格相等 ===
比较二者时,它们不相等,因为它们属于不同的类型。
当使用非严格相等 ==
比较二者时,JavaScript 存在一个特殊的规则,会判定它们相等。
当使用数学式或其他比较方法 < > <= >=
时:null/undefined
会被转化为数字:null
被转化为 0
,undefined
被转化为 NaN
。
// 奇怪的结果
alert( null > 0 ); // (1) false
alert( null == 0 ); // (2) false
alert( null >= 0 ); // (3) true
//为什么会出现这种反常结果,这是因为相等性检查 == 和普通比较符 > < >= <= 的代码逻辑是相互独立的。进行值的比较时,null 会被转化为数字,因此它被转化为了 0。这就是为什么 null >= 0 返回值是 true,null > 0 返回值是 false。
//另一方面,undefined 和 null 在相等性检查 == 中不会进行任何的类型转换,它们有自己独立的比较规则,所以除了它们之间互等外,不会等于任何其他的值。这就解释了为什么 null == 0 会返回 false。
基础运算符,数学运算
常用术语:
- 运算元 : 运算符应用的对象。
比如说乘法运算5 * 2
,有两个运算元:左运算元5
和右运算元2
。有时候人们也称其为“参数”而不是“运算元”。 - 一元运算符:一个运算符对应的只有一个运算元。
比如说一元负号运算符(unary negation)-
,它的作用是对数字进行正负转换 - 二元运算符:一个运算符拥有两个运算元。
减号还存在二元运算符形式。
JavaScript支持以下数学运算:运算符优先级 - JavaScript | MDN (mozilla.org)
条件分支:if 和 ‘?’
if(...)
语句计算括号里的条件表达式,如果计算结果是 true
,就会执行对应的代码块。
else
是if
语句包含的一个可选块。如果判断条件不成立,就会执行它内部的代码。
else if
子句可以实现一个条件的几个变体。
建议每次使用 if 语句都用大括号 {}
来包装代码块,即使只有一条语句。这样可以提高代码可读性。
if(...){
//....
}
else if(...){
//....
}
else{
//....
}
条件运算符 ‘?’
可以更简短地达到目的。
使用一系列问号 ?
运算符可以返回一个取决于多个条件的值。
let age = prompt('age?', 18);
let message = (age < 3) ? 'Hi, baby!' :
(age < 18) ? 'Hello!' :
(age < 100) ? 'Greetings!' :
'What an unusual age!';
alert( message );
逻辑运算符
JavaScript 中有四个逻辑运算符:||
(或),&&
(与),!
(非),??
(空值合并运算符)
已定义的(defined):一个既不是 null
也不是 undefined
的值。
a ?? b
的结果是:
- 如果
a
是已定义的,则结果为a
, - 如果
a
不是已定义的,则结果为b
。
result = a ?? b
//类似
result = (a !== null && a !== undefined) ? a : b;
使用场景
let user; // let user = "John"; alert(user ?? "匿名"); // 匿名(user 未定义)
或运算符
||
可以以与??
运算符相同的方式使用。let firstName = null; let lastName = null; let nickName = "Supercoder"; // 显示第一个真值: alert(firstName || lastName || nickName || "Anonymous"); // Supercoder
出于安全原因,JavaScript 禁止将 ??
运算符与 &&
和 ||
运算符一起使用,除非使用括号明确指定了优先级。
循环:while 和 for
while 循环
while (condition) {
// 代码
// 所谓的“循环体”
}
do…while
do {
// 循环体
} while (condition);
for( ; ; ) 循环
for (begin; condition; step) {
// ……循环体……
}
for…in 循环
遍历一个对象的所有键(key)
for (key in object) {
// 对此对象属性中的每个键执行的代码
}
let user = {
name: "John",
age: 30,
isAdmin: true
};
for (let key in user) {
// keys
alert( key ); // name, age, isAdmin
// 属性键的值
alert( user[key] ); // John, 30, true
}
for…of 循环
遍历数组,不能获取当前元素的索引,只是获取元素值
let fruits = ["Apple", "Orange", "Plum"];
// 遍历数组元素
for (let fruit of fruits) {
alert( fruit );
}
break / continue
通常条件为假时,循环会终止。但我们随时都可以使用 break
指令强制退出。
continue
指令不会停掉整个循环。而是停止当前这一次迭代,并强制启动新一轮循环(如果条件允许的话)。
禁止
break/continue
在 ‘?’ 的右边请注意非表达式的语法结构不能与三元运算符
?
一起使用。特别是break/continue
这样的指令是不允许这样使用的。
标签
标签 是在循环之前带有冒号的标识符
有时候需要一次从多层嵌套的循环中跳出来。可以使用标签完成这个过程。
labelName: for (...) {
//...
for(...){
//...
break labelName;
}
}
标签并不允许“跳到”代码的任意位置
break label; // 跳转至下面的 label 处(无效) label: for (...)
break
指令必须在代码块内。从技术上讲,任何被标记的代码块都有效。label: { // ... break label; // 有效 // ... }
“switch” 语句
switch
语句可以替代多个 if
判断。
switch
语句为多分支选择的情况提供了一个更具描述性的方式。
switch(x) {
case 'value1': // if (x === 'value1')
...
[break]
case 'value2': // if (x === 'value2')
...
[break]
default:
...
[break]
}
函数
函数是程序的主要“构建模块”。函数使该段代码可以被调用很多次,而不需要写重复的代码。
使用 函数声明 创建函数。
function showMessage() {
alert( 'Hello everyone!' );
}
showMessage();
function
关键字首先出现,然后是 函数名,然后是括号之间的 参数 列表(用逗号分隔,在上述示例中为空,我们将在接下来的示例中看到),最后是花括号之间的代码(即“函数体”)。
局部变量:在函数中声明的变量只在该函数内部可见。
外部变量:函数对外部变量拥有全部的访问权限。函数也可以修改外部变量。
如果在函数内部声明了同名变量,那么函数会 遮蔽 外部变量。
全局变量:任何函数之外声明的变量,都被称为 全局 变量。全局变量在任意函数中都是可见的(除非被局部变量遮蔽)。
减少全局变量的使用是一种很好的做法。现代的代码有很少甚至没有全局变量。大多数变量存在于它们的函数中。但是有时候,全局变量能够用于存储项目级别的数据。
函数表达式 :允许在任何表达式的中间创建一个新函数。
function sayHi() {
alert( "Hello" );
}
let sayHi = function() {
alert( "Hello" );
};
// 上面的两个代码示例的含义是一样的:“创建一个函数并将其放入变量 sayHi 中”。
注意:函数是一个值!
function sayHi() { alert( "Hello" ); } alert( sayHi ); // 显示函数代码 // 显示的内容: //function sayHi() { // alert( "Hello" ); //}
function sayHi() { // (1) 创建 alert( "Hello" ); } let func = sayHi; // (2) 复制 func(); // Hello // (3) 运行复制的值(正常运行)! sayHi(); // Hello // 这里也能运行(为什么不行呢)
回调函数
通过例子理解:
写一个包含三个参数的函数
ask(question, yes, no)
:
question
关于问题的文本
yes
当回答为 “Yes” 时,要运行的脚本
no
当回答为 “No” 时,要运行的脚本
函数需要提出
question
(问题),并根据用户的回答,调用yes()
或no()
:function ask(question, yes, no) { if (confirm(question)) yes() else no(); } function showOk() { alert( "You agreed." ); } function showCancel() { alert( "You canceled the execution." ); } // 用法:函数 showOk 和 showCancel 被作为参数传入到 ask ask("Do you agree?", showOk, showCancel);
ask
的两个参数值 showOk
和 showCancel
可以被称为 回调函数 或简称 回调。
可以使用函数表达式来编写一个等价的、更简洁的函数:
function ask(question, yes, no) { if (confirm(question)) yes() else no(); } ask( "Do you agree?", function() { alert("You agreed."); }, function() { alert("You canceled the execution."); } );
函数表达式 与 函数声明 的区别:
函数表达式是在代码执行到达时被创建,并且仅从那一刻起可用。
在函数声明被定义之前,它就可以被调用。
箭头函数
//语法
let func = (arg1, arg2, ..., argN) => {
expression;
};
//等价于
let func = function(arg1, arg2, ..., argN) {
return expression;
};