js基础语法之函数
# 定义与调用
无参 有参 带返回值 匿名 立即执行
// 1> 无参函数
function f1() {
console.log("无参函数!");
};
f1();
// 2> 有参数函数
function f2(a, b) {
console.log("有参函数!", a + b);
};
f2(10, 20);
// 3> 带返回值的函数
// js不能像python一样返回多个值..
// js只能返回一个值.(可将多个值放到数组或"字典 js中叫对象"中)
function f3() {
return [111, 222, 333];
};
f3();
// 4> 匿名函数
// 函数是一等公民!所以可以赋值给变量.
var f4 = function (a, b) {
console.log("匿名函数!", a + b);
}
f4(20, 30);
// 5> 立即执行函数
// 注意要用一个括号将函数包裹起来..
(function (a, b) {
console.log("立即执行函数!", a + b);
})(11, 22);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
内置的arguments对象
相当于将所有参数汇总到一个数组对象里面去了. 相当于python里的*args
function f2(a, b) {
console.log(arguments); // 内置的arguments对象
console.log(arguments.length); // 2
console.log(arguments[0], arguments[1]); // 10 20
console.log(a, b); // 10 20
};
f2(10, 20);
2
3
4
5
6
7
8
# 局部与全局变量
局部变量
在JavaScript函数内部声明的变量(使用 var)是局部变量.
所以只能在函数内部访问它(该变量的作用域是函数内部).
全局变量
在 函数外 声明的变量是全局变量, 网页上的所有脚本和函数都能访问它
在 函数内 不加var关键字声明的变量也是全局变量.
变量生存周期
JavaScript变量的生命期从它们被声明的时就开始
局部变量会在函数运行以后被删除
全局变量会在页面关闭后被删除
Ps: ES6的语法 let关键字声明的变量只属于自己的{}花括号 -- 块级作用域!
(let 关键字要考虑浏览器兼容性问题..)
# 作用域
首先在函数内部查找变量, 找不到则到外层函数查找, 逐步找到最外层.
另外函数的作用域关系是在定义阶段就固定死的, 与调用位置无关. - 与python一样的!!
# 函数内的代码块
下方的这一段代码的语法跟python类似!
var x = 11; // 全局x
var a = "芜湖"
function test() {
// 局部变量只能在局部访问得到
var x = 22; // 局部x
var y = 33; // 局部y
// 在js和shell里不加关键字声明的变量会在全局
z = 44; // 全局z
console.log(x,y,a);
};
test() // 22 33 "芜湖"
// console.log(y) 报错 因为函数都已经运行完了.y变量被释放了..
console.log(x) // 11
console.log(z) // 44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# if里的代码块
但凡涉及到子代码块的地方就需要讨论作用域啦!
除了函数,if for switch while都可以包含子代码块!
// 1> 默认情况下,if子代码块内声明变量的作用域是与if所在的位置保持一致的,即if在全局代码块里的变量就在全局,在函数里就局部
if (true) {
// 因为if的位置在全局,所以m变量也属于全局
var m = 666
}
function test() {
// 因为if的位置在局部,所以n变量也属于局部
if (true) {
var n = 666
}
}
// 2> 若想让if的子代码块有自己的作用域,用ES6的新语法 let关键字
// 即let声明的变量只属于自己的{}花括号
if (true) {
let p = 666; // ★ 这个p变量在全局就访问不到了!!
}
/*
下面几行代码. 一共有三层作用域.
var x = 11
function f() {
var x = 22
if (true) {
let x = 33
}
}
*/
for (var i = 0; i <= 3; i++) {}
console.log(i) // 4
for (let j = 0; j <= 3; j++) {
var b = 10
}
// console.log(j) 报错! for循环里用let关键字声明的变量不会污染全局作用域!!!
console.log(b) // 10
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 闭包
先看python代码的实现与解决.
my_list = []
for i in range(5):
# 定义完f函数后,并没有运行函数体代码
# 函数体里的i以定义的时候为准,局部没有i,向全局要i
def f():
print(i)
my_list.append(f)
# 调用的时候,全局的i的值为4
my_list[0]() # 4
my_list[1]() # 4
# --------------------使用闭包解决
my_list = []
for i in range(5):
def outer(x):
def f():
print(x)
return f
f = outer(i)
my_list.append(f)
my_list[0]() # 0
my_list[1]() # 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
再看js代码的实现与解决.
var arr = []
// 循环了5次 0 1 2 3 4
for (var i = 0; i <= 4; i++) {
var f = function () {
console.log(i)
}
arr.push(f)
}
console.log(arr[0]()) // 5 最后一次循环 i++后i的值为5,5<=4不成立,结束循环
console.log(arr[0]()) // 5
// ----------- 解决方案,闭包
var arr = []
// 循环了5次 0 1 2 3 4
for (var i = 0; i <= 4; i++) {
function outer(x) {
// var x = i 写在这就写死了..
var f = function () {
console.log(x)
}
return f
}
var f = outer(i)
arr.push(f)
}
console.log(arr[0]()) // 0
console.log(arr[1]()) // 1
// ------ 在这里,用let关键字能实现上面闭包的效果
// let属于块级作用域 但let一定要注意浏览器兼容的问题.. 现目前不建议用.
var arr = []
for (let i = 0; i <= 4; i++) {
var f = function () {
console.log(i)
}
arr.push(f)
}
console.log(arr[0]()) // 0
console.log(arr[1]()) // 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
函数的作用域关系是在定义阶段就固定死的,与调用位置无关!
// 栗子1
var city = "BeiJing";
function f() {
var city = "ShangHai";
function inner() {
var city = "ShenZhen";
console.log(city);
}
inner();
}
f(); //输出结果是 ShenZhen
// 栗子2
var city = "BeiJing";
function Bar() {
console.log(city);
}
function f() {
var city = "ShangHai";
return Bar;
}
var ret = f();
ret(); // 打印结果是 BeiJing
// 栗子3
var city = "BeiJing";
function f() {
var city = "ShangHai";
function inner() {
console.log(city);
}
return inner;
}
var res = f();
res(); // 打印结果是 ShangHai
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 变量提升
先来看一段python代码.╮(╯▽╰)╭(我也是醉了..)
var1 = "hello"
var2 = 111
var3 = []
var4 = None
# 报错: 在赋值前引用局部变量'x'
# 报错可以这样解释:
# py的模块代码不会预编译,但函数体内的代码会预编译.即模块内的函数体代码在运行前会经过预编译,因此不管变量名的绑定发生在作用域的那个位置,都能被编译器知道.它们在预编译(或者说是检查语法)的时候,就在当前的局部作用域找到了变量名,所以不会根据LEGB的顺序依次找变量..只会向当前的局部作用域里要变量啦. 但在调用此变量时,此变量还没有绑定到一个内存对象(没有定义和初始化,即没有赋值).报错.
# 标注【○】的报错 还可以从python在作用域里的赋值规则这个角度来解释:
# 若这个变量存在,则对其绑定新的对象;若不存在,则将这次赋值视为对这个变量的定义
# 前者是局部变量,后者是全局变量..冲突了..
def f():
# var1 = var1.upper() 报错 ○
# print(var1) 报错
# var1 = var1.upper()
# print(var2) 报错
# var2 = 222
# var3 = var3.append(3) 报错 ○
# print(var3) 报错
# var3 = var3.append(3)
# if var4 is None: 报错
# var4 = []
# print(a) 报错
# a = 555
print(var1)
var1.upper()
print(var3)
var3.append(3)
f()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
而js的代码就不会出现上方python的这些问题.
变量提升就是将所有的 变量声明语句 提升到变量自己作用域的最上面!代码还是一行行的依次运行.
1> 在全局作用域的变量提升
示例1:
console.log(a); // undefined
var a = "Hello World.";
console.log(a); // Hello World.
// ---- 上面这段代码等同于下面这段代码!
var a; // a变量的声明语句
console.log(a); // undefined -- a声明过但没赋值
a = "Hello World.";
console.log(a); // Hello World.
2
3
4
5
6
7
8
9
10
示例2:
var a = 10;
if (true) {
console.log(a); // 10
var a = 20;
console.log(a) // 20
}
console.log(a); // 20
2
3
4
5
6
7
2> 在函数局部作用域里变量提升
示例1:
var x = 10;
function f() {
console.log(x); // undefined
var x = 20;
console.log(x) // 20
}
// ---- 上面这段代码等同于下面这段代码!
var x = 10;
function f() {
var x; // x变量的声明语句
console.log(x);
x = 20; // 给x变量赋值
console.log(x)
}
f()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
示例2: 此例子函数体里未用到变量提升..
var x = 10;
function f() {
// 代码一行行的运行
console.log(x); // 10 运行到这,全局的x值为10
// f函数里没有x变量声明(无var关键字),所以这里这个x变量是属于全局的
// 若全局没有就定义到全局;若全局有,则给全局的x变量重新赋值
// Ps:同一个变量只会声明一次,其他的会被忽略掉
x = 20;
console.log(x) // 20 运行到这,全局的x值为20
}
f()
2
3
4
5
6
7
8
9
10
11
12
13
# 函数提升
js中的函数跟python中的函数都是一等公民! 即定义函数跟定义变量是一样的,也有一个提升的效果..
只不过变量分为了两部分(声明+赋值),提升的是声明.
函数的提升是将 函数这一个整体 提升到所有代码最前面.
所以js中会很玄学,后面定义的函数,在前面也可以调用! python的函数必须先定义再调用!
注意: 若函数名和变量名一样,函数提升优先于变量提升..
console.log(f);
var f = "Hello!"
function f() {
console.log("Hello World.")
}
console.log(f);
/*
ƒ f() {
console.log("Hello World.")
}
Hello!
*/
2
3
4
5
6
7
8
9
10
11
12
13