定义函数
【见】定义函数:https://www.yuque.com/tqpuuk/yrrefz/vzpags
调用函数
一、定义一个函数并不会自动地执行它。
定义了函数仅仅是赋予函数以名称并明确函数被调用时该做什么。
调用函数才会以给定的参数真正执行这些操作。
二、调用函数的方式
1、函数递归,就是说函数可以调用其本身。
2、某些地方需要动态调用函数。
3、函数的实参数量是变化的。
4、调用函数的上下文需要指定为在运行时确定的特定对象。
5、函数本身是对象,因此这些对象也有方法。
apply()方法可以实现这些目的。
函数提升
一、函数一定要处于调用它们的域中
1、函数域是指函数声明时所在的地方,或者函数在顶层被声明时指整个程序。
二、函数提升仅适用于函数声明,而不适用于函数表达式。
/* 函数声明*/
foo() // "bar"
function foo () {
console.log('bar')
}
/* 函数表达式 */
baz() // 类型错误:baz不是一个函数
var baz = function () {
console.log('bar2')
}
| 【示例】以下代码执行后,num的值是?```javascript var foo=function(x,y){ return x-y; } function foo(x,y){ return x+y; } var num=foo(1,2);
A. -1<br />- 3<br />- 1<br />- 2<br />
答案:A<br />解析:知识点:提升hoisting<br />以上代码会被javascript编译器处理为```javascript
//variable hoisting变量提升
var foo;//foo#1
var num;
//function declaration hoisting函数声明提升
function foo(x, y){//foo#2
return x + y;
}
//function expression NOT hoisted函数表达式不会被提升
foo =function(x, y){//foo#3
return x - y;
}
num = foo(1, 2);//这里使用foo#3
规则
1. 变量声明、函数声明都会被提升到作用域顶处;
2. 当出现相同名称时,优先级为:变量声明(foo#1) < 函数声明(foo#2) < 变量赋值(foo#3)
因此,num计算时是用的foo#3。答案为-1。 |
| —- |
返回值
一、函数可以将一个值返回到调用代码中作为结果。
【示例1】最简单的例子是将两个值相加的函数:
function sum(a, b) {
return a + b;
}
let result = sum(1, 2);
alert( result ); // 3
二、指令return可以在函数的任意位置。当执行到达时,函数停止,并将值返回给调用代码(分配给上述代码中的result)。
三、在一个函数中可能会出现很多次return。
【示例1】
function checkAge(age) {
if (age >= 18) {
return true;
} else {
return confirm('Got a permission from the parents?');
}
}
let age = prompt('How old are you?', 18);
if ( checkAge(age) ) {
alert( 'Access granted' );
} else {
alert( 'Access denied' );
}
四、只使用return但没有返回值也是可行的。但这会导致函数立即退出。
【示例1】
function showMovie(age) {
if ( !checkAge(age) ) {
return;
}
alert( "Showing you the movie" ); // (*)
// ...
}
1、在上述代码中,如果checkAge(age)返回false,那么showMovie将不会运行到alert。
五、空值的return或没有return的函数返回值为undefined
1、如果函数无返回值,它就会像返回undefined一样:
【示例1】
function doNothing() { /* 没有代码 */ }
alert( doNothing() === undefined ); // true
2、空值的return和return undefined等效:
function doNothing() {
return;
}
alert( doNothing() === undefined ); // true
六、不要在return与返回值之间添加新行
【示例1】对于return的长表达式,可能你会很想将其放在单独一行,如下所示:
return
(some + long + expression + or + whatever * f(a) + f(b))
1、但这不行,因为 JavaScript 默认会在return之后加上分号。上面这段代码和下面这段代码运行流程相同:
return;
(some + long + expression + or + whatever * f(a) + f(b))
2、因此,实际上它的返回值变成了空值。
3、如果我们想要将返回的表达式写成跨多行的形式,那么应该在return的同一行开始写此表达式。或者放上左括号:
【示例1】
return (
some + long + expression
+ or +
whatever * f(a) + f(b)
)
1、然后它就能像我们预想的那样正常运行了。
| 【示例】假设output是一个函数,用来输出一行文本。下面这行语句输出结果是什么?```javascript output(typeof (function() {output(“Hello World!”)})());
A. undefined Hello World!<br />- Object<br />- string<br />- function<br />- Hello World! undefined<br />
答案:D<br />解析:1.先立即执行匿名函数,输出Hello World! <br />2.函数执行后没有写return语句,所以默认返回undefined,则输出未定义 |
| --- |
| |
<a name="UXkrx"></a>
# 执行上下文、作用域(链)、函数堆栈
<a name="sBIMl"></a>
## 执行上下文
【见】执行模型:[https://www.yuque.com/tqpuuk/yrrefz/cm3vwm](https://www.yuque.com/tqpuuk/yrrefz/cm3vwm)
<a name="eHcnW"></a>
## 递归和堆栈
【见】递归和堆栈:[https://www.yuque.com/tqpuuk/yrrefz/mubxfz](https://www.yuque.com/tqpuuk/yrrefz/mubxfz)
<a name="QTCro"></a>
## 嵌套函数和闭包
【见】变量作用域:[https://www.yuque.com/tqpuuk/yrrefz/ulm8n9](https://www.yuque.com/tqpuuk/yrrefz/ulm8n9)<br />【见】闭包:[https://www.yuque.com/tqpuuk/yrrefz/mzldq4](https://www.yuque.com/tqpuuk/yrrefz/mzldq4)
<a name="IOLMj"></a>
## 多层嵌套函数
一、函数可以被多层嵌套。<br />二、闭包可以包含多个作用域,他们递归式的包含了所有包含它的函数作用域。这个称之为作用域链。<br />【实例1】
```jsx
function A(x) {
function B(y) {
function C(z) {
console.log(x + y + z);
}
C(3);
}
B(2);
}
A(1); // logs 6 (1 + 2 + 3)
1、C可以访问B的y和A的x。这是因为:
(1)B形成了一个包含A的闭包,B可以访问A的参数和变量
(2)C形成了一个包含B的闭包
(3)B包含A,所以C也包含A,C可以访问B和A的参数和变量。换言之,C用这个顺序链接了B和A的作用域。
2、反过来却不是这样。A不能访问C,因为A看不到B中的参数和变量,C是B中的一个变量,所以是B私有的。
命名冲突
一、当同一个闭包作用域下两个参数或者变量同名时,就会产生命名冲突。更近的作用域有更高的优先权,所以最近的优先级更高,最远的优先级最低,这就是作用域链。链的第一个元素就是最里面的作用域,最后一个元素便是最外层的作用域。
【实例1】以下例子,命名冲突发生在return x上,inside的参数x和outside变量x发生了冲突。这里的作用域链是{inside, outside, 全局对象}。因此inside的x具有最高优先权,返回了20(inside的x)而不是10(outside的x)
function outside() {
var x = 5;
function inside(x) {
return x * 2;
}
return inside;
}
outside()(10); // returns 20 instead of 10