1、两种创建方式
在 JavaScript 中函数的写法和 python 很相似,因为不用提前声明类型,所以也就没有返回值这一说法。下面演示最常用的一种写法
/* 先来看一下函数的结构:
function 函数名(参数1, 参数2 ... , 参数n){
函数体
return 返回值;
}
我们来观察一下 JavaScript 中函数的特点:
1、声明的时候没有说明返回值类型
2、参数列表中每一个参数也没有指明类型(多个参数使用 , 隔开)
3、所有的函数开头均以 function 开头
4、返回值使用 return */
//现在我们按照上面的格式,实现一个功能:判断输入的字符是数字还是字母
function judge(variable) {
if (variable >= "0" && variable <= "9") {
return variable + " 这是一个数字";
} else if (variable >= "a" && variable <= "z") {
return variable + " 这是一个小写字母"
} else if (variable >= "A" && variable <= "Z") {
return variable + " 这是一个大写字母";
} else {
return variable + " 这不是数字,也不是字母";
}
}
console.log(judge(1));
console.log(judge('a'));
console.log(judge('G'));
console.log(judge('@'));
控制台输出结果:
上边的写法其实还是很容易接受的,下面介绍另一种写法
/* 格式:
var 函数名 = function(参数1, 参数2 ... , 参数n){
函数体
return 返回值;
}
解释一下为什么可以这么写:首先在 JavaScript 中函数也是一个对象,那么当然可以通过变量赋值的形式创建,我们简化一下可以看成 var 变量名 = 值,这里值就是函数体
*/
//我们将上边的例子改写成第二种形式:
var judge = function(variable){
if (variable >= "0" && variable <= "9") {
return variable + " 这是一个数字";
} else if (variable >= "a" && variable <= "z") {
return variable + " 这是一个小写字母"
} else if (variable >= "A" && variable <= "Z") {
return variable + " 这是一个大写字母";
} else {
return variable + " 这不是数字,也不是字母";
}
}
2、函数参数
本小节我们会介绍 JavaScript 函数参数相关知识点,当然我并不会讲那些大家都知道的东西,我所讲的都是 JavaScript 特有的东西
先来了解一个关键字 arguments,中文翻译就是 参数(s),它永远指向当前函数的调用者传入的所有参数,请注意,一定是传入的参数。arguments 的存储方式类似数组,但切记,它并不是一个Array!
//我们先随便写一个例子,看看 arguments 的用法
function setArray(variable) {
var array = [];
//arguments.length 实际上就是传入参数的个数
for (let i = 0; i < arguments.length; i++) {
//获取的方式也和 Array 相似,可以通过下标访问
array[i] = arguments[i];
}
return array;
}
let arguments = setArray(10, 20, 30, 40, 50);
console.log(arguments);
控制台输出结果:
可以看到仅有 一个参数 列表的函数也可以接收 n 个参数,这就是 arguments 的力量。但是有一个缺点——重复保存参数
举个例子:假如现在有一个 function(value_1, value_2, value_3),而我传递了 5 个参数,分别是 a、b、c、d、e
value_1 对应 a;value_2 对应 b;value_3 对应 c;arguments 对应 a、b、c、d、e,这不就重复了嘛,如果存在一个关键字能够只保存 d 和 e 就好了!
为此 ES6 提供了一个新特性,引入 rest 关键字,用来保存多余的函数参数
/* 使用 rest 格式(这里使用最常用的函数写法):
function(参数1, 参数2, ...rest) {
函数体
return 返回值;
}
需要注意,rest 只能写在参数列表的最后边 */
function setArrayWithRest(variable, ...rest) {
var array = [];
for (let i = 0; i < rest.length; i++) {
array[i] = rest[i];
}
return array;
}
var rest = setArrayWithRest(10, 20, 30, 40, 50);
//直接转换成字符串输出
console.log(rest);
这样看不够清楚,下面展示一下 IDEA 的截图(分别使用 arguments 和 rest):
浏览器控制台输出:
3、变量那些事儿
3.1、变量作用域
函数还有一点需要我们格外注意,那就是变量的作用域,如果你自己写的代码和别人起冲突,那有可能就是变量冲突。但是不用担心,看完我的讲解,你一定会有所收获(哎就是这么自信)
之前我们的变量都是采用 var 声明,所以我们先来聊聊 var 的作用域
//首先先看下面两句 console.log(variable_1) 那一句会执行?
function func1() {
var variable_1 = "I'm variable_1, in func1";
console.log(variable_1); //语句 1
}
func1();
console.log(variable_1); //语句 2
运行结果(你猜对了嘛):
很明显,语句 1 成功执行,但是函数体外显示 variable_1 未定义,说明 variable_1 只有在函数体内才有用,那我们先得出一个结论(慢慢验证,不要急):使用 var 声明的变量在函数体外无法使用
好,接下来我们继续看例子:
function func1() {
var variable_1 = "I'm variable_1, in func1";
//函数 func1() 内写 func2()
function func2() {
console.log(variable_1); //语句 1
}
//下面这两句代码都会成功嘛?
console.log(variable_1); //语句 2
func2();
}
func1();
运行结果:
可以看到都执行成功了,说明 variable_1 在 func2() 内也起作用,我们得出第二个结论:使用 var 声明的变量,无论是当前函数自身,还是其内部函数,都可以使用
继续,我们离真相很接近了:
function func1() {
function func2() {
//现在我们在 func2 内部定义变量
var variable_2 = "I'm variable_2, in func2";
console.log(variable_2); //语句 1
}
//下面这两句代码都会成功嘛?
func2();
console.log(variable_2); //语句 2
}
func1();
运行结果:
语句 1 正常输出,语句 2 报错,所以 variable_2 无法在 func1() 内使用,只能在 func2() 中使用,我们得出第三个结论:使用 var 声明的变量无法在外层函数使用
综上所述,我们得出结论:使用 var 声明的变量无法在函数体外使用,在函数体内无论在哪都可以使用。例如 for (var a = 0; a < 10; a++),这里面的 a 只要是在当前函数体内,可以随便用!那有些人可能好奇,如果使用 var 修饰的变量出现在函数体外,那怎么办?我们可以把一整个文件看成一个函数(假设),按照原则,这个变量可以在文件内任意地方使用,即 全局变量
很明显,使用 var 容易引起变量冲突。所以 ES6 新增了两个修饰变量的关键字:let 和 const
ES6 新增的关键字 let,以及它的作用域
这里由于篇幅原因(因为还有其他知识点需要写,长了你们又不爱看),所以我简略一点
在 ES6 之前,是没有 块作用域(就是用 {} 包起来的代码块)的概念的。ES6 之后我们可以使用 let 声明变量,这种变量只能在当前代码块内使用,即 {} 外无法访问
function func3() {
for (let i = 0; i < 10; i++) {
let variable_3 = 10;
}
//这回能成功输出吗?按照 let 的定义,应该是不可以的
console.log("variable_3 = " + variable_3);
}
func3();
运行结果(确实是不可以输出 variable_3 的值,毕竟它的作用域仅在 for 语句内):
几点推导的知识点:
- 使用 let 声明的变量如果出现在 函数体外(直接写在非函数或代码块区域),那么它还是一个 全局变量
- 使用 let 声明的变量如果出现在 函数开头,那么该变量的作用范围就是整个函数
- 使用 let 修饰的变量不属于 window 变量。这点先看看吧,我们还没提到什么是 window 对象呢,这里剧透一下,window 对象是 JavaScript 的一个全局对象
ES6 新增的关键字 const,以及它的作用域
在其他编程语言中,const 用于修饰常量,在 JavaScript 中也不例外。ES6 之前定义常量有一个不成文规定 —— 全部用大写字母表示的变量就是常量(明显会出问题)。自 ES6 后,我们可以使用 const 声明常量(即不能改变的值),具体用法如下:
function func4() {
const variable_4 = 100;
console.log("variable_4 = " + variable_4); //语句 1
//尝试改变常量的值,编译都无法通过
variable_4 = 200;
console.log("改变 variable_4 的值:" + variable_4); //语句 2
}
func4();
运行结果(提示 Assigment to constant variable 错误):
上边介绍了 var、let、const 三种声明变量的方式,也详谈了它们的作用域,下面我们来看一下 在 JavaScript 中其它有关函数变量的知识点:
3.2、同名变量的处理机制
//我们还是先看例子,并观察执行结果
function func5() {
//学习了 let,我们之后尽量都用它
let variable_5 = "outer variable";
function func6() {
//和外部函数相同名称的变量
let variable_5 = "inner variable";
console.log("func6 输出:" + variable_5); //语句 1
}
func6();
console.log("func5 输出:" + variable_5); //语句 2
}
func5();
这回我们先分析语句 2 可能的输出结果:根据 let 的作用域规则,func6() 内部的变量一定无法在 func5() 中使用,所以语句 2 不可能输出 “inner variable”,那就只可能输出 “outer variable”;对于语句 1,确实两个值都符合规定,但如果我们是编译器,是不是会选择“近一点”的,所以可能会选择 “inner variable”,下面看运行结果:
确实和我们分析的一样,所以我们总结一下同名变量的处理机制(很简单):
- 当内部函数和外部函数拥有相同名称的变量时,内部函数优先使用内部变量
- 外部函数由于变量作用域的问题,无法访问内部函数的变量,所以只能使用属于自己的变量
3.3、变量提升
也许前面的结论你都能从其他编程语言中推导得出,但是对于变量提升,你应该是第一次听说。JavaScript 的函数定义有个特点,它会先扫描整个函数体的语句,把所有 **var 申明的变量 **“提升” 到函数顶部(看例子看例子)
function func7() {
'use strict';
var variable_6 = "Hello ";
//使用到了未定义的变量
console.log(variable_6 + variable_7); //语句 1
var variable_7 = "world!";
console.log(variable_6 + variable_7); //语句 2
}
//请问语句 1 会报错吗?
func7();
运行结果:
结果分析:即使我们开启了严格检查模式,代码依旧可以通过编译,而且正常输出,这是因为 variable_7 虽然在函数体末尾被声明,但是根据变量提升原则,在执行 func7() 时,会首先把所有的变量声明提到顶部,这样相当于 variable7 被声明了,但是没有赋值,所有才会显示 undefined
但是请注意,根据我的测试,变量提升对 let 和 const 声明的变量无效,所以为了正常使用,我推荐无论是使用 var 还是 let,变量的声明都放在函数顶部,防止给自己添麻烦!