面试题集锦
说明:刚经历过面试不久,以前老是不总结的我,这回总结一下我经常遇到的面试题,留个回忆~
这里只是做个简单的介绍,给自己提个醒,根据面试题延伸出自己需要补哪一块的知识。
下面,开始我的表演
js有哪些内置对象?
本地对象和单体内置对象统称为“内置对象”
- 本地对象(内置函数):
Boolean / Number / String / Object / Array / Date / RegExp / Function / Error - 单体内置对象:Global/Math/Json(ECMAScript5新增)
js中使用typeof能得到哪些类型?
undefined/number/string/boolean/object/function
- 值类型能详细的区分出来
- 引用类型区分不出来这么详细,只能区分出function
typeof undefined // undefined
typeof 'abc' // string
typeof 123 // number
typeof true // Boolean
typeof {} // object
typeof [] // object
typeof null // object
typeof console.log // function
何时使用=== 何时使用==?
if (obj.a == null) {
// 这里相当于obj.a === null || obj.a === undefined的简写形式
// jquery源码推荐写法
}
除了这个,其他一律使用全等.
JS变量按照存储方式区分为哪些类型,并描述其特点?
-
值类型
把数值分块存储在内存中,存储在栈内存中 -
引用类型
存储在堆内存,变量只存了变量名和指针(用于找到堆内存的位置)
如何理解JSON?
JSON只不过是一个JS对象,也是一种数据格式
JSON.stringify({a: 10, b: 20})
JSON.parse("{"a": 10, "b": 20}")
数据格式:简单值,对象,数组
JSON跟对象字面量的写法很像,JSON属性必须加双引号。
如何准确判断一个变量是数组类型?
简单判断:
var arr = [];
arr instanceof Array // true
typeof arr // object。 typeof 是无法判断是否是数组
更准确的判断:
function isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]';
}
isArray([1, 2, 3]);
描述new一个对象的过程?
- 创建一个新对象
- this指向这个新对象
- 执行代码,即对this赋值
- 返回this
实战中原型使用实例?
function Elem(id) {
this.elem = document.getElementById(id)
}
Elem.prototype.html = function(val) {
var elem = this.elem;
if (val) {
elem.innerHtml = val;
return this; // 链式调用
} else {
return elem.innerHtml;
}
}
Elem.prototype.on = function(type, fn){
var elem = this.elem;
elem.addEventListener(type, fn);
return this;
}
var div1 = new Elem('div1');
div1.html('<p>显示出来了</p>').on('click', function() {
console.log('ok');
}).html('<p>kaopu</p>')
说一下对变量提升的理解?
变量定义和函数声明(函数表达式)都会被提到前面,即变量提升。
什么是单线程,和异步有什么关系?
单线程:只有一个线程,同一时间只能做一件事,两段JS不能同时执行
原因:避免DOM渲染的冲突
解决方案:异步算是一种“无奈”的解决方案,虽然有很多问题
异步是单线程的解决方案
避免DOM渲染的冲突:
- 浏览器需要渲染DOM
- JS可以修改DOM结构
- JS执行的时候,浏览器DOM渲染会暂停
- 两段JS也不能同时执行(都修改DOM就冲突了)
- webworker支持多线程,但是不能访问DOM
异步存在的两个问题:
- 没有按照书写方式执行,可读性差
- callback中不容易模块化
实现方式:event loop
- 事件轮询,JS实现异步的具体解决方案
- 同步代码,直接执行
- 异步函数先放在异步队列中
- 待同步函数执行完毕,轮询执行 异步队列 的函数
- 注意:立即放入,指定时间内放入,请求完成后放入
使用异步的场景
- 定时任务:setTimeout,setInterval
- 网络请求:ajax请求,动态加载
- 事件绑定
总结当前异步的解决方案
- jQuery Deferred
- Promise
- Async/Await
- Generator
同步和异步的区别
同步会阻塞代码执行,而异步不会
alert是同步,setTimeout是异步
DOM节点的Attribute和property有什么区别?
property只是一个js对象的属性修改,它们会在初始化的时候再DOM对象上创建。
Attribute是对html标签属性的修改,attributes是属于property的一个子集。
- property能够从attribute中得到同步;
- attribute不会同步property上的值;
- attribute和property之间的数据绑定是单向的,attribute->property;
- 更改property和attribute上的任意值,都会将更新反映到HTML页面中;
cookie, sessionStorage, localStorage区别?
cookie: 本身用于客户端和服务器端通信,但是它有本地存储的功能,于是就被“借用”,使用document.cookie = … 获取和修改即可。
缺点:存储量太小,只有4kb,所有http请求都带着,会影响获取资源的效率,API简单,需要封装才能用。
sessionStorage, localStorage:HTML5专门为存储而设计,最大容量5M,API简单易用: localStorage.setItem(key, value) localStorage.getItem(key)
localStorage在IOS safari隐藏模式下,localStorage.getItem会报错
sessionStorage关闭浏览器后数据也会跟着清掉,而localStorage不会。
区别:容量,是否会携带到ajax中,API易用性
AMD和CommonJS的使用场景
- 需要异步加载JS,使用AMD,CommonJS是同步
- 使用npm之后建议使用CommonJS
上线流程要点
- 将测试完成的代码提交到git版本库的master分支
- 将当前服务器的代码全部打包并记录版本号,备份
- 将master分支的代码提交覆盖到线上服务器,生成新版本
回滚流程要点
- 将当前服务器的代码打包并记录版本号,备份
- 将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号
加载一个资源的过程
- 浏览器根据DNS服务器得到域名的IP地址
- 向这个IP的机器发送http/https请求
- 服务器收到、处理并返回http/https请求
- 浏览器得到返回内容
性能优化
- 资源合并,减少http请求(通过构建工具合并, webpack或者gulp);
- 静态资源缓存(通过连接名控制);
- 使用CDN, 减轻服务器端请求压力;
- 懒加载;
- 减少DOM操作(缓存DOM查询,合并DOM插入);
- 事件节流.
class和js构造函数的区别
- Class语法上更加贴合面向对象的写法,java发展方向
- Class实现继承更加易读、易理解
- 更易于写java等后端语言的使用
- 本质还是语法糖,使用prototype
es6常用的功能
- Class
- promise
- import export 模块化
编译:webpack rollup - let/const(常量)
- 多行字符串/模板变量
${}
- 解构赋值 const {a, b} =
- 块级作用域
- 函数默认参数
- 箭头函数
如何理解MVVM?
首先从名字来看,MVVM即Model, View ViewModel,Model对应的是data数据部分,View对应的是html视图部分,ViewModel对应的是new Vue实例部分,View通过事件绑定的方式影响到Model,Model通过数据绑定影响到View。
对ViewModel的理解:连接器,用于连接View和Model
MVVM不是一个从零开始的创新,是由之前的MVC发展过来的,因为MVC是应用到后端的,并不是前端的,为了适用前端的场景,把C换成了VM
如何实现MVVM
MVVM实现的三要素也是vue的三要素:
- 响应式:vue如何监听到data的每个属性变化?
- 模板引擎:vue的模板如何被解析,指令如何处理?
- 渲染:vue模板如何被渲染成html? 以及渲染过程?
设计模式的六大原则(SOLID)
- 单一职责原则
- 一个程序只做好一件事
- 如果功能过于复杂就拆分开,每个部分保持独立
- 开放封闭原则
- 对扩展开放,对修改封闭
- 增加需求时,扩展新代码,而非修改已有代码
- 这是软件设计的终极目标
- 李氏置换原则
- 子类能覆盖父类
- 父类能出现的地方,子类就能出现
- JS中使用较少(弱类型&继承使用较少)
- 接口独立原则
- 保持接口的单一独立,避免出现“胖接口”
- JS中没有接口(typescript例外),使用较少
- 类似于单一职责原则,这里更关注接口
- 依赖倒置原则
- 面向接口变成,依赖于抽象而不依赖于具体
- 使用方只关注接口而不关注具体类的实现
- JS中使用较少(没有接口&弱类型)
- 迪米特法则
去重 写代码消除 数组 【6 8 9 9 12 13 14 1 3】中重复的元素。
方法一:
let arr = [6, 8, 9, 9, 12, 13, 14, 1, 3];
function removeArr(arr) {
let arrayResult = [];
for (let i = 0, len = arr.length; i < len; i++) {
if (arrayResult.indexOf(arr[i]) === -1) {
arrayResult.push(arr[i]);
}
}
return arrayResult;
}
removeArr(arr);
方法二:
var arr = [6, 8, 9, 9, 12, 13, 14, 1, 3];
function removeArr(arr) {
var i = 0,
len = arr.length,
j;
for (i; i < len; i++) {
for (j= i+1; j < len; j++) {
if(arr[i] == arr[j]) {
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
removeArr(arr);
方法三:
let arr = [6, 8, 9, 9, 12, 13, 14, 1, 3];
function removeArr(arr) {
return Array.from(new Set(arr))
}
removeArr(arr);
方法四:
let arr = [6, 8, 9, 9, 12, 13, 14, 1, 3];
function removeArr(arr) {
let arrayResult = [];
arr.reduce((s1, s2) => {
if(s1 !== s2) {
arrayResult.push(s1);
}
return s2
});
arrayResult.push(arr[arr.length-1])
return arrayResult;
}
removeArr(arr);
ajax和axios的区别, fetch
- jQuery整个项目太大,单纯使用ajax却要引入整个jQuery非常不合理
- axios是Promise的实现版本,符合最新ES规范,支持Promise API
- axios客户端支持防止XSRF
- axios提供了一些并发的请求接口
fetch:
号称是AJAX的替代品.。
优势:
- 更加底层,提供的API丰富(request, response)
- 脱离了XHR,是ES规范里新的实现方式
缺点:
- fetch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
- fetch默认不会带cookie,需要添加配置项
- fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
- fetch没有办法原生监测请求的进度,而XHR可以
Vue生命周期
-
beforeCreate和created在vue实例化的时候就执行了,此处可请求数据,但是不能进行dom操作,因为拿不到组件对应的dom节点,如下图。
给实例赋值最早要在created里面操作。 -
beforeMount和mounted这里是把组件生成的html内容挂在到dom上面的一个过程,如果不指定el或者是$mount()的话,数据是不会挂在到页面上的,因为它不知道该挂载到哪里去。所以dom应该在mounted这里才去操作。
beforeMount()是渲染前的,mounted()是通过render函数渲染后拿到的,mounted之后实例就创建完成了.
说明:上述4个钩子都是只会被调用一次。beforeMount和mounted在服务端渲染的时候不会被调用。服务端渲染的时候会调用的只有beforeCreate和created。
在.vue文件里开发的过程中,是都没有template的,在.vue里面写的template都经过vue-loader处理直接变成了render函数放在vue-loader解析过的文件里面。 -
beforeUpdate和updated是有数据更新的时候才会去执行的。
-
beforeDestroy和destroyed组件被销毁的时候才会去执行。
$destroy() // 组件销毁的一个方法,解除所有的事件监听和所有的watch。 -
activated和deactivated和keep-alive有关系,该钩子在服务器端渲染期间不被调用。activated是在keep-alive 组件激活时调用。
deactivated在keep-alive 组件停用时调用。 -
render(h)和renderError(),有render的时候就不需要template了,因为template处理后最终也是变成render函数的返回vnode的,renderError是在render报错之后才会触发,报错之后渲染出来的就是报错的信息,只有在本组件的render函数出现错误的时候才会被调用,只关心自己有没有渲染成功,不管子组件。
-
errorCaptured() 正式环境可以使用,收集线上环境的一些错误。所有子组件的错误都可以捕捉到,除非子组件把冒泡事件停止掉。