正文
单行正文:
// 单行正文
多行正文:
/*
多行
正文
*/
历史上 JavaScript 能够兼容 HTML 正文,因而 <!--
和 -->
也能够是单行正文。
x = 1; <!-- x = 2;
--> x = 3;
只有 x = 1
会执行,其它局部都被正文掉了。
然而 -->
只有在行首才会被当成正文,否则会当作失常的运算。
function countdown(n) {while (n --> 0) console.log(n);
}
countdown(3)
下面代码中,n --> 0
实际上会当作 n-- > 0
,因而输入 2、1、0。
语句
JavaScript 程序的执行单位为行(line),也就是一行一行地执行。个别状况下,每一行就是一个语句。
语句(statement)是为了实现某种工作而进行的操作,比方上面就是一行赋值语句。
// 修饰符润饰变量 a 赋值 表达式,以分号完结,联合起来为一个语句
var a = 1 + 3;
// 多个语句能够写在一行内
var a = 1 + 3; var b = 'abc';
// 三个空语句
;;;
变量 & 常量
JavaScript 申明变量需应用关键字 var
来申明:
var i;
var sum;
// 或者
var i, sum;
// 申明变量时赋值
var message = "Hello";
var i = 0, j = 0, k = 0;
// 变量赋值不写 var 也是能够的
a = 1; // 等同于 var a = 1; 但这样写法不利于表白,且容易人不知; 鬼不觉地创立全局变量
如果未在 var
申明语句时指定初始值,初始值会默认为 undefined
。
ES6
新增 let
来申明变量,是古代的变量申明形式,与 var
大体雷同。但 let
申明的变量只能在 let
命令所在的代码块内无效,无变量晋升(变量晋升就是被晋升至所在作用域的顶部)。
ES6
新增 const
申明常量,不可被批改,无变量晋升。
const pi = 3.14;
pi = 3.15; // 这里会报错,常量不能被批改。
标识符
标识符就是用来标记各种值的非法名称。大小写敏感。
命名规定:
- 首字符为字母下划线 (
_
) 与美元符号($
)。 - 剩下的字符除了上述字符还能够有数字
0-9
但 JavaScript 中的保留字不能够用作标识符。
古代模式
ES5
标准新增了新的语言个性,但默认不失效。要应用非凡指令use strict
进行激活。将 use strict
放在脚本文件的顶部,整个脚本文件都将以 “ 古代 ” 模式工作。
"use strict";
// 代码以古代模式工作
...
古代 JavaScript 反对 “classes” 和 “modules”,它们会主动启动 use strict
。
区块
JavaScript 应用大括号,将多个相干的语句组合在一起,称为 “ 区块 ”(block)。
对于 var
命令来说,JavaScript 的区块不形成独自的作用域(scope)。
{var a = 1;}
a // 1
下面代码在区块外部,应用 var
命令申明并赋值了变量 a
而后在区块内部,变量 a
仍然无效,区块对于 var
命令不形成独自的作用域,与不应用区块的状况没有任何区别。在 JavaScript
语言中,独自应用区块并不常见,区块往往用来形成其它更简单的语法结构,比方 for
、if
、while
、function
等。
流程管制
条件语句
JavaScript 提供 if
构造和 switch
构造,实现条件判断。
if 语句
if
构造的条件语句有几种状况:
// 第一种
if (布尔值) {语句;}
// 第二种
if (布尔值) {语句;} else {语句;}
// 屡次判断
if (布尔值) {语句;} else if (布尔值) {语句;} else if (布尔值) {语句;} else {语句;}
switch 语句
switch
构造能够代替多个 if
语句构造:
switch (fruit) {
case "banana":
// ...
break;
case "apple":
// ...
break;
...
default:
// ...
}
如果没有 break
语句,代码会始终执行上来。
switch
语句前面的表达式,与 case
语句前面的表达式比拟运算后果时,采纳的是严格相等运算符(===
),而不是相等运算符(==
),这意味着比拟时不会产生类型转换。
JavaScript 还有一个三元运算符 ?:
能够用于逻辑判断:
var even = (n % 2 === 0) ? true : false;
三元运算符能够被视作 if...else...
的简写。
循环语句
循环语句用于反复执行某个操作。JavaScript 反对三种模式:while
循环、do...while
循环、for
循环。
while 循环
var i = 0;
while (i < 100) {i = i + 1;}
do…while 循环
var x = 3, i = 0;
do {i++;} while (i < x);
for 循环
for
语句里有三个表达式:初始化、条件表达式、递增语句。
for (var i = 0; i < 3; i++) {console.log(i);
}
break & continue
break
与 continue
能够用来完结循环语句,但作用不雷同。
break
语句用于跳出代码块或循环。
var i = 0;
while (i < 100) {
i++;
if (i === 10) break; // 当 i =10,跳出循环
}
continue
语句用于立刻终止本轮循环,返回循环构造的头部,开始下一轮循环。
var i = 0;
while (i++) {
i++;
if (i % 2 === 0) continue; // 当 i 为偶数时终止本轮循环
}
label
JavaScript 能够在循环语句前增加 label
,用于跳转到程序的任意地位。相当于定位。
label
设置的标识符不能是保留字。通常与 break
和 continue
进行配合,跳出循环语句。
top:
for (var i = 0; i < 3; i++) {for (var j = 0; j < 3; j++) {if (i === 1 && j === 1) break top;
console.log('i=' + i + ', j=' + j);
}
}
// i = 0, j = 0
// i = 0, j = 1
// i = 0, j = 2
// i = 1, j = 0
标签也能够用于跳出代码块。
foo: {console.log(1);
break foo;
console.log('本行不会输入');
}
console.log(2);
// 1
// 2
运算符
运算符是解决数据的根本办法,用来从现有的值得到新的值。运算符提供算术运算符、关系运算符、布尔运算符、位运算符与其它运算符来进行运算。上面咱们顺次来介绍它们。
算术运算符
算术运算符提供 加法 (+)
、 减法 (-)
、 乘法 (*)
、 除法 (/)
、 指数 (**)
、 余数 (%)
、 自增 (++)
、 自减 (--)
、 正数值 (+X)
、 负数值(-X)
运算符。
加法运算
应用 +
运算符来实现两数相加。JavaScript 容许非数值相加;而如果与字符串相加,则将字符串相连并返回新的字符串。
1 + 1 // 2
true + true // 2
1 + true // 2
'hello,' + 'world!' // hello, world!
1 + ', 数字转成字符串' // 1, 数字转成字符串
true + ', 布尔值转成字符串' // true, 布尔值转成字符串
对象相加
对象相加,必须先转成原始类型的值。
var obj = {p:1};
obj + 2 // [object Object]2
上述代码中,对象 obj
调用 valueOf
办法返回对象本身:
obj.valueOf() // { p: 1}
在调用 toString
办法,将其转为字符串。而上述对象默认返回 [object Object]
。
因而,咱们本人定义 valueOf
办法或 toString
办法,失去想要的后果。
var obj = {valueOf: function () {return 1;}
};
obj + 2 // 3
余数运算
余数的运算后果的正负号由第一个除数的正负号决定。
-1 % 2 // -1
1 % -2 // 1
自增与自减
自增和自减运算符,是一元运算符,只须要一个运算子。它们的作用是将运算子首先转为数值,而后加上 1 或者减去 1。它们会批改原始变量。
数值运算符,负数值运算符
数值运算符(+
)同样应用加号,但它是一元运算符,而加法运算符是二元运算符。
数值运算符的作用在于能够将任何值转为数值(与Number
函数的作用雷同)。
+true // 1
+[] // 0
+{} // NaN
负数值运算符(-
),也同样具备将一个值转为数值的性能,只不过失去的值正负相同。连用两个负数值运算符,等同于数值运算符。
var x = 1;
-x // -1
-(-x) // 1
数值运算符号和赋值运算符,都会返回一个新的值,而不会扭转原始变量的值。
指数运算符
指数运算符(**
)实现指数运算,前一个运算子是底数,后一个运算子是指数。
2 *** 4 //16
留神,指数运算符是右联合,而不是左联合。即多个指数运算符连用时,先进行最左边的计算。
// 相当于 2 ** (3 ** 2)
2 *** 3 *** 2 // 512
下面代码中,因为指数运算符是右联合,所以先计算第二个指数运算符,而不是第一个。
赋值运算符
赋值运算符(Assignment Operators)用于给变量赋值。能够与其它运算符联合。
var x = 1;
var y = x;
x += y; // 与加号运算符联合
x >>= y // 与位运算符联合
比拟运算符
比拟运算符用于比拟两个值之间的大小,而后返回一个布尔值,示意是否满足指定的条件。可用于各种类型的值,不限于数值。
提供 大于 (>)
、 小于 (<)
、 小于等于 (<=)
、 大于等于 (>=)
、 相等 (==)
、 严格相等 (===)
、 不相等 (!=)
、 严格不相等(!==)
八比拟运算符。
相等与不相等
==
和 ===
同是用于比拟两个值是否相等,然而语义的定义不尽相同。==
运算符称作雷同运算符,用于检测两个操作数是否雷同,容许类型转换。===
运算符称作严格相等运算符,用于检测两个值的类型和值是否都相等,比拟过程不会进行类型转换。而 !=
与 !==
也与下面一样。
对象比拟
如果比拟的是对象,会转成原始类型的值,在进行比拟。先调用 valueOf
办法,如果返回的还是对象,在调用 toString
办法。
var x = [2];
x >= '11' // true
// 等同于 [2].valueOf
逻辑运算符
逻辑运算符是将表达式转为布尔值,共四个运算符:逻辑非 (!)
、 与(&&)
、或 (||)
、 三元(?:)
。
逻辑非
取反操作就是将布尔值变成相同值。!true == false
。对于非布尔值,取反会将其转为布尔值。
!undefined // true
!null // true
!0 // true
!NaN // true
!"" // true
上述值进行取反操作为 true
,其它值就都会成为 false
。
逻辑与
逻辑与运算符(&&
)对多个值进行 AND
运算。该操作只有多个值皆为真,后果才为真。两个值执行 &&
操作,只有在第一个值和第二个值都为 true
,它才返回true
。如果其中一个为值为false
,它会返回false
。而第一个值为false
,它会提前结束AND
操作,并返回后果false
,这种行为称为 ” 短路 ”。
3-1 == 2 && 3+2 == 5 // true
3-1 == 2 && 3+2 != 5 // false
逻辑或
逻辑或运算符(||
)对多个值进行 OR
运算,该操作是有一个值为真,后果就为真。而值都为假时,才为假。
3-1 == 2 || 3+2 != 5 // true
3-1 != 2 || 3+2 != 5 // false
例如下面的逻辑与操作换成逻辑或操作后,后果为true
。
三元运算符
三元运算符由 问号 (?)
与 冒号(:)
组成。如果表达式为 true
,则返回第二个表达式的值,否则返回第三个表达式的值。
var x = 2;
var result = x > 1 ? 'true' : 'false';
三元表达式与 if...else
语句的成果差不多。
位运算符
对数字示意的二进制数据进行更低层级的按位运算,一共有 7 个:或 (|)
、 与(&)
、否 (~)
、 异或 (^)
、 左移 (<<)
、 右移 (>>)
和 头部补零的右移(>>>)
。
位运算符间接对每一个比特位(bit)间接解决,所以是十分底层的运算,速度快但不直观。
位运算符只对 32 位整数起作用,必要时,位运算会将操作数转为 32 位整数再执行。
按位或
或运算符(|
)对两个整数的二进制逐位进行 OR
操作。其中一个二进制操作数相应位为 1,或者两个整数比拟的二进制位都为 1,就返回 1,否则返回 0。
5 | 6 // 7
数字 5
转为二进制为 101
,数字 6
转为二进制为 110
,所以进行或运算失去 111
即(7
)
按位与
与运算符(&
)对两个整数的二进制逐位进行 AND
操作。只有两个整数的二进制为皆为 1,能力返回 1,否则返回 0。
5 & 6 // 4
数字 5
转为二进制为 101
,数字 6
转为二进制为 110
,所以进行或运算失去 100
即(4
)
按位否
否运算符(~
)是一元运算符,位于一个整型参数之前,它将操作数的二进制位取反,即 0
转为 1
,1
转为0
。
~3 // -4
数字 3
转为二进制为 32 整数模式的 0..011
,对它进行按位取反则为 32 整数模式的1..100
,转为十进制为-4
。
异或
异或运算(^
)对两个操作数的二进制执行 XOR
操作。只有二进制位不同时返回1
,雷同是则返回0
。
3 ^ 4 // 7
数字 3
转为二进制为 32 模式的 0..011
,数字4
转为二进制为 32 模式的0..100
,所以进行异或运算失去0..111
,转为十进制为7
。
左移
左移运算符(<<
)示意将一个数的二进制值向左挪动指定的位数,尾部补 0
,即乘以2
的指定次方。要挪动二进制的数有第一个操作数决定,而挪动的位数有第二个操作数决定。
3 << 4 // 48
数字 3
向左挪动 4
位,从二进制中看0..011
,失去0..11000
,转为十进制为48
。
右移
右移运算符(>>
)示意将一个数的二进制值向右挪动指定的位数,即除以 2
的指定次方,为负数,最高位补0
;为正数,最高位补1
。要挪动二进制的数有第一个操作数决定,而挪动的位数有第二个操作数决定。
-48>>4 // -3
数字 -48
向右挪动 4
位,从二进制中看10..011000
,失去10..011
,转为十进制为-3
。
头部补零的右移
头部补零的右移(>>>
)与右移(>>
)只有一个差异,就是 >>>
操作的头部一律补零,与原先符号无关。该运算失去的值皆为负数。
-48>>>4 // 268435453
数据类型
数据类型分为两类:原始类型(primitive type)和对象类型(object type)。
原始类型分为 数字 (Number)
、 字符串 (String)
、 布尔值 (Boolean)
。还有两个非凡的原始值为undefined
和null
。
ES6
新增了 Symbol
类型。BigInt
是在Symbol
之后新增加的数字类型。这两个也是原始类型。
对象类型是 Object
类型,它能够存储数据汇合和更简单的实体。
Number
Number
类型代表整数和浮点数,因为 JavaScript 外部都是以 64 位浮点数模式存储数字类型。
let pi = 3.14159;
0.1+0.2 === 0.3 // false
浮点数不是准确的值,因而小数的运算要小心。例如下面的例子 0.1+0.2
等于 0.30000000000000004
与 0.3
不相等。
Number
类型能够做四则运算等等。除了惯例的数字,还包含 ” 非凡数值 ”:无穷大 (Infinity)
、 负无穷大 (-Infinity)
和 非数字(NaN)
。
1 / 0 // Infinity
1 / -0 // -Infinity
1 - 'A' // NaN 次要呈现在将字符串解析成数字出错的场合
BigInt
JavaScript 中 Number
无奈示意 $2^{53}-1$ 或小于 $-(2^{53}-1)$ 的整数。
有时须要加密或奥妙读取的工夫戳时,这个范畴就不够了。
BigInt
类型是最近被增加到 JavaScript 中的,用于示意任意长度的整数。能够通过 n
附加到整数字段的末端来创立 BigInt
值。
const bigInt = 98765432100123456789n;
String
String
类型代表字符串,必须应用引号括起来,单引号或双引号皆可,能够应用 length
计算字符串长度。
let name = "Jam";
name.length // 3
JavaScript 应用 Unicode
字符集存储字符,还能够间接应用 Unicode
示意字符。例如 \172
,输入时是 z
。
Boolean
boolean
类型仅蕴含 true
和 false
。用于示意正确与否。
let isReal = 2 != 3; // true
会被转为 false
的值有 undefined
、null
、false
、0
、NaN
和 空字符串。其它值会被转为 true
。
null & undefined
null
属于原始类型中的非凡值,代表 ” 无 ”、” 空 ” 等未知值。不是其它编程语言中的 “ 对不存在的 object
的援用 ” 或者 “null
指针 ”。
undefined
也属于原始类型中的非凡值,代表 ” 未定义 ”。与 null
的用法差不多,都代表值不存在,带语义不一样。对于没有初始化的变量、函数调用时候提供的函数参数、缺失的对象属性,它们的默认值就是 undefined
。
let name; // undefined
name = "Any";
name = undefined; // 不倡议
name = null; // 倡议
变量被申明,但未被赋值,那它的值就是 undefined
。而在有值后要批改为无值,不倡议应用 undefined
。能够应用 null
。null
将一个 ” 空 ” 或者 ” 未知 ” 的值写入变量。而 undefined
保留作为未进行初始化的变量的默认初始值。
Symbol
Symbol
类型的实例是惟一且不可变的;一种惟一标识符,可用作对象的惟一属性名。
let id1 = Symbol('id'); // id 是形容为 "id" 的 Symbol
let id2 = Symbol('id');
id1 == id2; // false
能够调用 Symbol.description
属性来显示形容信息。
“ 暗藏 ” 属性
Symbol
容许创建对象的 “ 暗藏 ” 属性,这些属性都不能拜访或重写。
let student = {
name: 'XiaoMing',
age: 23
};
let sid = Symbol('sid'); // 学号
student[sid] = 2020;
student[sid]; // 2020 能够应用 Symbol 作为键拜访数据
Symbol
在 for...in
循环中会被跳过,不会显示。
for (var val in student) {console.log(val);
}
/*
name
age
*/
全局 Symbol
通常所有的 Symbol
都不同,即便它们由雷同的名字。但为了实现雷同的 Symbol
具备雷同的实体。有一个全局 Symbol
注册表。咱们能够创立并拜访,确保每次拜访雷同名字的 Symbol
时,返回的都是雷同的 Symbol
。
let id = Symbol.for('id'); // 从全局注册表中读取,如果不存在,则创立
let idAgain = Symbol.for('id'); // 再次读取
id == idAgain; // true, 雷同的 Symbol
还能够应用 Symbol.keyFor(sym)
通过 Symbol
返回该 Symbol
的名称。
let name = Symbol.for('name');
Symbol.keyFor(name); // name
Object
Object
类型是重要的数据类型,它能够存储 “ 键值对 ”(key-value),是无序的合乎数据汇合。
var o1 = {
val1: 123,
val2: function() {...}
}
// 动态创建
var o2 = {}
o2.val1 = 123;
o2.val2 = function() {...}
咱们能够应用点运算符读取对象的属性 o.val1
或者应用方括号o['val1']
。
类型转换
typeof
运算符能够返回参数的类型。对解决不同类型或类型测验时有用。反对两种语法:运算符模式typeof x
或 函数模式typeof(x)
。
typeof undefined // underfined
typeof 3 // number
typeof 3n // bigint
typeof true // boolean
typeof "string" // string
typeof null // object
typeof Math // object
应用 String(value)
将值转换为字符串类型。应用 Number(value)
将值转换为 Number
类型。应用 Boolean(value)
将值转换为布尔类型。对 undefined
进行数字转换时,输入为 NaN
,而非 0
。而 "0"
和只有空格的字符串进行布尔转换时,输入为 true
。
Array
Array
数组类型是值的有序汇合。数组中的元素都是依照插入程序排序的。
数组申明
创立数组的办法总共有如下几种:
let arr = new Array();
let arr = [];
// 也能够间接增加元素
let arr = ['Jan', 'Aug', 'Mar'];
任何类型的数据都能够放入数组。通过下标获取数组的元素 arr[0]
。
数组是一种非凡的对象。应用 typeof
效验 arr
属于的类型。
typeof arr // object
遍历
数组能够应用 for...in
遍历。
arr.test = 23;
for (var i in arr) {console.log(i);
}
/*
0
1
2
test
*/
然而 for...in
还会遍历非数字键。举荐应用 while
或者 for
循环遍历。
存取方法
Array
数组反对push/pop
和 shift/unshift
两种存取形式。
push
办法会在末端增加一个元素;pop
办法会在末端取出一个元素;shift
会在首端取出一个元素;unshift
办法会在首端增加一个元素。
队列是一种先进先出的线性构造,咱们能够应用 push/shift
两种办法来进行实现。
栈是一种后进先出的数据结构,咱们能够应用 push/pop
来实现。
这就是数组的两种利用。
函数
咱们会在许多中央执行许多类似的操作。而这些操作能够应用函数来进行构建。而在 JavaScript 中函数实际上是对象。每个函数都是 Function
类型的实例。
函数申明
应用 function
关键字来申明代码块。
function add(x, y) { // add 为函数名
return x + y;
}
函数还能够通过一个表达式申明。
var add = function(x, y) {return x + y;};
// 调用形式
add(10, 20); // 30
函数表达式实际上就是一个匿名函数,存储在变量中,不须要函数名,通过变量名调用。
咱们还能够应用 “ 箭头函数 ” 来代替函数表达式:
var add = (x, y) => {return x + y;};
// 或者
var add = (x,y) => x + y;
add(10, 20); // 30
下面的函数是两种写法:(...args) => expression
和 (...args) => {statements}
。
咱们也能够应用 Function
构造函数来申明。
var sub = new Function(
'x',
'y',
'return x - y'
);
// 调用形式
sub(20, 10); // 10
反复申明
同一函数被屡次申明,后面的函数会被前面的函数笼罩。
function add(x, y) {return x - y;}
add(10, 20); // 30
function add(x, y) {return x + y;}
add(10, 20); // 30
函数作为值
JavaScript 中函数名自身就是变量,所以函数能够作为值来应用。但凡应用值的中央,都能够应用函数。
function sub(func, x) {return func - x;}
sub(add(10, 20), 25); // 5
下面的函数将 add
函数作为参数传递给了它。
函数晋升
函数会像申明变量一样,被晋升到代码头部。
add(10, 20); // 30
function add(x, y) {return x + y;}
因而,如上的 add
调用不会报错。
函数外部属性
函数外部有两个属性:arguments
和 this
。
arguments
是一个类数组对象,蕴含传入的所有参数。
function min(x, y) {console.log(arguments);
return Math.min(x, y);
}
min(10, 20);
/*
[Arguments] {'0': 10, '1': 20}
10
*/
arguments
带有一个 callee
属性,返回它所对应的原函数。
function factorial(num) {if (num <= 1) {return 1;}
return num * arguments.callee(num - 1);
}
factorial(3); // 6
咱们通过 arguments.callee
,达到调用函数本身的目标。
this
就是函数运行时所在的环境对象。
在全局执行环境中应用 this
,浏览器中指window
对象。
在没有对象的状况下调用 this
的值为 undefined
。
例如,这里雷同的函数被调配给两个不同的对象,在调用中有着不同的 “this” 值:
let user = {name: 'John'};
let admin = {name: 'Admin'};
function sayHi() {return this.name;}
// 两个对象中应用雷同的函数
user.func = sayHi;
admin.func = sayHi;
// 调用不同的 this 值
user.func(); // John
admin.func(); // Admin
函数属性和办法
name 属性
name
用于返回申明函数的名称。
sub.name; // sub
length 属性
length
用于返回函数的形参个数。
sub.length; // 2
prototype 属性
每个函数都有一个prototype
属性,这个属性指向一个对象,这个对象就是原型对象。原型对象的作用就是定义所有实例对象共享的属性和办法。实例对象能够看作从原型对象衍生进去的子对象,原型对象上的所有属性和办法都被实例对象共享。
toString()办法
toString
用于返回对于函数源码的字符串。
sub.toString()
/* function sub(func, x) {return func - x;}
*/
闭包
闭包(closure)是一个函数读取其它函数外部变量的函数。变量的作用域分为全局变量和局部变量,局部变量是只有被函数外部读取,内部读取不了,因而就会用到闭包。
function clos1() {
var x = 10;
function clos2() {return x++;}
return clos2;
}
var result = clos1();
result(); // 10
上述代码中,函数 clos1
的返回值就是clos2
函数,clos2
读取了 clos1
中的外部变量 x
,在调用函数时,会获得clos1
中的外部变量。函数 clos2
就是闭包。
闭包最大的两个用途就是读取函数外部的变量和让这些变量的值始终保持在内存中。
result(); // 11
result
实际上就是闭包 clos2
函数,它一共运行两次,第一次返回 10
之后,做了 ++
操作的 x
的值为11
;下一次调用的值就会返回 11
。
起因是 clos2
被赋给了一个全局变量,导致 clos2
始终在内存中,而 clos2
依赖于 clos1
,因而 clos1
也始终在内存中,不会在调用完结后,被垃圾回收机制(garbage collection)回收。
立刻调用的函数表达式(IIFE)
依据 JavaScript 语法,在下面的例子中 funcname()
函数名 +()
就能够调用函数。
function iife() {console.log("立刻调用");
}(); // 报错 SyntaxError: Unexpected token ')'
而想要解决该问题就是让 function
不要呈现的行首,让引擎将其了解成一个表达式。
(function iife() {console.log("立刻调用");
}());
// 或者
(function iife() {console.log("立刻调用");
})();
// 了解调用
上述两种都以(
圆括号结尾,引擎会认为前面跟的是表达式,而不是函数定义语句,所以会防止谬误。这就叫 “ 立刻调用的函数表达式 ”(Immediately-Invoked Function Expression)。
如果想要查看更多文章,请关注公总号「海人的博客」