也不知道是何时我曾经立下一天一遍博文的豪言壮志,现在打开一看,有关技术的文章的建立时间都定格在2017年,豪言壮志当然不可靠,脚踏实地才是硬道理。这几个月个人变化很大,曾经懒惰睡懒觉的我天天都坚持6点起床,如今我还在尝试学着胖哥(技术胖)10点半睡觉4点半起床,结果第一天就吃到了失败的苦果。以上内容交代了文章主人公从一个懒惰it行业从事者慢慢向业界前辈学习的经历。接下来我会记录下关于前端面试的一些问题和解答,不能保证彻底是正确的,可是对我自身也算是种技术沉淀了。javascript
主要内容分为 JS原生部分,vue框架使用部分和优化的部分。由于我最近在面试,因此可能会抽面试题来先写,其实我也没有太多思路,秉着没人看的心态拼命乱写,我仍是但愿有我的在下面大声说出博主你写错啦等等吐槽,我是菜鸡我为本身代盐。html
因为是我的总结因此并不会太详细,也不会举例去说明,只能按个人表达出来前端
前端的根基所在,原生JS是限制不少前端向更高方向发展的一个瓶之一,废话很少说先从简单的开始吧
复制代码
这题看起很简单,我会不假思索的答出const通常用来定义常量或者对象(对象类型在js中是保存一个指针地址因此不会有改变),而let咱们就用来定义变量,const是须要初始化的,在定义的时候就须要初始化赋值,而let则不用一开始就初始化,二者都不存在变量提高(暂时性死区),而且会造成一个块级做用域,并且不能重复定义相同的变量名。如今咱们稍微整理一下他们的异同点,同时给他俩来个参照物var,得出如下结论。vue
相同点:html5
不一样点:java
当程序的控制流程在新的做用域(module function 或 block 做用域)进行实例化时,在此做用域中用let/const声明的变量会先在做用域中被建立出来,但所以时还未进行词法绑定,因此是不能被访问的,若是访问就会抛出错误。所以,在这运行流程进入做用域建立变量,到变量能够被访问之间的这一段时间,就称之为暂时死区。git
翻译一下人话就是,在函数做用域,或者使用let/const时造成的块级做用域中,在执行到let/const之定义以前,定义的变量都是不可访问的,在语法上这个暂时性死区(TDZ)。es6
面试超级喜欢问的问题,简单来讲,非要追求逼格的话google一下能够找找到不少,这里我只介绍最实用的方法,在处理性能方面方法3>方法1>方法2github
function unique(array) {
let temp = []
for (let i = 0, l = array.length; i < l; i++) {
if (temp.indexOf(arr[i]) === -1) temp.push(arr[i])
}
return temp
}
复制代码
最普通的for循环,时间复杂度O(n^2) 空间复杂度O(n),固然咱们能够价格hash表让查找更加快速面试
function unique(array) {
let temp = []
let hash = {}
for (let i = 0, l = array.length; i < l; i++) {
if (!hash[array[i]]) {
hash[array[i]] = true //存入hash表
temp.push(arr[i])
}
}
return temp
}
复制代码
这样咱们就能够把时间复杂度降到了O(n),但因为新建了一个对象因此消耗内存方面要远大于indexOf
function unique(array) {
return array.filter((item, index, array) => {
return array.indexOf(item) === index
})
}
复制代码
这个性能应该是最差的了,从空间复杂度和时间复杂度上推论的
//Set 版本
function unique(array) {
return [...new Set(array))]
}
复制代码
经测试这个最好
function unique(array: Array<number>): Array<number> {
let temp: Array<number> = []
for (let i: number = 0, l: number = array.length; i < l; i++) {
if (temp.indexOf(array[i]) === -1) temp.push(array[i])
}
return temp
}
复制代码
ts用的很是少,随手写的,不够健壮性,只能去掉重复的数字类型数组,二维数据,和其它类型的数据都没有考虑到,由于比较随意啊,因此我也不考虑那么多了,总结下就行了
首先,事件冒泡和事件捕获的提出都是为了解决事件执行顺序,咱们在使用中能够经过addEventListener()的第三个参数来决定咱们注册的事件是捕获时触发仍是冒泡时触发,false的时候是冒泡触发,true则为捕获。下面我就简单举个例子,请看如下代码
<div id="el_1">
<p class="el_2">有本事点我</p>
<div>
//假设咱们给p添加一个点击事件
复制代码
事件冒泡:是微软的方案,冒泡就像一块石头扔进水里,气泡会从底下往上冒,因此以上的事件执行顺序是p>div>body>html>document
事件捕获:网景公司提出,事件捕获和事件冒泡相反,这时候p元素的点击事件执行顺序是 document>html>body>div>p
细心的小伙伴必定会发现咱们如今用的w3c标准实际上是存在事件冒泡和事件捕获的,其实这就是w3c作的折中方案,这个方案就是先捕获后冒泡,到事件target上时,则是谁先注册谁就先执行,好比你在target上注册了一个事件捕获事件和事件冒泡事件,那决定他们执行顺序的其实就是注册的顺序。
关于闭包,我想你们都很熟悉,此次我打算用变量对象的角度来解析闭包的原理。 闭包提供了函数外部访问函数内部变量的能力,同时因为没法被回收会致使内存泄露等问题。
个人描述创建在有必定基础的状况下的,默认你明白函数执行上下文的建立,js垃圾回收机制,做用域链等基础知识。
首先咱们讲一下做用域的概念,做用域链其实由当前环境和上层环境一些类变量对象组成,它保证了当前环境对符合访问权限的变量和函数的有序访问。 这时候咱们举例有一个执行上下文A和一个在执行上下文A下建立的函数也就是执行上下文B,当执行上下文A被激活的时候,这时候执行上下文就会被建立,在建立阶段分别会建立变量对象,肯定this的指向,肯定做用域链, 以后就是执行阶段,也就是执行咱们写在函数体内的代码,分别是变量赋值,函数引用和其它代码,执行完毕后变执行上下文就会出栈,等待垃圾回收。 那么闭包是什么,上面提到了执行上下文B执行时若是访问了执行上下文A的变量对象,那么闭包就产生了,这时候函数B就是个闭包(这里是有不一样解释的,各类大神书称内层函数为闭包,而chrome中则之外层函数为闭包),变量对象中包含了argument,声明的变量和声明的函数,而咱们知道,函数的执行上下文,在执行完毕以后,生命周期结束,那么该函数的执行上下文就会失去引用。其占用的内存空间很快就会被垃圾回收器释放。但是闭包的存在,会阻止这一过程。闭包的运用就很少说了,有柯里化等
这一个我以前写过一篇总结,我就直接放进来了
history给咱们保存状态的能力,经过pushState()添加激活历史条目,经过replaceState()修改当前激活的历史条目history接收三个参数
history.pushState({page:1},'title1','?page=1')
复制代码
stateObj(状态对象) : 是一个JavaScript对象类型,咱们能够经过这个这个对象保存数据,能够在新的历史条目里(新的页面)获取到这个对象
title(标题) :目前浏览器大多不支持,保险起见能够传一个空字符串
url(地址): 新的页面地址,可选,传入的地址必须是同源,不然pushState()会抛出异常l 不传或传空字符串新历史条目默认为当前文档url
经过onpopstate事件的event对象会拷贝一份改历史记录条目的state,下面咱们来实现一个小案例。
history.pushState({
color: 'red',
},
'',
'?color=red') //添加并激活一个历史条目,histroy.html?color=red
history.back() //返回上一条历史条目 histroy.html
setTimeout(() => {
history.forward() //设置定时器后前进到下一条历史条目 ,histroy.html?color=red
}, 1000)
// 状态的历史记录条目发生变化时, popstate事件就会在对应window对象上触发.
window.onpopstate = function (e) {
console.log(e.state)
if (e.state && e.state.color === 'red') {
document.body.style.color = 'red'
}
}
复制代码
经过pushstate把页面的状态保存在state对象中,当页面的url再变回这个url时,能够经过event.state取到这个state对象,从而能够对页面状态进行还原,这里的页面状态就是页面字体颜色,其实滚动条的位置,阅读进度,组件的开关的这些页面状态均可以存储到state的里面。
补充资料
window.onpopstate = function (event) {
alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
//绑定事件处理函数.
history.pushState({
page: 1
}, "title 1", "?page=1"); //添加并激活一个历史记录条目 http://example.com/example.html?page=1,条目索引为1
history.pushState({
page: 2
}, "title 2", "?page=2"); //添加并激活一个历史记录条目 http://example.com/example.html?page=2,条目索引为2
history.replaceState({
page: 3
}, "title 3", "?page=3"); //修改当前激活的历史记录条目 http://ex..?page=2 变为 http://ex..?page=3,条目索引为3
history.back(); // 弹出 "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // 弹出 "location: http://example.com/example.html, state: null
history.go(2); // 弹出 "location: http://example.com/example.html?page=3, state: {"page":3}
复制代码
补充关于hash的一些知识点,咱们histroy是html5 的 api,在此以前还有个hash,浏览器经过记录hash值让页面不刷新跳转,背后的原理其实onhashchange 事件,该事件是在window上的,下面经过一个小demo来理解
<body>
<div>这是一段测试hash的小文字,改变hash值将触发hashchange事件,这段文字的颜色取自hash值</div>
</body>
复制代码
window.onhashchange = function (e) {
console.log('form:', e.newURL, 'from:', e.oldURL)
document.body.style.color = location.hash.slice(1)
console.log(location.hash)
}
复制代码
这样咱们就实现了对页面状态的保持,在chrome中前进后退咱们的页面,但咱们只能经过修改#后面的值来达到咱们须要的效果,这样自由性会很是低,因此有了咱们的history模式
预告:先占个坑位,预告是单向数据流,mixin,低耦合可拓展路由配置
预告:组件分割,缓存,tree shaking,图片优化