面试题解读
1.1 面试题--i++与++i解读
- 考点:
- i++在进行运算时,是先进行运算,再加1;++i在运算时,先加1,再进行运算;
- return的预解释问题:1)return后面的函数定义代码,不进行预解释;2)return下面的代码会进行预解释,但是不会运行;
- 在复杂的函数嵌套中,考虑私有作用域的释放问题,判断作用域在执行完成后是立即销毁,或是不立即销毁,还是不销毁;
- 如果当前作用域中找不到变量,去它对应的堆地址的开辟空间寻找(即上级作用域),依次寻找,找不到,会报错;
- 如何将作用域释放,给其赋值null;
- 实战题i++
<script>
//全局作用域:
//1.预解释:1)将var fn=xxff00;地址中的内容是字符串;2)var f,只声明;
//2.执行JS代码:1)f=fn()=xxff00()即变量f的赋值是,xxff00地址中函数调用的结果,赋值结果就是xxff11;则f=xxff11;
function fn() {
var i=8;
return function (n) {
console.log(n+(i++));
}
}//预解释:将var fn=xxff00;地址中的内容是字符串;
var f=fn();//此时fn()的结果赋给变量f,然后私有作用域被占用,所以fn()形成的私有作用域不释放;如果想让其释放则给其赋值null;即:f=null;
f(15);//执行xxff11(15)所得的结果,为23;注意的是执行前上级作用域中的i已经变成8;执行后变为9;此时私有作用域属于不销毁状态;
f(20);//执行xxff11(20)所得的结果,为29;注意的是执行前上级作用域中的i已经变成9;执行后变为10;
fn()(15);//自执行函数分两步,第一步先执行fn();第二步再执行fn()(15);最后两个私有作用域一起释放(销毁);此时私有作用域属于不立即销毁状态;
fn()(20);//自执行函数,会重新建立新的私有作用域,然后同时释放;
fn()(30);
f(30);//执行xxff11(30)所得的结果,注意的是执行前上级作用域中的i已经变成10;执行后变为11;
//打印结果依次为:23,29,23,28,38,40
</script>
i++题分析图
- 实战题++i
<script>
function fn() {
var i=10;
return function (n) {
console.log(n+(++i));
}
}
var f=fn();
f(15);//结果为26,此时i为11;
f(20);//结果为32,此时i为12;
fn()(15);//结果为26,两个作用域同时释放;
fn()(20);//结果31,两个作用域同时释放;
fn()(30);//结果为41
f(30);//结果为43;
</script>
++i题分析图
1.2 面试题-this指向解读
- 考点:
- 自执行函数的this指向window,带"."的属性里面的this为"."前面的元素或对象;
- 普通对象,在堆内存的地址中是以键值对的形式存放的;
- 私有作用域的释放及上级作用域的查找;
- 全局属性和私有属性概念;
<script>
var num=10;
var obj={
num: 20,
fn:(function (num) {
this.num*=2;
num+=10;
return function () {
this.num*=3;
num+=1;
console.log(num);
}//return后面的函数定义不进行预解释,返回的是地址,地址中存放的是JS代码字符串;
})(num)//此时的num为全局属性中的10;自己对象中的num值,如若获取,需要obj.num才能拿到
};
var fn=obj.fn;
fn();//执行结果为:21;
obj.fn();//执行结果为:22
console.log(window.num,obj.num);//执行结果为:60,60;
</script>
this指向题分析图
- 对象定义阶段解读
- 考点:对象定义阶段时,会开辟一个空间地址,内容是以键值对形式存在里面,需注意的一点是,当键值对中属性值为自执行函数时,在对象的定义阶段就开始自执行;把执行结果作为属性值赋给对应的属性名;当调用属性值时,就直接赋值,不再执行自执行函数里面的过程;
<script> var num=10; var obj={ num: 20, fn:(function (num) { this.num*=2; num+=10; return function () { this.num*=3; num+=1; console.log(num); }//return后面的函数定义不进行预解释,返回的是地址,地址中存放的是JS代码字符串; })(num) }; console.log(window.num,obj.num);//结果为:20,20; </script>
- 代码解读:在以上代码中,obj全局变量先在预解释中只声明,在执行代码时,进行赋值定义,定义时,会新建一个堆地址,里面放的是键值对,键值对中fn属性名的属性值为自执行函数,当在定义阶段,自执行函数就已经执行完毕,把结果赋给属性名;
1.3 面试题-乘法与++i
- 考点:上级作用域的查找,全局作用域中i的赋值问题
<script>
var i=3;
function fn() {
i*=2;
return function (n) {
console.log(n*(++i));
}
}
var f=fn();
f(3);//结果为21;
fn()(3);//结果为45;
f(4);//结果为64;
fn()(3);//结果为99;
</script>
分析图1
1.4 面试题-this与||的运用
- 考点:
- 1)当函数存在形参但是未赋实参,则形参获得值为undefined;
- 2)自执行函数的this指向window;函数执行后,没有返回值,返回的是undefined;
- 3)逻辑运算符"或",可以用作判断,即代码a||b中,只有a为假时才会执行后面的代码,如:var m=a || b;其中a为假,则执行b,此时m赋值为b;
<script>
//当函数存在形参但是未赋实参,则形参获得值为undefined;
var name="圆梦源";
var age=500;
name=(function (name,age) {
//形参赋值:var name="圆梦源"; var age=undefined;
arguments[0]="圆梦源培训";//即name重新赋值为"圆梦源培训";
age=age || this.age;
//重点:当||参与赋值时,判断前面的值是否为false,当为false时,执行||后面的代码
//此时的this指向为window,则this.age为500;所以age赋值为500;
console.log(name,age);//结果为"圆梦源培训",500;
})(name);
//自执行函数的结果赋给全局作用域中的name,此时自执行函数中没有返回值,所以name赋值为undefined;
console.log(name,age);//结果为undefined,500;
</script>
this与或分析图
1.5 实战题
- 需求:点击div按钮使里面的数字形成自增效果;
- 思路1:设置全局变量属性;不建议使用,因为会出现全局属性;
var oDiv=document.getElementsByTagName("div")[0];
var num=0;//全局属性
oDiv.onclick=function () {
this.innerHTML=++num;
}
- 思路2:闭包,将全局变量,包裹到私有作用域中,此时就不会存在全局属性;但是会形成不销毁的私有作用域,也不建议;
- 第一种自执行函数:
var oDiv=document.getElementsByTagName("div")[0]; (function () { var num=0; oDiv.onclick=function () { this.innerHTML=++num; } })();
- 第二种自执行函数:
var oDiv=document.getElementsByTagName("div")[0]; oDiv.onclick=(function () { //此处的this指向window; var num=0; return function () { //此处的this指向的是oDiv元素; this.innerHTML=++num; } })(); //先执行自执行函数,跟点击事件是否触发无关;实际上点击事件赋值为一个地址,地址中的内容为return后面的函数定义字符串;
this指向分析图
- 第一种自执行函数:
- 思路3:DOM操作:不建议频繁的进行DOM操作;
var oDiv=document.getElementsByTagName("div")[0];
oDiv.onclick=function () {
this.innerHTML=++this.innerHTML;
}
- 思路4:建议:自定义属性;
var oDiv=document.getElementsByTagName("div")[0];
oDiv.num=0;//给div元素添加自定义属性;
oDiv.onclick=function () {
his.innerHTML=++this.num;
}