JavaScript进阶:函数、上下文与闭包的深度解析
发布时间: 2025-08-17 01:57:53 订阅数: 4 

# JavaScript 进阶:函数、上下文与闭包的深度解析
## 1. 函数声明与函数表达式
在 JavaScript 中,函数声明和函数表达式是定义函数的两种方式,它们之间存在一些重要的区别。
### 1.1 函数表达式
可以定义一个变量 `myFunc`,并为其分配一个函数,示例代码如下:
```javascript
var myFunc = function() {
// 函数体
};
myFunc();
```
注意,这是一个语句,语句末尾(右花括号之后)需要有分号。这个函数没有名称,因此其 `name` 属性将为空字符串,不能通过名称调用它,但可以通过分配给它的变量来执行。
### 1.2 函数声明与函数表达式的区别
函数声明会被提升,而函数表达式不会。以下示例可以说明这一点:
```javascript
funcDecl();
funcExpr();
function funcDecl() {
alert('function declaration');
}
var funcExpr = function() {
alert('function expression');
};
```
在这个示例中,`funcDecl()` 调用成功,而 `funcExpr()` 会引发错误。这是因为 `funcDecl()` 是函数声明,被提升到了作用域的顶部,而 `funcExpr()` 是函数表达式,不会被提升。
### 1.3 函数表达式赋值给对象属性
函数表达式不仅可以赋值给变量,还可以赋值给对象的属性,示例如下:
```javascript
var myObj = {
bar: function() {}
};
```
## 2. 函数作为回调
在处理事件、定时器或执行 Ajax 请求时,网页中的代码通常是异步的。异步编程中一个常见的概念是回调函数。
### 2.1 定时器示例
以定时器为例,可以通过向 `window.setTimeout()` 方法传递适当的持续时间值来设置定时器。当定时器到期时,该方法会调用提供的函数来通知你。示例代码如下:
```javascript
function hello() { alert('Hi there!'); }
setTimeout(hello, 5000);
```
在这个示例中,声明了一个名为 `hello` 的函数,并设置了一个 5 秒后触发的定时器。`setTimeout()` 方法的第一个参数是一个函数引用,当定时器到期时,`hello` 函数会被调用,因此 `hello` 函数被称为回调函数。
### 2.2 更优雅的写法
为了避免创建不必要的名称,可以使用内联匿名函数,示例如下:
```javascript
setTimeout(function() { alert('Hi there!'); }, 5000);
```
这种写法在 jQuery 代码中经常使用,当不需要将函数实例分配给顶级属性时,这种写法更加优雅。
## 3. `this` 关键字的含义
在面向对象语言中,通常会提供一种从方法内部引用对象当前实例的方式。在 JavaScript 中,也有类似的概念,使用 `this` 关键字来访问与函数关联的对象,但 JavaScript 中 `this` 的实现与面向对象语言有所不同。
### 3.1 `this` 的上下文
在 JavaScript 中,函数的上下文(`this`)不是由函数的声明方式决定的,而是由函数的调用方式决定的。同一个函数根据调用方式的不同,可能会有不同的上下文。
### 3.2 示例:摩托车对象
以下是一个摩托车对象的示例:
```javascript
var ride = {
make: 'Yamaha',
model: 'XT660R',
year: 2014,
purchased: new Date(2015, 7, 21),
owner: {
name: 'Spike Spiegel',
occupation: 'bounty hunter'
},
whatAmI: function() {
return this.year + ' ' + this.make + ' ' + this.model;
}
};
var bike = ride.whatAmI();
```
在这个示例中,当通过 `ride.whatAmI()` 调用函数时,函数的上下文(`this`)被设置为 `ride` 对象,因此 `bike` 变量的值为 `'2014 Yamaha XT660R'`。
### 3.3 顶级函数的上下文
顶级函数是 `window` 对象的属性,当作为顶级函数调用时,其函数上下文是 `window` 对象。
### 3.4 显式控制函数上下文
JavaScript 提供了 `call()` 和 `apply()` 方法来显式控制函数的上下文。`call()` 方法的第一个参数是要作为函数上下文的对象,其余参数成为被调用函数的参数;`apply()` 方法的工作方式类似,但第二个参数必须是一个数组,数组中的元素成为被调用函数的参数。
以下是一个示例代码:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Function Context Example</title>
</head>
<body>
<script>
var obj1 = { handle: 'obj1' };
var obj2 = { handle: 'obj2' };
var obj3 = { handle: 'obj3' };
var value = 'test';
window.handle = 'window';
function whoAmI(param) {
return this.handle + ' ' + param;
}
obj1.identifyMe = whoAmI;
alert(whoAmI(value));
alert(obj1.identifyMe(value));
alert(whoAmI.call(obj2, value));
alert(whoAmI.apply(obj3, [value]));
</script>
</body>
</html>
```
这个示例展示了不同调用方式下函数上下文的变化:
- 当直接作为顶级函数调用时,函数上下文是 `window` 对象。
- 当作为对象的属性调用时,对象成为函数上下文。
- 使用 `call()` 方法时,函数上下文被设置为传递给 `call()` 方法的第一个参数。
- 使用 `apply()` 方法时,函数上下文同样被设置为传递给 `apply()` 方法的第一个参数。
### 3.5 函数与对象的关系
不能简单地说一个函数是某个对象的方法,更准确的说法是:当对象作为函数调用的上下文时,函数充当该对象的方法。
以下是一个进一步说明的示例:
```javascript
alert(obj1.identifyMe.call(obj3));
```
即使通过 `obj1` 的属性引用函数,但这次调用的函数上下文是 `obj3`,这进一步强调了函数上下文是由调用方式决定的。
## 4. 闭包
闭包是一个函数实例与执行该函数所需的局部变量的组合。当函数声明时,它可以引用声明时作用域内的任何变量。即使声明点的作用域已经超出范围,这些变量仍然会与函数一起保留,形成闭包。
### 4.1 定时器示例
以下是一个使用定时器的闭包示例:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Closure Example</title>
</head>
<body>
<div id="display"></div>
<script src="../js/jquery-1.11.1.min.js"><
```
0
0
相关推荐










