JavaScript:面试频繁出现的几个易错点

1.前言

这段时间,金三银四,不少人面试,不少人分享面试题。在前段时间,我也临时担任面试官,为了大概了解面试者的水平,我也写了一份题目,面试了几个前端开发者。在这段时间里面,我在学,在写设计模式的一些知识,想不到的设计模式的这些知识,就是面试题里面,频繁让人掉坑的考点。因此,今天就总结一下,那些让人掉坑的考点。javascript

2.面向对象编程

关于面向对象和面向过程,我的以为这二者不是绝对独立的,而是相互相成的关系。至于何时用面向对象,何时用面向过程,具体状况,具体分析。html

针对于面向对象编程的。知乎上有一个高赞回答:前端

面向对象: 狗.吃(屎)
面向过程: 吃.(狗,屎)java

可是这个例子以为不太优雅,我改一下了,举一个优雅些的小例子说明一下面向对象和面向过程的区别。面试

需求:定义‘守候吃火锅编程

面向对象的思想是:守候.动做(吃火锅)设计模式

面向过程的思想是:动做(守候,吃火锅)数组

代码实现方面:微信

//面向对象
//定义人(姓名)
let People=function(name){
    this.name=name;
}
//动做
People.prototype={
    eat:function(someThing){
        console.log(`${this.name}${someThing}`);
    }
}
//守候是我的,因此要建立一我的(new一次People)
let shouhou=new People('守候','男',24);
shouhou.eat('火锅');

//面向过程
let eat=function(who,someThing){
    console.log(`${who}${someThing}`);
}
eat('守候','火锅');复制代码

结果都同样,都是输出‘守候吃火锅’。可是万一我如今吃饱了,准备写代码了。这下怎么实现呢?看代码闭包

//面向对象
shouhou.coding=function(){
    console.log(this.name+'写代码');
}
shouhou.coding();
//面向过程
let coding=function(who){
    console.log(who+'写代码');
}
coding('守候');复制代码

结果也同样:‘守候写代码’

可是不难发现面向对象更加的灵活,复用性和扩展性更加。由于面向对象就是针对对象(例子中的:‘守候’)来进行执行某些动做。这些动做能够自定义扩展。
而面向过程是定义不少的动做,来指定谁来执行这个动做。

好了,面向对象的简单说明就到这里了,至于面向对象的三大特性:继承,封装,多态这个自行上网查找资料。

3.this

使用 JavaScript 开发的时候,不少开发者多多少少会被 this 的指向搞蒙圈,可是实际上,关于 this 的指向,记住最核心的一句话:哪一个对象调用函数,函数里面的this指向哪一个对象。

下面分几种状况谈论下

3-1.普通函数调用

这个状况没特殊意外,就是指向全局对象-window。

let username='守候'
function fn(){
    alert(this.username);//undefined
}
fn();复制代码

可能你们会困惑,为何不是输出守候,可是在细看一看,我声明的方式是let,不会是window对象
若是输出守候,要这样写

var username='守候'
function fn(){
    alert(this.username);//守候
}
fu();
//---------------
window.username='守候'
function fn(){
    alert(this.username);//守候
}
fn();
//能够理解为
//window.fn();复制代码

3-2.对象函数调用

这个相信不难理解,就是那个函数调用,this指向哪里

window.b=2222
let obj={
    a:111,
    fn:function(){
        alert(this.a);//111
        alert(this.b);//undefined
    }
}
obj.fn();复制代码

很明显,第一次就是输出obj.a,就是111。而第二次,obj没有b这个属性,因此输出undefined,由于this指向obj

可是下面这个状况得注意

let obj1={
    a:222
};
let obj2={
    a:111,
    fn:function(){ alert(this.a);
    }
}
obj1.fn=obj2.fn;
obj1.fn();//222复制代码

这个相信也不难理解,虽然obj1.fn是从obj2.fn赋值而来,可是调用函数的是obj1,因此this指向obj1

3-3.构造函数调用

let TestClass=function(){
    this.name='111';
}
let subClass=new TestClass();
subClass.name='守候';
console.log(subClass.name);//守候
let subClass1=new TestClass();
console.log(subClass1.name)//111复制代码

这个也是不难理解,回忆下(new的四个步骤)就差很少了!

可是有一个坑,虽然通常不会出现,可是有必要提一下。

在构造函数里面返回一个对象,会直接返回这个对象,而不是执行构造函数后建立的对象

3-4.apply和call调用

apply和call简单来讲就是会改变传入函数的this。

let obj1={
    a:222
};
let obj2={
    a:111,
    fn:function(){ alert(this.a);
    }
}
obj2.fn.call(obj1);复制代码

此时虽然是 obj2 调用方法,可是使用 了call,动态的把 this 指向到 obj1。至关于这个 obj2.fn 这个执行环境是 obj1applycall 详细内容在下面说起。

3-5.箭头函数调用

首先不得不说,ES6 提供了箭头函数,增长了咱们的开发效率,可是在箭头函数里面,没有 this ,箭头函数里面的 this 是继承外面的环境。

一个例子

let obj={
    a:222,
    fn:function(){    
        setTimeout(function(){console.log(this.a)})
    }
};
obj.fn();//undefined复制代码

不难发现,虽然 fn() 里面的 this 是指向 obj ,可是,传给 setTimeout 的是普通函数, this 指向是 windowwindow 下面没有 a ,因此这里输出 undefined

换成箭头函数

let obj={
    a:222,
    fn:function(){    
        setTimeout(()=>{console.log(this.a)});
    }
};
obj.fn();//222复制代码

此次输出 222 是由于,传给 setTimeout 的是箭头函数,而后箭头函数里面没有 this ,因此要向上层做用域查找,在这个例子上, setTimeout 的上层做用域是 fn。而 fn 里面的 this 指向 obj ,因此 setTimeout 里面的箭头函数的 this ,指向 obj 。因此输出 222

4.call和apply

callapply 的做用,彻底同样,惟一的区别就是在参数上面。
call 接收的参数不固定,第一个参数是函数体内 this 的指向,第二个参数如下是依次传入的参数。
apply接收两个参数,第一个参数也是函数体内 this 的指向。第二个参数是一个集合对象(数组或者类数组)

let fn=function(a,b,c){
console.log(a,b,c);
}
let arr=[1,2,3];复制代码

如上面这个例子

let obj1={
    a:222
};
let obj2={
    a:111,
    fn:function(){ alert(this.a);
    }
}
obj2.fn.call(obj1);复制代码

callapply 两个主要用途就是

1.改变 this 的指向(把 thisobj2 指向到 obj1

2.方法借用( obj1 没有 fn ,只是借用 obj2 方法)

5.闭包

闭包这个可能你们是迷糊,可是必需要征服的概念!下面用一个例子简单说下

let add=(function(){
let now=0;
return {
 doAdd:function(){
    now++;
    console.log(now);
}
}
})()复制代码

而后执行几回!

上图结果看到,now 这个变量,并无随着函数的执行完毕而被回收,而是继续保存在内存里面。
具体缘由说下:刚开始进来,由于是自动执行函数,一开始进来会自动执行,这一块

而后把这个对象赋值给 add 。因为 add 里面有函数是依赖于 now 这个变量。因此 now不会被销毁,回收。这就是闭包的用途之一(延续变量周期)。因为 now 在外面访问不到,这就是闭包的另外一个用途(建立局部变量,保护局部变量不会被访问和修改)。

可能有人会有疑问,闭包会形成内存泄漏。可是你们想下,上面的例子,若是不用闭包,就要用全局变量。把变量放在闭包里面和放在全局变量里面,影响是一致的。使用闭包又能够减小全局变量,因此上面的例子闭包更好!

6.小结

在学设计模式的时候,遇到的知识点就是这一些了,这些知识点,也是我在群聊,社区里面,让人掉坑比较多的考点。这些知识,能够说是开发经常使用,面试常考的知识,仍是建议你们深刻些学习。上面那里也是简单的过一下而已。不算深刻。若是你们对文章有什么建议,欢迎指点。

-------------------------华丽的分割线--------------------
想了解更多,关注关注个人微信公众号:守候书阁

clipboard.png

相关文章
相关标签/搜索