一、参数默认值
- 没有传入实参时,形参默认值为undefined
- 可以在函数定义时,设置默认值(ES6写法)
// es5
function test(a, b) {
var a = arguments[0] || 1; // 使用arguments对象判断是否传入实参:
//使用typeof操作符检测某个实参是否为undefined来判断是否传入实参;
var a = typeof(arguments[0]) === 'undefined' ? 2 : arguments[0];
console.log(a); // 1
console.log(b); // undefined
}
// es6,在函数定义时,给形参设置默认值
function test(a = 1) {
console.log(a); // 1
}
test();
二、递归
- 递归总是走到出口的时候,再一步一步向上赋值后返回结果
- 递归的两个因素:1.找到计算的规律;2.找到出口,让这个递归函数可以结束。
/**
* ! :阶乘
* n! = n * (n-1)!
*/
function fact(n) {
if (n === 1) { //让递归结束的条件-出口
return 1;
}
return n * fact(n - 1);
}
fact(5); //5!
//5
三、预编译
- var变量声明提升(变量赋值不提升);
- 函数声明整体提升,函数表达式不会提升;
1. js运行时会进行三件事:
- 语法分析 :检查通篇的语法错误(语法分析会在代码执行前对代码进行通篇检查,以排除一些低级错误)
- 预编译 :发生在代码执行的前一刻
- 解释执行:解释一行,执行一行
2. 函数预编译的步骤:
GO(global object)全局对象,全局上下文:
- 找变量声明
- 找函数声明
- 执行
AO (activation object)活跃对象,函数上下文(执行期上下文):函数执行前一刻会生成AO, 函数执行完成后,自身的AO会销毁。
- 函数形参、变量声明
- 函数实参赋值给形参
- 函数声明
- 执行
3.案例分析:
1. 案例1:
test(); // 1
function test() {
console.log(1);
}
console.log(a); // undefined
var a = 2;
//打印结果:1 undefined
/**
* 过程:
* 一、预编译阶段: 1. var变量声明提升-> var a ; --> 这时候a的值为undefined
* 2. 函数声明提升-> function test() {..}
* 二、函数执行:test();
* 三、变量赋值:a = 2;
*/
2. 案例2:
console.log(a);
function a(a) {
var a = 10;
var a = function() {
}
}
var a = 1;
//打印结果:function a(){...}
/**
* 过程:
* 一、预编译阶段: 1. var变量声明提升-> var a ; --> 这时候a的值为undefined
* 2. function a() {...} -> 这时候a变为function a() {...}
* 二、函数执行: console.log(a); -> function a() {...}
* 三、变量赋值:a = 1;
*/
3. 案例3:
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() {}
}
test(2);
//打印结果:function a() {} 1 1 function() {}
/**
* 过程:
* test(2)执行,产生test(2)的AO(执行期上下文)
* AO = {
* a:undefined -> a = 2 -> a:function a() {...} -> a = 1
* b:undefined -> b = function b() {...}
* d:undefined -> d:function b() {...}
* }
**/
4. 案例4:
/**
* 1. GO = {
* a : function a() {}
* }
* 2. a函数执行前生成自身的AO
* AO = {
* b : function b() {}
* }
* 3. b函数执行前生成自身的AO
* AO = {
* c : function c() {}
* }
* 4. c函数执行前生成自身的AO
* AO = {}
*/
function a() {
function b() {
function c() {
}
c();
}
b();
}
a();
/**
* 过程:
*
* a定义:a.[[scope]] -> 0: GO
* a执行:a.[[scope]] -> 0: a的AO
* 1:GO
* b定义:b.[[scope]] -> 0:a的AO
* 1:GO
* b执行:b.[[scope]] -> 0: b的AO
* 1:a的AO
* 2:GO
* c定义:c.[[scope]] -> 0: b的AO
* 1:a的AO
* 2:GO
* c执行:c.[[scope]] -> 0:c的AO
* 1:b的AO
* 2:a的AO
* 3:GO
* c结束:c.[[scope]] -> 0:b的AO
* 1:a的AO
* 2:GO
* b结束:b.[[scope]] -> 0:a的AO
* 1:GO
* c.[[scope]]销毁
* a结束:a.[[scope]] -> 0: GO
* b.[[scope]] -> 销毁
*/
四、暗示全局变量
- 在全局环境下,用var声明的变量会成为window对象上的属性;
- 未声明直接赋值的变量,会存到window里面,成为window对象上的属性;
// 1. 全局var声明
var a = 1;
b = 2;
console.log(window.a); // 1
console.log(window.b); // 2
// 2.函数内部的var声明
function test() {
// 1. var a ; 2. b = 1 ; 3. a = b;
// 所以这里的b是全局的,它的作用域是window,在函数外部也可以访问,
// a在函数内部声明了,所以a的作用域为test(),在函数外部不可访问
var a = b = 1;
}
test();
// 访问对象中不存在的属性,会返回undefined
console.log(window.a); // undefined
console.log(window.b); // 1
1. 案例1:
function test() {
// 函数内部可用return语句终止函数执行,返回对应的值
return a;
a = 1;
function a() {}
var a = 2;
}
console.log(test()); // function a() {}
// 打印结果:function a() {}
/**
* 思路:
* test()执行,产生test()的AO(执行期上下文)
* 1. 函数声明提升: var a ; 2. 函数声明提升: function a() {...}
* AO = {
* a:undefined -> a:function a() {...} -> 函数终止执行,返回function a() {}
* }
**/
2. 案例2:
a = 1;
function test(e) {
function e() {}
arguments[0] = 2; //实参的第一位赋值为2
console.log(e); // 2
// 此时变量a的值为undefined,不执行if语句内容
if (a) { // 函数内部重新声明了var a; a为undefined,所以没进判断语句,b也为undefined
var b = 3;
}
var c;
a = 4;
var a;
console.log(b); // undefined 上面的判断没进去,所以b只声明没赋值,为undefined
// 暗示全局变量
f = 5;
console.log(c); // undefined c只声明,没赋值,所以为undefined
console.log(a); // 4 var a ; -> a = 4 ;
}
var a;
test(1);
console.log(a); // 1 //在函数内部,用了var声明a,所以函数内部的a,在函数外部访问不到,a = 1;
console.log(f); // 5 // 函数内部,f没用var声明,所以f是挂载在window下的,外面可访问
//打印结果:2 undefined undefined 4 1 5
/**
* 思路:
* GO = {
* a:undefined -> 1
* test: function test(e){...}
* f:undefined -> 5
* }
*
* AO = {
* e : undefined -> 1 -> function e() {} -> 2
* b : undefined
* c : undefined
* a : undefined -> 4
* }
*/
六、面试题
1. 题目1:
var a = false + 1;
console.log(a);
// 打印结果: 1
// Number(false) = 0; 0 + 1 = 1;
2. 题目2:
var b = false == 1;
console.log(b);
//打印结果: false
// var a ; -> false == 1 : false ; -> a = false
3. 题目3:
if (typeof(a) && (-true) + (+undefined) + '') {
console.log('通过了'); // 执行
} else {
console.log('没通过');
}
// 打印结果: '通过了'
/**
* 思路:
* && 左边:typeof(a) = "number";
* && 右边:
* 1. (-true) -> (-Number(true)) -> -1
* 2. (+undefined) -> (+Number(undefined)) -> NaN
* 3. -1 + NaN + '' -> 'NaN'
* 判断结果:Boolean('NaN') -> true
*/
4. 题目4:
console.log(!!' ' + !!'' - !!false || '通过了');
//打印结果:1
/**
* 思路:
* null、undefined、0、''、 NaN、false都为假,除了以上6个,其他的都为真;
* 1. 遇到 &&、|| 、! 会转成Boolean类型
* 2. Boolean(' ') : true -> !!true: true
* 3. Boolean('') : false -> !!false: false
* 4. !!false -> false
* 5. true + false - false -> Number(true) = 1 , Number(false) = 0 -> 1 + 0 - 0 = 1
* 6. 1 || '通过了' -> 1 (||或运算符:遇到假就往后走;遇到真或者走到最后就返回当前值;)
*
*/