javascipt基础/函数

一、函数

1.1 函数的声明
函数就是功能。
我们可以自己封装一些语句在函数内部,函数就具有了某一种特定的功能。
声明函数:
使用关键字function 空格隔开函数名(参数) { },在大括号内部封装语句,表示一个整体

1.	function 函数名(参数) {
2.	    语句;
3.	}

函数声明语句并不会执行,只是告诉我们函数可以实现某一种功能,内部有哪些语句。

1.	// 声明函数=
2.	function xiyiji() {
3.		console.log("接水");
4.		console.log("洗衣服");
5.		console.log("漂洗");
6.		console.log("甩干");
7.		console.log("烘干");
8.	}

函数名命名规则和变量相同。
函数优点1:将一些重复性的语句封装在函数内部,可以声明一次多次调用,简化书写。
1.2 调用
函数名();
函数是一个整体,在调用时,内部语句都会执行。
函数执行的位置和声明没有位置没有关系只和调用位置有关。

1.	console.log(1);
2.	// 声明函数
3.	function xiyiji() {
4.		console.log("接水");
5.		console.log("洗衣服");
6.		console.log("漂洗");
7.		console.log("甩干");
8.		console.log("烘干");
9.	}
10.	// 调用,函数内部语句都会执行
11.	xiyiji();
12.	console.log(2);
13.	// 函数声明一次可以多次调用
14.	xiyiji();

在这里插入图片描述

1.3 函数的参数
函数可以帮我们封装一些代码,代码可以重复调用,函数留了一个接口,就是我们的参数,可以通过参数的变化让我们的函数发生不同作用。
参数都是变量:命名规则与变量一样。
调用过程中给函数传递参数的过程就是一个给变量赋值的过程。

形式参数:在函数声明语句中小括号内部书写的是形式参数,简称形参
实际参数:在函数调用语句中小括号内部书写的是实际参数,简称实参
传参:在函数调用时,将实际参数赋值给形式参数的过程。

在这里插入图片描述
形参也有数据类型,是动态变化,根据实际参数赋值动态变化的。
参数个数问题:
arguments: 由实际参数组成的类数组对象。
实际参数个数多于形式参数,多于实际参数直接舍弃。
实际参数个数少于形式参数,优先将实参赋值前面的形参,没有赋值是undefined。

  1. sum(1);
    在这里插入图片描述

函数的优点2:函数有参数,相当于给我们提供一个API接口,我们可以通过接口去调用函数,执行不同的操作,后面封装函数的时候,只需要了解API的用途就够了,就是传参之后有什么结果,不用去了解函数里面的构造。不论是自己的函数还是用的别人封装好的函数,都只需要知道怎么用就够了。

1.4 return
return返回值
return也可以接收参数(变量),作为函数的返回值。return并不会输出,想看输出console.log()

1.	// 声明函数
2.	function sum(a,b) {
3.		// return 4;
4.		// 只要调用函数可以的到a+b结果
5.		return a + b;
6.	}
7.	
8.	console.log(sum(1,4));
9.	console.log(sum(10,sum(1,19)));

返回值相当于将我们的函数变成了一个表达式。利用这个特性,我们可以将函数作为一个实际参数,传递给另外一个函数。



return特点:函数内部遇见return,直接返回
1.	// 函数内部遇见return直接返回值
2.	function fun() {
3.		console.log(1);
4.		console.log(2);
5.		console.log(3);
6.		return;
7.		console.log(4);
8.	}
9.	
10.	// 函数调用
11.	fun()

函数优点3:函数内部书写return,可以让函数作为参数,传递,有利于模块化编程

1.5模块化编程
人类从古至今,习惯将事情分工,将一些内容做成一些公共模块,模块可以重复反复使用。
模块化编程:将一些基础的公共的部分单独封装到一个函数内,可以多次被调用。
注意:模块化编程,可以让我们的程序更加优化,各个小模块要尽量功能单一,提高重复使用率。

案例:输出100以内的质数,模块化编程。
案例:输出100以内的完美数

1.	/*
2.		找100以内的完美数:(一个数的约数除了它本身外其他约数和还等于这个数)。
3.		比如:6 = 1 + 2 + 3   6完美数
4.			  8 != 1 + 2 + 4  8不是完美数
5.	
6.	
7.		输出100以内所有的完美数:
8.		判断任何数字是不是完美数
9.		一个数字除了本身之外其他约数和
10.	*/
11.	/*
12.		*函数1:一个数字约数和(不包含本身)
13.		*参数: 一个数字
14.		*返回值:约数和
15.	*/
16.	function yueshuhe(n) {
17.		// 求n约数和
18.		// 累加器
19.		var sum = 0;
20.		for (var i = 1; i < n; i ++) {
21.			// 约数和
22.			if (n % i === 0) {
23.				sum += i;
24.			}
25.		}
26.		// 返回值
27.		return sum;
28.	}
29.	
30.	
31.	/*
32.		*判断任何数字是不是完美数
33.		*参数: 一个数字
34.		*返回值:布尔值
35.	*/
36.	function isWms(num) {
37.		// 判断num是不是完美数
38.		if (num === yueshuhe(num)) {
39.			return true;
40.		}else {
41.			return false;
42.		}
43.	}
44.	
45.	// 输出100以内所有的完美数
46.	for (var i = 1; i <= 100; i ++) {
47.		// i是完美数,输出
48.		if (isWms(i)) {
49.			console.log(i);
50.		}
51.	}

⦁ 1.6函数表达式
声明函数可以使用关键字function
还可以使用函数表达式
将一个匿名函数(拉姆达函数)赋值给一个变量。

var 函数名 = function (形式参数) {
⦁	      语句;
⦁	}//结尾必须添加分号

调用 函数名(实际参数);

1	// 将匿名函数赋值给变量,结尾必须添加分号
2	var fun = function (a,b) {
3		return a + b;
4	};
5	
6	// a,b数字  字符串
7	// 调用函数名()
8	alert(fun(1,10));
9	console.log(fun("1","你好"));

1.6 函数数据类型
简单数据类型:number, string, undefined, boolean
引用数据类型:object, function ,arry

不管是关键字function还是函数表达式数据类型都是function。

简单数据类型:不同变量在赋值,变量将保存的值复制一份然后进行赋值。变量改变不互相影响

1	//简单数据类型
2	var a = 10;
3	// b = 10
4	var b = a;
5	// 改变a
6	a = "你好";
7	console.log(b);  //b=10

引用数据类型:不同变量在赋值,将变量保存的地址进行赋值,变量改变会互相影响

1	// 引用数据类型
2	var fun1;
3	function fun2() {
4		console.log(1);
5	}
6	// fun2地址指向了fun1
7	fun1 = fun2;
8	// 改变
9	fun2.xixi = "嘻嘻";//设置属性
10	fun1.haha = "哈哈";//设置属性
11	console.log(fun1.xixi);//嘻嘻
12	console.log(fun2.haha);//哈哈 

总结:简单数据类型保存的值,引用数据类型保存的地址。

1.7 执行上下文与执行上下文栈

1.71 执行上下文

1. 代码分类(位置)

  * 全局代码

  * 函数代码

2. 全局执行上下文

  * 在执行全局代码前将window确定为全局执行上下文

  * 对全局数据进行预处理

    * var定义的全局变量==>undefined, 添加为window的属性

    * function关键字声明的全局函数==>赋值(fun), 添加为window的方法

    * this==>赋值(window)

  * 开始执行全局代码

3. 函数执行上下文

  * 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象

  * 对局部数据进行预处理

    * 形参变量==>赋值(实参)==>添加为执行上下文的属性

    * arguments==>赋值(实参列表), 添加为执行上下文的属性

    * var定义的局部变量==>undefined, 添加为执行上下文的属性

    * function声明的函数 ==>赋值(fun), 添加为执行上下文的方法

    * this==>赋值(调用函数的对象)

  * 开始执行函数体代码

1.72 执行上下文栈

1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象

2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)

3. 在函数执行上下文创建后, 将其添加到栈中(压栈)

4. 在当前函数执行完后,将栈顶的对象移除(出栈)

5. 当所有的代码执行完后, 栈中只剩下window

1.8函数声明提升

1. 变量声明提升

  * 通过var定义(声明)的变量, 在定义语句之前就可以访问到

  * : undefined

2. 函数声明提升

  * 通过function声明的函数, 在之前就可以直接调用

  * : 函数定义(对象)

变量提升只提升声明语句,赋值语句不提升。
function关键字声明函数提升。在函数预解析时将函数名提升到所有语句之前,由于函数保存的是地址,通过地址可以找到声明整体。相当于函数整体提升到所有语句之前。
函数先调用,在声明也不会报错。

1	// 先调用
2	fun();
3	// 后声明
4	function fun() {
5		console.log(1);
6	}

函数表达式,提升只提升声明语句,赋值语句不提升先调用会报错

1	// 先调用
2	fun();
3	// 后声明
4	var fun = function () {
5		console.log(1);
6	};

总结:在声明函数时最好使用function关键字声明,不会出错。
一般我们习惯先书写函数调用,将声明书写在所有语句前,便于代码读取。

function声明函数名和变量相同,优先提升function,给function使用。

1	// 先使用
2	console.log(fun);
3	// 使用function声明fun函数
4	function fun() {
5		return 1;
6	}
7	// 声明fun变量
8	var fun = 10;
9	
10	/*
11	预解析:
12	优先提升函数
13	function fun(){};名字不会重复声明
14	console.log(fun);
15	fun = 10;
16	*/
17	console.log(fun);

函数表达式和function关键字重名,优先给function关键字使用。

1	// 先使用
2	fun();
3	// 声明函数表达式
4	var fun = function () {
5		console.log(1);
6	};
7	// function关键字
8	function fun() {
9		console.log(2);
10	}
11	/*
12	预解析:优先提升function
13	function fun(){console.log(2)}; fun名字声明过不会重复声明
14	fun();
15	fun = function() {console.log(1)};
16	*/
17	fun();

1.8递归函数
在函数内部调用自身。一般解决数学问题
菲波那切数列:

1,1,2,3,5,8,13,21……
1	/*
2	1,1,2,3,5,8,13,21……
3	
4	函数:用户输入一个项数,告诉用户该项对应的值
5	从第3项开始,该值=前1项+前2项对应值
6	参数:  项数1-
7	返回值:该项对应的值
8	*/
9	function feibo(n) {
10		// 求第n项对应的值
11		if (n === 1 || n === 2) {
12			return 1;
13		}else {
14			return feibo(n-1) + feibo(n-2);
15		}
16	}
17	
18	console.log(feibo(1));
19	console.log(feibo(2));
20	console.log(feibo(3));
21	console.log(feibo(4));
22	console.log(feibo(8));
23	console.log(feibo(20));

1.9变量作用域
作用域是什么:

1. 理解

  * 就是一块"地盘", 一个代码段所在的区域

  * 它是静态的(相对于上下文对象), 在编写代码时就确定了

2. 分类

  * 全局作用域

  * 函数作用域

  * 没有块作用域(ES6有了)

3. 作用

  * 隔离变量,不同作用域下同名变量不会有冲突

在函数内部声明的变量只能在函数内部使用,在函数外部任何地方都不能使用。

1	// 在函数内部声明a变量
2	function fun() {
3		var a = 10;
4		console.log(a);
5	}
6	
7	// 在函数外部使用a
8	console.log(a);//报错
 

js中只有函数可以关住变量的作用域。

1.9局部变量和全局变量
局部变量:在一个作用域(定义域)内定义的变量就是这个作用域内的局部变量。只能在作用域内被访问到。
全局变量:从广义上来看,全局变量也是一种局部变量。全局变量定义在全局,所以也叫全局变量。可以在任何地方都被访问到。
变量申明的原理:全局变量,在全局定义之后,会永久存在,任何时候,任何位置访问,都能够找到它。局部变量定义在函数内部的,函数定义的过程,并没有真正的去定义这个局部变量,只有在执行函数的时候,才会立即定义这个局部变量,执行完之后,变量就被立即销毁了,在其他的地方访问变量的时候,找不到这个变量,所以会有一个引用错误,变量未定义。


1	// 没有书写在函数内部的变量,可以在全局范围内访问
2	var b = "你好";
3	// 在函数内部声明a变量
4	function fun() {
5		// a只能在函数内部使用,a局部变量
6		var a = 10;
7		console.log(a);
8		// 全局b,可以在全局使用,当然可以在函数内部使用
9		console.log(b);
10	}
11	
12	// 在函数外部使用a
13	// console.log(a);
14	fun();
15	console.log(b);

1.10 形参是局部变量
形参只能在该函数内部使用,在函数外部不能访问,会报错。

1	function fun(a,b) {
2		console.log(a);
3		console.log(b);
4	}
5	
6	fun(1,10);
7	// 形参是fun()函数局部变量,在fun函数外部不能使用,会报错
8	console.log(a);
9	console.log(b);

1.11全局变量使用
函数间的通信作用:函数声明一次可以多次调用,全局变量可以进行累加。

1	// 声明全局变量a
2	var a = 1;
3	// 声明函数
4	function jia() {
5		a ++;
6		console.log(a);
7	}
8	// 函数间的通信,都可以改变a
9	jia();  //a=2
10	jia();  //a=3
11	console.log(a);

全局变量作用:传递作用
不同的函数都可以改变全局变量值,并且使用最新值参与计算。(信号量)

1	// 声明全局变量a,信号量
2	var a = 1;
3	// 声明函数1让a自加
4	function jia() {
5		a ++;
6		console.log(a);
7	}
8	// 声明函数2让a自减
9	function jian() {
10		a --;
11		console.log(a);
12	}
13	
14	jia(); //a=2
15	jian(); //a=1
16	jia();
17	jia();
18	jia(); //a=4
19	console.log(a);

1.12 作用域链
指的是我们变量查找的一个规律:我们可以在不同的作用域内使用相同的标识符去命名变量。我们在使用一个变量的时候,需要找到匹配的标识符,我们有重复的,用哪一个?如果在当前作用域有这个变量,就直接使用,如果当前作用域没有这个变量定义,会一层一层的从本层往外依次查找,遇到第一个就直接使用,类似于就近原则。


1	// 声明全局a
2	var a = 1;
3	function fun1() {
4		// 声明fun1局部变量a
5		var a = 2;
6		// a=2
7		console.log(a);
8		function fun2() {
9			var a = 3;
10			function fun3() {
11				// 本层fun3没有a定义;从本层出发依次向外查找。
12				//a=3
13				console.log(a);
14			}
15			fun3();
16		}
17		fun2();
18	}
19	fun1();
20	console.log(a);

当遇见一个变量时,JS引擎会从其所在的作用域依次向外层查找,查找会在找到第一个匹配的标识符的时候停止。在多层嵌套的作用域中可以定义同名的标识符,发生“遮蔽效应”。
如果变量声明时,不写var关键字,计算机会自动在全局作用域内给它进行一个声明,局部变量就强制性的变成了全局变量。这种情况是不合理,会造成一个全局变量的污染。所以,定义变量必须写var关键字。

1	// 声明全局a
2	var a = 1;
3	// a=2; a=3
4	function fun1() {
5		a = 2;
6		console.log(a);
7		function fun2() {
8			a = 3;
9			function fun3() {
10				console.log(a);
11			}
12			fun3();
13		}
14		fun2();
15	}
16	fun1();
17	// 由于变量强制转为全局变量a=3
18	console.log(a);

1.13函数作用域
函数的作用域和变量作用域相似,也是函数只能在声明函数的内部使用,在声明函数外部不能使用。

1	function outer() {
2		var a = 1;
3		// inner函数在outer内部声明,只能在outer内部使用
4		function inner() {
5			console.log(a);
6		}
7		inner();
8	}

9	outer();
10	// 在outer外部不能使用inner
11	inner();

总结:函数能关住变量和函数的作用域。
1.14 闭包
闭包就是能够读取其他函数内部变量的函数。只有函数内部的子函数才能读取局部变量,在本质上,闭包是函数内部和函数外部连接起来的桥梁。

1. 如何产生闭包?

  * 当一个嵌套的内部()函数引用了嵌套的外部()函数的变量(函数), 就产生了闭包

2. 闭包到底是什么?

  * 使用chrome调试查看

  * 理解一: 闭包是嵌套的内部函数(绝大部分人)

  * 理解二: 包含被引用变量(函数)的对象(极少数人)

  * 注意: 闭包存在于嵌套的内部函数中

3. 产生闭包的条件?

  * 函数嵌套

  * 内部函数引用了外部函数的数据(变量/函数)

4. 常见的闭包
	* 将函数作为另一个函数的返回值
	* 将函数作为实参传递给另一个函数调用

闭包的作用

1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)

2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题:

  1. 函数执行完后, 函数内部声明的局部变量是否还存在?

	一般不存在,存在于闭包中的变量才可能存在

  2. 在函数外部能直接访问函数内部的局部变量吗?

	不能但是可以通过闭包操作它
1	function outer(){
2		var a = 1;
3		function inner(){
4			console.log(a);
5		}
6		return inner;
7	}
8	var inn = outer();
9	inn();//值为1

inner函数把它自己内部的语句(console.log(a)),和自己声明时所处的作用域(var a = 1;)一起封装成了一个密闭环境,我们称为“闭包”。

闭包天生存在,并不需要什么特殊的结构才存在,只不过我们必须要刻意地把函数放到其他的作用域中调用,才能明显的观察到闭包性质。

⦁	闭包外部环境并不是一成不变的。
1	function fun1() {
2		var a = 1;
3		function fun2() {
4			a ++;
5			console.log(a);
6		}
7		return fun2;
8	}
9	/*
10		对于fun2 外部环境a=1  内部语句a++; console.log(a);
11	
12	*/
13	var inn = fun1();
14	inn(); //a=2
15	inn(); //a=3
16	inn(); //a=4
17	inn(); //a=5
18	inn(); //a=6
19	inn(); //a=7

⦁ 函数每一次调用都产生一个全新的闭包,外部环境和内部语句都是全新的

1	function outer(a) {
2		function inner(b) {
3			console.log(a + b);
4		}
5		return inner;
6	}
7	
8	/*
9	第一个包
10	outer(10) 初始定义inner 外部环境a=10  和 内部语句console.log(10 + b)
11	*/
12	var fun1 = outer(10);
13	/*
14	第二个包:
15	外部环境a = 100
16	内部语句console.log(100 + b)
17	*/
18	var fun2 = outer(100);
19	
20	fun1(1);
21	fun2(1);

闭包的生命周期:

<!--
1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
2. 死亡: 在嵌套的内部函数成为垃圾对象时
-->
<script type="text/javascript">
  function fn1() {
    //此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
    var a = 2
    function fn2 () {
      a++
      console.log(a)
    }
    return fn2
  }
  var f = fn1()
  f() // 3
  f() // 4
  f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)
</script>

闭包的缺点及解决:

1. 缺点

  * 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长

  * 容易造成内存泄露

2. 解决

  * 能不用闭包就不用

  * 及时释放
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值