前端面试基础篇 二(附答案)

1.数组去重

答:

法一:indexOf循环去重

法二:ES6 Set去重;Array.from(new Set(array))

法三:Object 键值对去重;把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。

 

2.去除字符串首尾空格

答:

使用正则(^\s*)|(\s*$)即可

 

3.性能优化

答:

减少HTTP请求

使用内容发布网络(CDN)

添加本地缓存

压缩资源文件

将CSS样式表放在顶部,把javascript放在底部(浏览器的运行机制决定)

避免使用CSS表达式

减少DNS查询

使用外部javascript和CSS

避免重定向

图片lazyLoad

精灵图

 

4.闭包 有什么用

答:

(1)什么是闭包:

闭包是指有权访问另外一个函数作用域中的变量的函数。

闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配。当在一个函数内定义另外一个函数就会产生闭包。

(2)为什么要用:

匿名自执行函数:我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。除了每次使用变量都是用var关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,可以用闭包。

结果缓存:我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

封装:实现类和继承等。

 

5.JS实现跨域

答:

JSONP:通过动态创建script,再请求一个带参网址实现跨域通信。document.domain + iframe跨域:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

location.hash + iframe跨域:a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

window.name + iframe跨域:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。

postMessage跨域:可以跨域操作的window属性之一。

CORS:服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求,前后端都需要设置。

代理跨域:启一个代理服务器,实现数据的转发

参考https://segmentfault.com/a/1190000011145364

 

6.Js基本数据类型

答:

undefined、null、number、boolean、string、symbol

 

7.暂停死区

答:

使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

 

8.ant-design优点和缺点

答:

优点:组件非常全面,样式效果也都比较不错。

缺点:框架自定义程度低,默认UI风格修改困难。

 

9.vue的生命周期

答:

Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、销毁等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。

每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁。

实例、组件通过new Vue() 创建出来之后会初始化事件和生命周期,然后就会执行beforeCreate钩子函数,这个时候,数据还没有挂载呢,只是一个空壳,无法访问到数据和真实的dom,一般不做操作

挂载数据,绑定事件等等,然后执行created函数,这个时候已经可以使用到数据,也可以更改数据,在这里更改数据不会触发updated函数,在这里可以在渲染前倒数第二次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取

接下来开始找实例或者组件对应的模板,编译模板为虚拟dom放入到render函数中准备渲染,然后执行beforeMount钩子函数,在这个函数中虚拟dom已经创建完成,马上就要渲染,在这里也可以更改数据,不会触发updated,在这里可以在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取

接下来开始render,渲染出真实dom,然后执行mounted钩子函数,此时,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了,可以在这里操作真实dom等事情...

当组件或实例的数据更改之后,会立即执行beforeUpdate,然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染,一般不做什么事儿

当更新完成后,执行updated,数据已经更改完成,dom也重新render完成,可以操作更新后的虚拟dom

当经过某种途径调用$destroy方法后,立即执行beforeDestroy,一般在这里做一些善后工作,例如清除计时器、清除非指令绑定的事件等等

组件的数据绑定、监听...去掉后只剩下dom空壳,这个时候,执行destroyed,在这里做善后工作也可以

参考:https://segmentfault.com/a/1190000008010666

 

10.简单介绍一下symbol

答:

Symbol是ES6 的新增属性,代表用给定名称作为唯一标识,这种类型的值可以这样创建,let id=symbol(“id”)

Symbl确保唯一,即使采用相同的名称,也会产生不同的值,我们创建一个字段,仅为知道对应symbol的人能访问,使用symbol很有用,symbol并不是100%隐藏,有内置方法Object.getOwnPropertySymbols(obj)可以获得所有的symbol。 
也有一个方法Reflect.ownKeys(obj)返回对象所有的键,包括symbol。

所以并不是真正隐藏。但大多数库内置方法和语法结构遵循通用约定他们是隐藏的,

 

11.介绍一下promise,及其底层如何实现

答:

Promise是一个对象,保存着未来将要结束的事件,她有两个特征:

1、对象的状态不受外部影响,Promise对象代表一个异步操作,有三种状态,pending进行中,fulfilled已成功,rejected已失败,只有异步操作的结果,才可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也就是promise名字的由来

2、一旦状态改变,就不会再变,promise对象状态改变只有两种可能,从pending改到fulfilled或者从pending改到rejected,只要这两种情况发生,状态就凝固了,不会再改变,这个时候就称为定型resolved,

 

12.ES6箭头函数的特性

答:

箭头函数与普通函数的区别在于:

1、箭头函数没有this,所以需要通过查找作用域链来确定this的值,这就意味着如果箭头函数被非箭头函数包含,this绑定的就是最近一层非箭头函数的this,

2、箭头函数没有自己的arguments对象,但是可以访问外围函数的arguments对象

3、不能通过new关键字调用,同样也没有new.target值和原型

 

13.给出以下代码,输出的结果是什么?原因?

答:

for(var i=0;i<5;i++) { 
    setTimeout(function(){ 
        console.log(i);     //后输出5次
    },1000); 
}
console.log(i)     //先输出

//5   
//(5)5   5次

每次for循环的时候setTimeout都会执行,但是里面的function则不会执行被放入任务队列,因此放了5次;

for循环的5次执行完之后不到1000毫秒;   先输出一个5

1000毫秒后全部执行任务队列中的函数,再输出5个5

 

14.js对象类型,基本对象类型以及引用对象类型的区别

答:

基本数据类型:按值访问,可操作保存在变量中的实际的值。基本类型值指的是简单的数据段。基本数据类型有这六种:undefined、null、string、number、boolean、symbol。

引用类型:当复制保存着对象的某个变量时,操作的是对象的引用,但在为对象添加属性时,操作的是实际的对象。引用类型值指那些可能为多个值构成的对象。

引用类型有这几种:Object、Array、RegExp、Date、Function、特殊的基本包装类型(String、Number、Boolean)以及单体内置对象(Global、Math)。

 

15.怎么实现一个计算一年中有多少周?

答:

首先你得知道是不是闰年,也就是一年是365还是366.

其次你得知道当年1月1号是周几。假如是周五,一年365天把1号 2号3号减去,也就是把第一个不到一周的天数减去等于362
还得知道最后一天是周几,加入是周五,需要把周一到周五减去,也就是362-5=357.正常情况 357这个数计算出来是7的倍数。357/7=51 。即为周数。

 

16.面向对象的继承方式

答:

原型链继承

核心: 将父类的实例作为子类的原型

特点:非常纯粹的继承关系,实例是子类的实例,也是父类的实例

父类新增原型方法/原型属性,子类都能访问到

简单,易于实现

缺点:要想为子类新增属性和方法,不能放到构造器中

无法实现多继承

来自原型对象的所有属性被所有实例共享

创建子类实例时,无法向父类构造函数传参

 

构造继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

特点:解决了子类实例共享父类引用属性的问题

创建子类实例时,可以向父类传递参数

可以实现多继承(call多个父类对象)

缺点:实例并不是父类的实例,只是子类的实例

只能继承父类的实例属性和方法,不能继承原型属性/方法

无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

 

实例继承

核心:为父类实例添加新特性,作为子类实例返回

特点:不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果

缺点:实例是父类的实例,不是子类的实例

不支持多继承

 

拷贝继承

特点:

支持多继承

缺点:

效率较低,内存占用高(因为要拷贝父类的属性)

 

组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

特点:

可以继承实例属性/方法,也可以继承原型属性/方法

既是子类的实例,也是父类的实例

不存在引用属性共享问题

可传参

函数可复用

 

寄生组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

参考https://www.cnblogs.com/humin/p/4556820.html

 

17.class

答:

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

 

 

前端面试基础篇 一:https://blog.csdn.net/caoyan0829/article/details/89888629

前端面试核心篇 :https://blog.csdn.net/caoyan0829/article/details/90022103

 

此文章参考牛客面经汇总

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章