重要性
time 0
预编译等知识点是面试考的最多的,
初始化参数,默认值
time 1m58s
默认值是undefined
time 3m33s
1 2
time 4m46s
我想给b赋值,但a用默认值,这样就不好办了
function test(a=1,b){
console.log(a);
console.log(b);
}
test(2);
/*2
undefined
*/
time 5m
undefined就可以了
function test(a=1,b){
console.log(a);
console.log(b);
}
test(undefined,2);
/*1
2
*/
一、参数默认值
1没有传入实参时,形参默认值为undefined
2可以在函数定义时,设置默认值(ES6写法)
1 undefined
1 2
1 2
1 2
arguments可能是undefined,a的默认值也有可能是undefined,谁不是undefined就取谁,都是undefined就是undefined
没有特地赋予默认值就是undefined
test(1,2)是给arguments[0]=1,argument[1]=2赋值,arguments和实参a,b是影射关系,就对比啊a、b、argument[0]、argumet[1]的值,对比实参、arguments的值
主要取决于arguments的值,怎么给arguments
function test(a = 1, b) {
console.log(a); // 1
console.log(b); // undefined
}
test();
function test(a = 1, b) {
console.log(a); // 1
console.log(b); // undefined
}
test(2,3);
2,3
function test(a = 1, b) {
console.log(a); // 2
console.log(b); // 3
arguments[0]=1;
console.log(a)//2
console.log(arguments[0])//1
a=6;
console.log(arguments[0]);//1
}
test(2,3);
是映射关系,只有在函数传实参时是映射关系,之后再函数内部改a,b,argumets就没用了
因为a、b都是形参,函数里面的ab都是形参,test(2,3) 2,3是实参,argumets与实参有关与形参无关,argumentes与实参有对应关系
错了有映射关系,但默认参数影响了
function test(a , b) {
console.log(a); // 2
console.log(b); // 3
arguments[0] = 5;
console.log('a',a); //5
console.log(arguments[0]); //5
a = 6;
console.log(arguments[0]); //6
}
test(2, 3);
console.log('-----------')
function test2(a=1 , b) {
console.log(a); // 2
console.log(b); // 3
arguments[0] = 5;
console.log('a',a); //2
console.log(arguments[0]); //5
a = 6;
console.log(arguments[0]); //5
}
test2(2, 3);
function test(a, b, c) {
arguments[0]=6;
console.log(a)//6
a = 3;
c = 5;
console.log(arguments[0]); // 3
console.log(arguments[2]); // undefined
}
test(1, 2);
设置默认值的方法
给形参直接赋值(a=1)是es6的写法,低版本浏览器可能不兼容
1.ES6写法。
2.使用typeof操作符检测某个参数是否等于undefined,如果是则意味着没有传入这个参数,那就给它赋一个值。
3.使用arguments对象判断是否传入实参:
function test1(a, b) {
///var a=a||2; //不推荐这么写,因为与实参是一一对应的
var a = arguments[0] || 2;
var b = arguments[1] || 3
console.log(a + b);
}
test(); // 5
function test2(a, b) {
var a = typeof(arguments[0]) === 'undefined' ? 2 : arguments[0];
var b = typeof(arguments[1]) === 'undefined' ? 3 : arguments[1];
console.log(a + b);
}
test2(); // 5
二、递归
1找出规律。
2找出递归出口。
先找规律,再找出口,避免无限循环
return1结果不是1,因为有队列
当fact(1)有确定值1时,fact(2)的值确定,之后fact345…
递归慎用,for循环更好
三、预编译
处理代码过程
1 检查通篇的语法错误
语法分析也叫语义分析,语法分析他是通篇执行的一个过程,比如我写了好多行代码,这些代码在执行的时候他是解释一行执行一行,但是在执行之前系统执行的第一步它会扫描一遍,看看有没有低级的语法错误,比如少些个符号,带个特殊字符之类的,它会通篇扫描一遍,但是不执行,这个通篇扫描的过程叫语法分析,通篇扫描之后它会预编译,然后在解释一行执行一行,也就是解释执行。
console.log(a);
console.log(a);//中文的; 不是英文的
console.log(a);
打印Uncaught ReferenceError: a is not defined
console.log(a);
// console.log(a);//中文的; 不是英文的
console.log(a);
打印Uncaught ReferenceError: a is not defined
结论:
先检查语法错误,如果有语法错误 ,先暴露出来问题 ,不执行接下来的步骤
未定义变量不属于语法层面的错误,属于语法应用错了,语法应用的错误不会在一开始执行报错,语法应用的错误会阻碍另一个语法错误的执行 。
短路原则:
当有语法错误时,发现第一个语法错误时,打印发现的错误, 之后的语法错误不检查。
原因:假设代码很长,错误很多,如果一开始就有错误,之后再检查或执行所有代码的那就太蠢了,看到结果的速度会很慢,并且结果并不会改变,也造成了能源的浪费,费电费、费cpu。
1.5 预编译的过程
2 解释一行 执行一行
什么是预编译?
预编译打个比方就像对代码就行初步的加工,在就行处理执行。
过程
ao
1 先寻找并提取变量声明(变量声明包括形参)
2再把实参的参数值赋值给行参
3再寻找函数体声明,赋值函数体
4 再执行函数
go
1找变量
2找函数声明
3执行
简化
Ao
形参声明、变量声明
形参赋值
函数声明
Go
变量声明
函数声明
预编译过程
1.通篇的检查语法错误
1.5 预编译的过程
2.解释一行执行一行
2. 函数执行之前进行的步骤
- 创建 AO对象 -> AO activation object 活跃对象, 函数上下文 =>环境
2. 寻找函数中的形参和变量声明
3. 将实参赋值给形参
4. 寻找函数体内函数声明 赋值函数体
5. 执行(赋值等操作)3. 全局上下文 =>环境
- 创建 GO对象 -> 全局上下文 ===window
2. 寻找变量声明
3. 寻找函数声明
4. 执行(赋值等操作)精简
ao:形参声明 赋值,变量、函数声明
go:变量、函数声明
ao:1形参声明 赋值 2变量声明 3函数声明
go:1变量声明 2函数声明
有先后顺序
预编译分析
function test(a) {
console.log(a); // function a() {}
var a = 1;
c=2
console.log(a); // 1
function a() {}
console.log(a); // 1
var b = function () {};
console.log(b); // function() {}
function d() {}
}
test(2);
分析1
执行前
1 先寻找并提取变量声明寻找形参和变量声明
GO={test:func}
看见test函数执行了 test(2) 所以有了AO,通过test(a)得到a变量,var b得到b变量,写了c=2也不算,只有通过var出来的才是
AO={
a:undefine
b:undefine
}
2 再把实参的参数值赋值给行参
通过test(2)得知
AO{
a:undefine->2
b:undefine
}
3.再寻找函数体声明,赋值函数体
找函数声明,只有function 变量(){}才是,var b = function () {};不是<br /> AO{<br /> a:undefine->2-> function a(){}<br /> b:undefine<br /> d:function d(){}<br /> }
执行后
再执行函数:
执行函数从上往下依次执行,执行经过预编译的代码,函数声明相当于提到最前面声明了,原来地方就没有了,相当于源代码没有写
c=2因为没有写var所以,不是声明,不参与预编译,执行函数时才执行,c=2是在函数内部写的,没有写var提升到全局GO里
GO={func:test,c:2}
AO={
a:undefine->2-> function a(){}->1
b:undefine->function()
d:function d(){}
}
简化1
Undo
GO={test:func}
AO={a:u->2->func,b:u,d:func}
do
GO={test:func,c:2}
AO={a:u->2->func->1,b:u->func,d:func}
简化2
GO={test:func,(do)c:2}
AO={a:u->2->func->(do)1,b:u->func,d:func}
GO={test:func,c:2}
AO={a:u->2->func->1,b:u->func,d:func}
代码分析
test(); //1
function test() {
console.log(1);
}
console.log(a); //u
var a;
console.log(a);//u
a = 2;
console.log(a);//2
结果1 undefined undefined 2
undo
GO={a:u,test:func}
do
GO={a:u->2,test:func}
GO={a:u->func a->(do)2}
console.log(a, b); // function a() {} undefined
function a() {}
var b = function () {};
GO={b:u->(d)func,a:func}
function test() {
var a = (b = 1);
console.log(a); // 1
}
test();
undo
GO={test:func}
AO={a:u}
do
GO={test:func,(do)b->1}
AO={a:u->b->1}
var a = b = 1; 可以拆分成var a=b;b=1;
a从undefine到b,当b取值为1时确定下来为1
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);
undo GO={b:u,a:func a(a){…}}
AO={a:u->func a(){},b:u}
do GO={b:u->3,a:func a(a){…}}
AO={a:u->1->func a(){}->2,b:u->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;
console.log(a)//1
GO={test:func,a:u}
AO={a:u}
do GO={test:func,a:u->1}
AO={a:u->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
undo
GO={test:func test(){…},a:u}
AO={b:u}
do
GO={test:func test(){…},a:u->1,c->3}
AO={b:u}
有if预编译也会直接看if里面的,if表达式预编译优先级很高,
预编译执行if里面的语句
作业
function test() {
// 遇到return直接返回
return a;
a = 1;
function a() {}
var a = 2;
}
console.log(test()); // function a() {}
AO={a:u->func a(){}}
return 直接终止函数运行,相当于return后面的没有写
function test() {
a = 1;
function a() {}
var a = 2;
return a;
}
console.log(test()); // 2
undo
GO={test:func}
AO={a:func a}
do
GO={test:func,a->1}
AO={a:func a->2}
题目
a = 1;
function test(e) {
function e() {}
console.log(e); // func
arguments[0] = 2;
console.log(e); // 2
// 此时变量a的值为undefined,不执行if语句内容
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
undo GO={test:func,
a:u}
AO={e:u->1->func e,
b:u,
c:u,
a:u}
do GO={test:func,
a:u->1,
f->5}
AO={e:u->1->func e->2,
b:u,
c:u,
a:u->4}
面试题
// Number(false) -> 0
var a = false + 1;
console.log(a); // 1
//----------------------------------
// Number(false) -> 0
var b = false == 1;
console.log(b); // false
//------------------------------------
/**
* typeof(a) -> 'undefined'
* (-true) -> Number(-true) -> -1
* (+undefined) -> Number(+undefined) -> NaN
* -1 + NaN + '' -> 'NaN'
* 'undefined' && 'NaN' -> 'NaN'
* Boolean('NaN') -> true
*/
if (typeof(a) && (-true) + (+undefined) + '') {
console.log('通过了'); // 执行
} else {
console.log('没通过');
}
//-----------------------------------------------------------------
/**
* 5 * '3' -> 5 * Number('3') -> 15
* 1 + 15 === 16 -> true
*/
if (1 + 5 * '3' === 16) {
console.log('通过了'); // 执行
} else {
console.log('未通过');
}
//----------------------------------------------------------------
/**
* Boolean(' ') -> true
* Boolean(') -> false
* Boolean(false) -> false
* true + false - false || '通过了'
* Number(true) + Number(false) - Number(false) || '通过了'
* 1 + 0 - 0 || '通过了'
* 1 || '通过了'
* 1
*/
console.log(!!' ' + !!'' - !!false || '通过了'); // 1