Web/04-JavaScript基础/09-函数.md
2019-12-11 15:22:20 +08:00

465 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 前言
关于函数的核心内容:
- 函数有哪几种定义和调用方式
- this函数内部的 this 指向、如何改变 this 的指向。
- 函数的严格模式
- 高阶函数:函数作为参数传递、函数作为返回值传递
- 闭包:闭包的作用
- 递归:递归的两个条件
- 深拷贝和浅拷贝的区别
## 函数的介绍
函数:就是将一些功能或语句进行**封装**,在需要的时候,通过**调用**的形式,执行这些语句。
- **函数也是一个对象**
- 使用`typeof`检查一个函数对象时会返回function
**函数的作用**
- 将大量重复的语句抽取出来,写在函数里,以后需要这些语句的时候,可以直接调用函数,避免重复劳动。
- 简化编程,让编程模块化。高内聚、低耦合。
来看个例子:
```javascript
console.log("你好");
sayHello(); // 调用函数
// 定义函数
function sayHello(){
console.log("欢迎");
console.log("welcome");
}
```
## 函数的定义
### 方式一:函数声明(命名函数)
使用`函数声明`来创建一个函数(也就是 function 关键字)。语法:
```javascript
function 函数名([形参1,形参2...形参N]){ // 备注:语法中的中括号,表示“可选”
语句...
}
```
举例:
```javascript
function fun1(a, b){
return a+b;
}
```
解释如下:
- function是一个关键字。中文是“函数”、“功能”。
- 函数名字:命名规定和变量的命名规定一样。只能是字母、数字、下划线、美元符号,不能以数字开头。
- 参数:可选。
- 大括号里面,是这个函数的语句。
PS在有些编辑器中方法写完之后我们在方法的前面输入`/**`,然后回车,会发现,注释的格式会自动补齐。
### 方式二:函数表达式(匿名函数)
使用`函数表达式`来创建一个函数。语法:
```javascript
var 变量名/函数名 = function([形参1,形参2...形参N]){
语句....
}
```
举例:
```javascript
var fun2 = function() {
console.log("我是匿名函数中封装的代码");
};
```
从方式二的举例中可以看出:所谓的“函数表达式”,其实就是将匿名函数赋值给一个变量。
### 方式三:使用构造函数 new Function()
使用构造函数`new Function()`来创建一个对象。这种方式,用的少。
语法:
```javascript
var 变量名/函数名 = new Function('形参1', '形参2', '函数体');
```
注意Function 里面的参数都必须是**字符串**格式。也就是说,形参也必须放在**字符串**里;函数体也是放在**字符串**里包裹起来,放在 Function 的最后一个参数的位置。
代码举例:
```javascript
var fun3 = new Function('a', 'b', 'console.log("我是函数内部的内容"); console.log(a + b);');
fun3(1, 2); // 调用函数
```
打印结果:
```
我是函数内部的内容
3
```
**分析**
方式3的写法很少用原因如下
- 不方便书写:写法过于啰嗦和麻烦。
- 执行效率较低:首先需要把字符串转换为 js 代码,然后再执行。
### 总结
1、**所有的函数,都是 `Fuction` 的“实例”**(或者说是“实例对象”)。函数本质上都是通过 new Function 得到的。
2、函数既然是实例对象那么**函数也属于“对象”**。还可以通过如下特征,来佐证函数属于对象:
1我们直接打印某一个函数比如 `console.log(fun2)`,发现它的里面有`__proto__`。(这个是属于原型的知识,后续再讲)
2我们还可以打印 `console.log(fun2 instanceof Object)`,发现打印结果为 `true`。这说明 fun2 函数就是属于 Object。
## 函数的调用
### 方式1普通函数的调用
函数调用的语法:
```javascript
函数名();
```
或者:
```
函数名.call();
```
代码举例:
```javascript
function fn1() {
console.log('我是函数体里面的内容1');
}
function fn2() {
console.log('我是函数体里面的内容2');
}
fn1(); // 调用函数
fn2.call(); // 调用函数
```
### 方式2通过对象的方法来调用
```javascript
var obj = {
a: 'qianguyihao',
fn2: function() {
console.log('千古壹号,永不止步!');
},
};
obj.fn2(); // 调用函数
```
如果一个函数是作为一个对象的属性保存,那么,我们称这个函数是这个对象的**方法**。
PS关于函数和方法的区别本文的后续内容里有讲到可以往下面翻。
### 方式3立即执行函数
代码举例:
```javascript
(function() {
console.log('我是立即执行函数');
})();
```
立即执行函数在定义后,会自动调用。
PS关于立即执行函数本文的后续内容里有讲到可以往下面翻。
上面讲到的这三种方式,是用得最多的。接下来讲到的三种方式,暂时看不懂也没关系,可以等学完其他的知识点,再回过头来看。
### 方式4通过构造函数来调用
代码举例:
```javascript
function Fun3() {
console.log('千古壹号,永不止步~');
}
new Fun3();
```
这种方式用得不多。
### 方式5绑定事件函数
代码举例:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="btn">我是按钮,请点击我</div>
<script>
var btn = document.getElementById('btn');
//2.绑定事件
btn.onclick = function() {
console.log('点击按钮后,要做的事情');
};
</script>
</body>
</html>
```
这里涉及到DOM操作和事件的知识点。
### 方式6定时器函数
代码举例:(每间隔一秒,将 数字 加1
```javascript
let num = 1;
setInterval(function () {
num ++;
console.log(num);
}, 1000);
```
这里涉及到定时器的知识点。
## 函数的参数:形参和实参
函数的参数包括形参和实参。先来看下面的图就很好懂了:
![](http://img.smyhvae.com/20180118_1130.png)
注意:实际参数和形式参数的个数,一般要相同。
举例:
```javascript
sum(3,4);
sum("3",4);
sum("Hello","World");
//函数:求和
function sum(a, b) {
console.log(a + b);
}
```
控制台输出结果:
```
7
34
helloworld
```
**形参:**
- 定义函数时,可以在函数的`()`中来指定一个或多个形参。
- 多个形参之间使用`,`隔开,声明形参就相当于在函数内部声明了对应的变量,但是并不赋值。
**实参**
- 在调用函数时,可以在函数的 `()`中指定实参。
- 实参将会赋值给函数中对应的形参。
举例:
```javascript
// 调用函数
sum(3,4);
sum("3",4);
sum("Hello","World");
// 定义函数:求和
function sum(a, b) {
console.log(a + b);
}
```
控制台输出结果:
```
7
34
helloworld
```
**实参的类型:**
函数的实参可以是任意的数据类型。
调用函数时,解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查。
**实参的数量:**
调用函数时,解析器也不会检查实参的数量:
- 如果实参的数量多余形参的数量,多余实参不会被赋值。
- 如果实参的数量少于形参的数量,则没有对应实参的**形参将是undefined**。例如:
![](http://img.smyhvae.com/20180314_2030.png)
## 函数的返回值
举例:
```javascript
console.log(sum(3, 4));
//函数:求和
function sum(a, b) {
return a + b;
}
```
return 的作用是结束方法。
注意:
- return 后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
- 在函数中return后的语句都不会执行函数在执行完 return 语句之后停止并立即退出)
- 如果return语句后不跟任何值就相当于返回一个undefined
- 如果函数中不写return则也会返回undefined
- 返回值可以是任意的数据类型,可以是对象,也可以是函数。
## 函数名、函数体和函数加载问题(重要,请记住)
我们要记住:**函数名 == 整个函数**。举例:
```javascript
console.log(fn) == console.log(function fn(){alert(1)});
//定义fn方法
function fn(){
alert(1)
};
```
我们知道,当我们在调用一个函数时,通常使用`函数()`这种格式;但此时,我们是直接使用`函数`这种格式,它的作用相当于整个函数。
**函数的加载问题**JS加载的时候只加载函数名不加载函数体。所以如果想使用内部的成员变量需要调用函数。
### fn() 和 fn 的区别【重要】
- `fn()`:调用函数。调用之后,还获取了函数的返回值。
- `fn`:函数对象。相当于直接获取了函数对象。
## 立即执行函数
现有匿名函数如下:
```javascript
function(a, b) {
console.log("a = " + a);
console.log("b = " + b);
};
```
立即执行函数如下:
```javascript
(function(a, b) {
console.log("a = " + a);
console.log("b = " + b);
})(123, 456);
```
立即执行函数:函数定义完,立即被调用,这种函数叫做立即执行函数。
立即执行函数往往只会执行一次。为什么呢?因为没有变量保存它,执行完了之后,就找不到它了。
## 方法
函数也可以成为对象的属性。**如果一个函数是作为一个对象的属性保存,那么,我们称这个函数是这个对象的方法**。
调用这个函数就说调用对象的方法method。函数和方法有什么本质的区别吗它只是名称上的区别并没有其他的区别。
函数举例:
```javascript
// 调用函数
fn();
```
方法举例:
```javascript
// 调用方法
obj.fn();
```
我们可以这样说,如果直接是`fn()`,那就说明是函数调用。如果是`XX.fn()`的这种形式,那就说明是**方法**调用。
## 构造函数、原型对象、对象实例之间的关系
暂略。
## 我的公众号
想学习<font color=#0000ff>**代码之外的技能**</font>?不妨关注我的微信公众号:**千古壹号**id`qianguyihao`)。
扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外:
![](http://img.smyhvae.com/2016040102.jpg)