1. DOM性能

DOM操作是非常昂贵的,所谓昂贵就是指DOM操作会耗费CPU,重复的重排和重绘也就是重复渲染,比较耗时耗费CPU的计算比较多。如果频繁操作可能会造成卡顿的问题,所以我们需要注意这些地方。

用变量存储获取到的页面元素,避免频繁查询页面dom。

多个dom操作建议合并统一操作。

for (var i = 0; i < 10; i++) {
    var div = document.createElement('div');
    document.body.appendChild(div);
}

// 使用文档片段保存操作,一次性插入页面
var frag = document.createDocumentFragment();

for (var i = 0; i < 10; i++) {
    var div = document.createElement('div');
    frag.appendChild(div);
}

document.body.appendChild(frag);

2. BOM

BOM就是浏览器提供的一些api。

浏览器信息

// 获取浏览器信息
var ua = navigator.userAgent;

屏幕信息

screen.width
screen.height

地址信息

location.href
location.protocol
location.pathname
location.host
location.origin
location.search
location.hash

历史记录信息

// 后退
history.back();
// 前进
history.forward();

3. 事件冒泡

事件冒泡就是我在一个标签上绑定了一个事件,当事件被触发的时候会沿着他的祖先标签一级一级的向上传递,祖先中如果有绑定和他相同类型的事件也会触发。

<body onclick>
    <div onclick>
        <p>
            <span onclick>测试</span>
        </p>
    </div>
</body>

比如上面的代码中在span标签中绑定了onclick事件,当点击span标签的时候,会触发自身的onclick事件,然后会向父级找到p标签,p标签并没有绑定事件所以继续向上,找到div标签,div绑定了click事件所以会触发,然后继续向上找到body…

事件冒泡的存在是绝对合理的。举个简单的例子,A住在某小区的1号楼1单元1楼,B进入到A的家中肯定也就进入了1楼,1单元,1号楼,这个小区。

span标签在p标签内,p标签又在div标签内,点击了span肯定也就点击了p标签,div标签。

我们可以通过阻止事件冒泡的方式让事件停止继续向上传递,如果我们在span的onclick事件中阻止了冒泡那么事件就不会执行到祖先标签中。如果在祖先的任意一级阻止了冒泡,事件就会终止在阻止的这一级不会继续向上传递。

span.onclick = function(event) {
    event.stopPropagation();
}

4. 事件委托

事件委托又叫事件代理,一般是利用事件冒泡的功能对事件进行批量处理。

比如页面中有10个li,每点击一个li都要触发一次点击事件,我们可以给每个li绑定一个点击事件,这样就会绑定10次,也可以给li的父节点绑定事件,只需要绑定一次,根据事件冒泡的原理,点击li的时候绑定在li父级的事件也会触发。

ul.onclick = function() {

}

事件委托一个典型的应用场景是当子元素个数不确定的时候用来解决事件问题。当页面有3个li的时候,我们给每个li绑定一个点击事件,如果页面需要动态增加1个li的时候,我们除了给页面添加两个li之外还要给新增了两个li绑定事件。

var li = document.createElement('li');

li.onclick = function() {}

ul.appendChild(li);

这就比较麻烦,如果我们的事件是绑定在li的父节点上时,我们就可以自由的增删li节点,并且新增的节点也会触发父节点上的事件。因为前面说了,点击了li也相当于点击了父节点。

这种将事件绑定在需要监控的元素祖先节点上的方式一般称为事件委托。

5. JSONP

很多人对jsonp的理解都停留在概念上,没有真正理解过他的原理,他为什么可以跨域,当然不仅仅是script标签不受同源策略影响,实际上jsonp是一种前后端约定的解决方案。

不过现在基本已经很少用到了。因为现在已经有了更流行的CORS方案,相对来说也会更安全,不过jsonp还是有其自身的优势的。

很多人都知道浏览器的同源策略,就是发送请求的页面地址和被请求的接口地址的域名,协议,端口三者必须一致,否则浏览器就会拦截这种请求。浏览器拦截的意思不是说请求发布出去,请求还是可以正常触达服务器的,如果服务器正常返回了浏览器也会接收的到,只是不会交给我们所在的页面。这一点查看network是可以看到的。

jsonp一般是利用script标签的src属性,对于服务器来说只有请求和响应两种操作,请求来了就会响应,无论响应的是什么。请求的类型实在太多了。

浏览器输入一个url是一个请求,ajax调用一个接口也是一个请求,img和script的src也是请求。这些地址都会触达服务器。那为什么jsonp一般会选用script标签呢,首先大家都知道script加载的js是没有跨域限制的,因为加载的是一个脚本,不是一个ajax请求。

你可以理解为浏览器限制的是XMLHttpRequest这个对象,而script是不使用这个对象的。

仅仅没有限制还不够,还有一个更重要的点因为script是执行js脚本的标签,他所请求到的内容会直接当做js来执行。

这也可以看出,jsonp和ajax对返回参数的要求是不同的,jsonp需要服务返回一段js脚本,ajax需要返回的是数据。

因此这就要求服务器单独来处理jsonp这中请求,一般服务器接口会把响应的数据通过函数调用的方式返回,比如说返回的内容是'yd',那就要返回成cb('yd')

cb('yd')

这是一段函数调用的脚本,通过script标签加载之后会立即执行的,如果我们在全局定义一个cb函数。那么这段脚本执行的时候就会调用到我们定义的那个函数,函数中的参数就是服务返回的值了。前端也就可以在这个函数中获取到了。

function cb (data) {
    console.log(data);
}

所以说jsonp是前后端共同约定的一种结果。

为什么jsonp需要使用script标签而不用link或者img标签呢?link和img也不受同源策略的影响,但是如果使用img去做请求不会实现想要的效果,因为img加载的是图片资源,是用来展示的。link加载的是css资源,是以css tree解析的。我们想要的数据应该用在js中。所以必须使用可以执行js的脚本去请求。

6. 描述cookie, localStorage, sessionStorage区别

本身是用于浏览器服务器通讯的,最早是被借用到本地存储中来,因为在H5之前前端基本没有存储数据的能力,所以只能选择将数据存储在cookie中,但是cookie的真正用处并不是存储数据,他是浏览器和服务器通讯,所以cookie中存储的数据会随请求发送给服务器。

服务器可以获取到浏览器发送过来的cookie,也可以去修改cookie,所以cookie最好的用处是记录用户信息。

当用户登录成功之后,服务器将用户登录标识存储在cookie中,浏览器下次请求服务器的时候,cookie会随请求一并发送给服务器,服务器可以通过这个标识判断用户是否登录,以及用户的权限。请求中携带cookie是一种主动能力,开发者不需要额外设置。

所以cookie的价值在于此而不在于存储前端数据,否则H5的普及会导致cookie的衰败,显然并没有。cookie存储大小一般为4kb。用于前后端通信4kb基本足够了。太多的cookie会导致请求过重。

cookie设置的时候可以设置有效期,存储的path路径,存储的域名,被存储数据是否转译,如果是服务端存储的cookie也可以设置是否只有服务器可操作,前端不得操作。

cookie设置的有效期如果不设置则为临时cookie,当会话结束后会消失,windows系统基本关闭浏览器会话就结束了,mac系统需要退出浏览器才会结束会话。

H5提供的前端永久存储能力,最大可以存储5M,每个域名都可以存储5M,当超过5M时会抛错,可通过try…catch捕获。

localStorage是前端存储能力,所以他只用于存储,相比较cookie不会发送给服务器,我不知道为啥有些面试官喜欢问localStorage会不会发送给服务端这种问题,localStorage和cookie根本就没有可比性,就不是一个东西, 设计初衷都不一样。

localStorage是永久存储,除非手动删除,不然会一直存在浏览器当中。

和localStorage类似,sessionStorage也是H5提供的一种存储能力,同样最大可以存储5M,每个域名都可以存储5M,当超过5M时会抛错,可通过try…catch捕获。

sessionStorage是会话存储,当会话结束sessionStorage就消失了,很多人说关闭浏览器sessionStorage才会消失这是不正确的。

sessionStorage是会话结束就会消失,关闭浏览器会话肯定结束了,但是不关闭浏览器会话结束sessionStorage也会消失。

比如我们在A页面设置了sessionStorage,A页面跳转到了B页面,B页面是可以访问到A设置的sessionStorage的,但如果我们复制B页面的url地址,新开选项卡粘贴B的url回车访问,这个时候就访问不到A设置的sessionStorage。这是因为这样打开的B和A不是同一个会话。

7. http状态码

服务器收到了请求

请求成功,比如常见的200

重定向,比如服务器发现浏览器访问的地址不正确就会返回这个状态码告诉前端访问另一个地址。

比如用户访问个人中心的时候,服务器发现用户没有登录或者登录状态失效了,就要通知用户跳转到登录页面重新登录,此时就可以使用302。

301是永久重定向,302是临时重定向。如果一个页面的地址迁移了,但之前放出去的地址基本修改不了,可以用301永久转发到新页面,如果只是临时转发到页面使用302。

304表示资源未被修改,一般配合协商缓存使用。我们知道用户访问浏览器的时候浏览器会向服务器发送请求获取网站的数据,比如浏览器向服务器请求一段js代码,服务器返回给浏览器这个过程是需要网络传输的,网络的快慢决定浏览器拿到这段js的时间也就决定了页面访问的时间。

为了提高效率,浏览器请求服务器获得js之后会在浏览器中备份,当再次请求这个js文件时浏览器会先询问服务器,这个js是否有变化,如果文件变化了,服务器会返回新的js给浏览器状态码为200,浏览器使用后备份,如果js文件没有变化,服务器会告诉浏览器文件未变化,状态码为304,浏览器就时候自己备份的js文件,节省了文件的传输时间。

基本都是浏览器端的错误,比如常见的404,就是请求的地址不存在,浏览器输入错地址了。403是用户访问的地址没有权限。这个用户不可以访问这个地址。

基本都是服务器的错误,比如说服务器长时间不返回数据就会超时。

其实http的协议和规范就是一个约定,我们在使用它的时候按照这个约定执行就可以了。

8. Restful API

是一种新的API设计方法,其实也不新了,好多年了,很多公司已经在用了,甚至很多人都觉得他不好用淘汰了。

Restful不是规范,是一种风格,所以可以用也可以不用,看你自己团队的要求。

相比传统API将每个url当做一种功能的设计来说,Restful是把每个url当做一个唯一的资源。

我们知道传统的API是将每个url当做一种功能来设计,每一种功能就需要一个api地址,比如说新增,修改,删除,这就会出现三个url地址。一个地址对应一个功能。

Restful是把url当做资源来设计,比如我们想找到电脑里的某一个文件,那么这个文件是有访问路径的。这个路径就是url,访问到的文件就是资源。访问资源的时候是无法传递参数的。

所以Restful的设计方式就是,尽量不用url参数,用method表示操作类型,一个资源地址我们通过post请求方式表示更新,delete请求方式表示删除。

# 传统的API
/api/list?pageNum=2
# Restful
/api/list/2

9. 刷新操作对缓存的影响

强制缓存有效,协商缓存有效

强制缓存失效,协商缓存有效

强制缓存失效,协商缓存失效

10. onload和DOMContentLoaded区别

window.onload是资源全部加载完毕才执行,包括图片等资源。

DOMContentLoaded是DOM渲染完成之后才执行,图片可能尚未下载。

11. 节流和防抖

防抖debounce, 比较监听输入框,当用户频繁输入的时候会频繁调用事件,可以防抖阻止重复请求,只在最后一次才请求。

节流throttle和防抖的区别是防抖是最后一次触发,节流是保持一个频率连续触发,比如拖拽的时候要随时拿到该元素拖拽的位置,如果直接用drag事件,则会频繁触发,很容易导致卡顿,一般我们可以限制无论拖拽速度多快,都会每隔100ms触发一次。

13. 函数声明和函数表达式区别

函数声明的函数可以在声明前调用,表达式创建的函数只能创建后调用。

aaa();
function aaa() {

}

var bbb = function() {

}
bbb();

14. new Object和Object.create区别

{}等同于new Object, 存在原型Object.prototype, {}和new Object都是Object的实例。所以{}和new Object的proto等于Object.prototype。

Object.create可以自定义原型prototype,如果传入null就是没有原型。可以创建一个干净的对象。

Object.create(null); // 原型为null

Object.create({a: 1}); // 原型为{a: 1}

15. 什么是JSON

json是一种数据格式,本质是一段字符串。json格式与js对象结构一致,对js语言更友好。

json是数据格式,不是js特有的对象,在java,php中都可以使用json。

window.JSON是一个全局对象,是js中提供的用于操作json的方法。JSON.stringify和JSON.parse。

16. 获取url中的参数

const query = new URLSearchParams(location.search);
const value = query.get('name');
query.forEach(function(value, key) {
    console.log(value, key);
})
console.log(value);

17. 数组拍平

数组的concat方法可以将数组解开,比如

[1,2].concat([1,2,3]) // [1, 2, 1, 2, 3];

我们可以利用concat配合递归将多级数组拍平。

function flat(arr) {
    const isDeep = arr.some(item => item instanceof Array);
    if (!isDeep) { // 数组里面没有数组了就返回
        return arr;
    }
    const res = Array.prototype.concat.apply([], arr);
    return flat(res);
}

18. requestAinmateFrame

这是一个动画的API,一般我们对于动画来说,想要动画流畅每秒动画要更新60帧,这也是人眼适应的频率。也就是16.67ms更新一次。相对setTimeout来说requestAinmateFrame性能更加优秀,对于一些后台标签或隐藏iframe中的内容,requestAinmateFrame会暂停,而setTimeout依然执行。

requestAinmateFrame会在一个恰当的时机执行,可以理解为requestAinmateFrame会检测页面的渲染,当渲染空闲的时候才会执行,而setTimeout是到时间就执行,这就会造成卡UI的情况,UI还没渲染完成setTimeout执行了如果setTimeout中存在渲染UI, 就会卡住之前渲染,造成卡顿。

19. 面试技巧

简历要简洁明了,突出个人技能和项目经验,一般简历写个两页纸就可以了,太多也没人看,太少单薄,我一般面试的时候就只看前两页。

简历中个人技能要写在前面,项目经验写在后面,不要弄反了,项目经验中要描述清楚技术栈。个人介绍,工作经历,教育经历简单就好。

如果个人博客和开源作品质量都不错,可以写在简历中,如果没有或者质量一般最后不要放。

简历要保证能力的真实性,不要造价,水份不要过大,切忌不要写精通,也没几个人能真的做到精通。

如何看待加班?这个还是要看个人,我一般是不推荐加班的,就像借钱,救急不救穷,要说明白怎么看待加班,什么样的加班接受什么样的加班不接受。项目特别急并且价值特别大加加班也是没什么的,常态的加班肯定是不可取的。长时间的加班带来的结果必定的虚假繁荣,生产力的低下。

面试中最好不要挑战面试官,如果面试官说的不对可以低调的或者委婉的转变一下说辞,离开也可以不要和面试官争辩,即使你不想要这份工作。

要给面试官惊喜,证明你可以做的更好知道的更多,一个问题如果你能回答及格就要考虑怎么回答到优秀,但是要做到更好不是更多,别说太多废话,也不要说起没完。比如说这个问题还有一个更好的解决办法等等。

遇到不会的问题说出你知道的部分即可,但别岔开话题,给人心虚的感觉,而且面试的过程中你也基本很难岔开话题,这会让面试官很反感。

对于自己的缺点可以换一种话术,委婉一些的表述,人都有缺点这没什么,你可以说你知道自己哪部分知识薄弱最近也正在学习,这样就化缺点为优点了。