一、参数默认值
1、指定默认值的方式,
在形参后加等号(=)即可
function test(a = 1, b = 2) {};
2、一些问题
1、第一个参数使用默认值,第二个参数使用实参传递过来的值,该怎么实现?
// 需求:a使用默认值,b使用实参传递过来的值
function test(a = 1, b) {
}
test(undefined, 2); // 在第一个实参那里传递过去undefined就可以做到。
为什么会这样呢?我们再看一个例子
function test(a = 1, b = undefined) {
console.log(a, b); // 结果为 1, 2
}
test(undefined, 2);
得出结论:形参与实参都有值的话,尽量不去选择undefined的值,如果实参传递undefined,形参默认值不为undefined,则用形参默认值;如果实参传递的不是undefined,形参默认值是undefined,那么则选用实参传递的值。
所以我们例1可以通过实参传递undefined,形参有默认值这样子,实现第一个参数用默认值,第二个参数用实参。
2、形参默认值和实参的值都不是undefined,那么用实参的值。
3、解决兼容性问题
因为参数默认值是es6之后才有的,所以如果是不支持es6的浏览器该怎么做呢?
核心都是使用arguments来判断传递的实参是不是undefined。以下展示两种简单合适的写法。
最好还是使用typeof来判断一个实参的值是不是undefined,直接判断false有可能会出问题,比如我就是想要0这个值,而不是默认值。
// 第一种,通过逻辑运算符来实现
// a的默认值是1,b的默认值是2
function test(a, b) {
// 这里最好使用typeof来判断一下,不然有可能我想要的是0,结果给我整成默认值了
var a = arguments[0] || 1;
var b = arguments[1] || 2;
console.log(a, b);
}
test(undefined, 5);
// 第二种,通过三目运算符来实现
function test(a, b) {
// 使用三目运算符来判断传递过来的实参是不是undefined最合适
var a = typeof(arguments[0]) !== 'undefined' ? arguments[0] : 1;
var b = typeof(arguments[1]) !== 'undefined' ? arguments[1] : 2;
console.log(a, b);
}
test(undefined, 5);
二、递归(函数自己调用自己)
1、递归两个核心要素
1、找到递归的规律
2、找到出口
2、递归的一些小案例
// 定义一个函数,从wp接收一个n,算出n的阶乘
var num = parseInt(window.prompt('请输入一个需要阶乘的n'));
function fact(n) {
// 出口
if (n === 1) {
return 1;
}
// 规律
return n * factorial(n - 1);
}
console.log(factorial(num));
// 定义一个函数,从wp接收一个n,算出斐波那契数列的第n位
var num = parseInt(window.prompt('请输入需要查找第几位斐波那契数:'));
function fib(n) {
// 出口
if (n === 2 || n === 1) {
return 1;
}
// 规律
return fib(n - 1) + fib(n - 2);
}
console.log(fib(num));
3、递归的问题
递归因为自己调用自己,所以函数一直在嵌套,所以性能、效率方面不是很好。所以递归慎用。
三、预编译前奏
1、JS引擎解析代码步骤
1、检查通篇的语法错误。如果某个地方语法错误,全篇都不执行。
2、预编译的过程。
3、解释一行,执行一行。
2、一些现象引起我们的思考
1、函数可以写在执行语句的后面。
test(); // 正常执行
function test() {};
2、变量声明如果在使用之前,变量值是undefined。
console.log(a); // 打印undefined
var a = 10;
所以我们得出结论:
函数声明整体提升,变量只有声明提升,赋值是不提升的。
我们之前强调,var a = 10;
是有两个步骤的,第一步先声明变量a,第二步为a赋值10。提升只有第一步。
但是上面的结论太浅显了,我们抛出一个问题:
console.log(a);
function a(a) {
var a = 10;
var a = function() {};
}
var a = 2;
// 请问上面的console.log(a)打印什么?
很明显,只使用上面的知识我们解决不了问题。所以我们得继续深入学习预编译。但是现在我们先学习暗示全局变量。
四、暗示全局变量(imply global variable)
1、在script标签里直接声明的变量
在script声明的变量中,我们可以使用var a = 1;
,也可以不使用var关键字,如a = 1
;这两种方式声明都是会在window上面挂载a变量。
var a = 1;
b = 2;
console.log(window.a); // 1
console.log(window.b); // 2
2、在函数体里声明的变量
在函数体里声明的变量,如果使用var关键字声明,那就是该函数的局部变量,如果没有关键字,那就是全局变量了,挂载在window上面。不用关键字声明变量是暗示全局变量的一种体现。
function test() {
var a = b = 1;
}
test();
console.log(a); // 报错,因为a在test函数中,外面访问不到里面的变量
console.log(b); // 打印1,因为b已经为全局变量了,挂载在window上面,所以可以访问
console.log(window.b); //打印1
五、预编译开始
我们直接开始讲步骤,然后看一道案例题。
1、创建AO对象(activation object,活跃对象,也叫函数上下文)
2、寻找函数中所有的形参与变量声明,然后添加到AO对象中,值为undefined
3、形参与实参相统一
4、寻找函数中所有的函数声明,注意是普通的函数声明function a(){}
,字面量函数声明不算var a = function(){}
,如果有与变量重名的,函数覆盖变量
5、开始一步一步执行代码
// 预编译案例
function test(a) {
console.log(a); // function a(){},
var a = 1;
console.log(a); // 1
function a() {};
console.log(a); // 1
var b = function() {};
console.log(b); // function(){}
function d(){};
console.log(d); // function d(){};
}
test(2);
// 预编译过程
AO = {
a: undefined, -> 2, -> function a(){},
b: undefined,
d: function d(){},
}
练习一道题:
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(){}
}
如果是在全局作用域下,我们的过程也是差不多的,只有没有形参了而已。
1、创建GO对象(global object,全局上下文)
2、寻找所有的变量声明,并且添加到GO中,值为undefined
3、寻找函数中所有的函数声明,注意是普通的函数声明function a(){}
,字面量函数声明不算var a = function(){}
,如果有与变量重名的,函数覆盖变量
4、一步一步执行代码
注意:GO就是window
案例:
var a = 1;
function a() {
console.log(2);
}
console.log(a); // 1
// 预编译过程
GO = {
a: undefined -> function a(){} -> 1,
}
练习一道题:
var b = 3;
console.log(a); // function 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(){},
}
AO = {
a: undefined -> 1 -> function a(){} -> 2,
b: undefined -> 5,
}
六、作业题
第一题:
function test() {
console.log(b); // undefined
if (a) {
var b = 2; // 不执行,以为a要拿到外面的a,是undefined
}
c = 3; // window.c = 3;
console.log(c); // 3;
}
var a;
test();
a = 1;
console.log(a); // 1
// 预编译过程
GO = {
a: undefined -> 1,
test: function test(){},
c: 3
}
AO = {
b: undefined,
}
// 最终结果 undefined 3 1
第二题
function test() {
return a;
a = 1;
function a(){}
var a = 2;
}
console.log(test()); // function a(){};
// 预编译过程
AO = {
a: undefined -> function a(){},
}
第三题
function test() {
a = 1;
function a() {}
var a = 2;
return a;
}
console.log(test()); // 2
// 预编译过程
AO = {
a: undefined -> function a(){} -> 1 -> 2,
}
第四题
a = 1;
function test(e) {
function e(){}
arguments[0] = 2; // 这一步会影响到e吗?应该是会的。前面好像有讲过
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);
// 预编译过程
GO = {
a: undefined -> 1,
f: 5
}
AO = {
e: undefined -> 1 -> function e(){} -> 2,
b: undefined,
c: undefined,
a: undefined -> 4,
}