5.1 函数初始化参数
5.1.1 函数初始化参数,默认值:undefined
// ES6 写法
function fn(a = 1, b) {
console.log(a, b);
}
fn(1); // 1 undefined
5.1.2 默认值的取值条件
function fn(a = 1, b) {
// 为什么打印的a打印的是1,而不是undefined?
console.log(a, b); // 1 2
}
fn(undefined, 2);
// 默认值的取值:
//1.如果形式参数是undefined,那就取值arguments实参的值
//2.如果形式参数不是undefined,实参是undefined,就取形式参数的值
//3.相当于,形参和实参有一方是undefined,那就取另一方的值作为默认值
// 例子
function fn(a, b) {
console.log(a, b); // 1 undefined
}
fn(1);
function fn(a = 1, b) {
console.log(a, b); // 1 1
}
fn(undefined, 1);
function fn(a, b) {
a = 3;
b = 4;
console.log(a, b); // 3 4
}
fn();
5.1.3 默认值与实参/形参的注意事项
function fn(a, b) {
a = 3; // 和fn(a = 3, b = 4)没区别
b = 4;
console.log(a, b); // 3 4
// 函数形式参数没有和实参产生对应关系,导致更改形参,实参的映射的值不发生改变,只有传递的实参和形参一一对应,才自动设置映射,二者才能随着改变
console.log(arguments[0],arguments[1]); // undefined undefined
}
fn();
5.1.4 低版本默认值的写法
function fn(a, b) {
var a = arguments[0] || 1;
var b = arguments[1] || 2;
console.log(a, b); // 1 2
}
fn();
// 三元运算符
function fn(a, b) {
var a = typeof(a) === 'undefined'? 2 : arguments[0];
var b = typeof(b) === 'undefined'? 2 : arguments[1];
console.log(a, b);
}
fn();
5.2 递归
5.2.1 递归的两个要素:
1.递归的规律
2.递归的出口,递归的return值一直在等待着计算结果,然后逐步返回上层的输出结果
5.2.2 递归例题
1. 输出N的阶乘
var n = parseInt(window.prompt('N=?'));
function factorial(n) {
// 错误机制
if(n <= 0) {
return '输入有误';
}
// 递归出口
if(n === 1) {
return 1;
}
return n * factorial(n - 1);
}
var num = factorial(n);
console.log(num);
// 执行过程形成的队列
factorial(5) = factorial(4) * 5;
factorial(4) = factorial(3) * 4;
factorial(3) = factorial(2) * 3;
factorial(2) = factorial(1) * 2;
factorial(1) = 1;
2. 输出斐波那契数列的N位
var n = parseInt(window.prompt('N=?'));
function fobo(n) {
// 错误机制
if(n < 1) {
return '输入有误';
}
if(n === 1 || n === 2) {
return 1;
}
return fobo(n - 1) + fobo(n - 2);
}
var num = fobo(n);
console.log(num);
// 执行过程中的形成的队列
fobo(5) = fobo(4) + fobo(3);
fobo(4) = fobo(3) + fobo(2);
fobo(3) = fobo(2) + fobo(1);
fobo(2) = 1;
fobo(1) = 1;
5.3 预编译
5.3.1 JavaScript引擎解析代码的执行顺序
- 检查通篇的代码语法错误,如果有则直接抛出语法错误(任何代码不执行),没有则进行第2步
- 进行预编译
- 解释一行,执行一行
5.3.2 暗示全局变量(imply global variable)
// 没有用var声明的变量是全局变量
// 在全局环境下,变量和方法都是挂载到window对象上,相当于window对象的属性和方法
var a = 3;
var b = 4;
--->
window = {
a : 3,
b : 4
}
// 为什么window.c一个不存在的属性是undefined,而直接打印console.log(c)一个不存在的属性报错(Uncaught ReferenceError: c is not defined)呢?
// 解析: 因为window.c是属于对象语法的性质,在通过window.c形式调用不存在的属性返回的就是undefined。
5.3.3 AO(activation object)活跃对象,函数上下文
1. 在AO中JavaScript引擎预编译的步骤
-
-
- 寻找函数的形参和变量声明
- 实参的值赋值给形参
- 寻找函数声明
- 执行函数
-
2. AO例题:
function test(a, b) {
console.log(a); // 1
c = 0;
var c;
a = 5;
b = 6;
console.log(b); // 6
function b(){};
function d(){};
console.log(b); // 6
}
test(1);
// 预编译
// AO = {
a:undefined,
---> 1
---> 5
b:undefined,
---> function b(){}
---> 6
c:undefined,
---> 0
d:function d(){}
}
5.3.4 GO(global object)全局上下文
1. 在GO中JavaScript引擎预编译的步骤
-
-
- 寻找变量声明
- 寻找函数声明
- 执行赋值
-
2. GO全局上下文与window对象相等 GO===window
3. 函数表达式和函数声明和对象的匿名函数提升问题
// 函数声明会在GO中JS引擎预编译期间进行提升
// 那么为什么函数表达式却不会呢?
var fn = function(){};
// 因为在GO的预编译期间,首先寻找变量声明,var fn就是变量声明,而var fn = function(){};虽然是函数声明的一种方式,但是它的执行顺序是,先创建一个变量fn,然后通过赋值的方式将function fn(){}函数在栈内存中的地址赋值给变量fn;所以function(){}函数是在JS引擎执行脚本语言代码的时候定义的。所以在预编译第二阶段寻找函数声明的时候找不到这个匿名函数。这个匿名函数是在JS引擎执行脚本语言是被定义的,GO是对象不会执行。
// 对象内部匿名函数显然不会提升,而且这两个匿名函数是在GO执行(JS引擎执行脚本语言)的时候定义的,和上面的是同一道理。
var obj = {
name:function(){},
sex:function(){},
age:18
}
// GO和AO是给谁看的?预编译是为了什么?函数一定会变量提升吗?
GO和AO的定义是让JS引擎看的,预编译是为了让JS引擎能够更好的去解析代码。上面的例子,在JS引擎执行脚本语言的时候,才将{}赋值给obj变量,obj变量内部存放是的指向{}的指针,obj的name属性存放着指向function(){}的指针,当这个对象被定义的时候,匿名函数才会被定义。反过来说,如果单纯的提升一个匿名函数,这个匿名函数也没有指向,对于代码整体来说没有任何意义。
// 预编译去寻找变量的目的是:这次执行先要确定有哪些变量,然后才好去赋值。
4. GO例题:
console.log(a, b); // function a(){} undefined
function a(){};
var b = function(){};
// 预编译
GO = {
b:undefind,
--> function(){}
a:function a(){},
}
5.3.5 AO与GO预编译
var b = 1;
function test() {
var a = 1;
var b = 2;
console.log(b); // 2
}
test();
// 预解析
GO = {
b: undefined,
--> 1
test:function test(){},
}
AO = {
a:undefined,
--> 1,
b:undefined,
--> 2,
}
var b = 3;
console.log(a); // function a(a) {}
function a(a) {
console.log(a); // function a() {}
var a = 2;
console.log(a); // 2
function a() {};
var b = 5;
console.log(b); // 5
}
a(1);
// GO = {
b:undefined,
--> 3,
a:function a(a) {}
}
// AO = {
a:undefined,
--> 1
--> function a(){}
--> 2
b:undefined,
--> 5
}
a = 1;
function test() {
console.log(a); // undefined
a = 2;
console.log(a); // 2
var a = 3;
console.log(a); // 3
}
test();
var a;
// GO = {
a:undefined,
--> 1
test:function() {},
}
// 因为AO环境中预解析时有a变量,所以就不去GO找了
// AO = {
a:undefined,
--> 2
--> 3
}
\
// 预编译不看语句判断,只看有没有变量声明和函数声明
function test() {
console.log(b); // undefined
if(a) {
var b = 2;
}
c = 3;
console.log(c); // 3
}
var a;
test();
a = 1;
console.log(a); // 1
// GO = {
a:undefined,
--> 1
test:function test(){},
c:undefined,
--> 3
}
// AO = {
b:undefined,
--> 2
}
function test() {
return a;
a = 1;
function a() {}
var a = 2;
}
console.log(test()); // function a() {}
// AO = {
a:undefined,
--> function a() {}
// 执行
--> return function a(){}
}
// 函数在没有声明返回值return的情况下,系统会在函数底部自动添加return undefined,终止函数执行。
function test() {
a = 1;
function a() {}
var a = 2;
return a;
}
console.log(test()); // 2
//AO = {
a:undefined,
--> function a(){}
--> 1
--> 2
--> return a
}
a = 1;
function test(e) {
function e() {}
arguments[0] = 2;
console.log(e); // 2
if(a) {
var b = 3;
}
var c;
a = 4;
var a;
console.log(b); // undefined
f = 5;
console.log(c); // undefined
console.log(a); // 4
}
var a;
test(1);
console.log(a); // 1
console.log(f); // 5
// GO = {
a:undefined,
--> 1
test:function test(e) {}
f:undefined,
--> 5
}
// AO = {
e:undefined,
--> 1
--> function e() {}
--> 2
a:undefined,
--> 4
b:undefined,
--> undefined
c:undefined,
}