307——声明所请求的资源被暂时删除
400——请求错误,比如语句错误
401 - 请求授权失败
402 - 保持有效的 ChargeTo 标头响应
403 - 请求不允许
404 - 未找到文件、查询或 URL
405——用户在Request-Line数组中定义的方法不被允许
406——根据用户发送的Accept消息,请求的资源无法访问
407 - 与401类似,用户必须首先在代理服务器上获得授权
408 - 客户端没有在用户指定的时间内完成请求
409 - 当前资源状态无法完成请求
410 - 服务器上不再存在该资源,并且没有进一步的参考地址
411 - 服务器拒绝用户定义的Content-Length属性请求
412 - 当前请求中一个或多个请求头数组错误
413 - 请求的资源小于服务器允许的大小
414 - 请求的资源 URL 长度超过服务器允许的长度
415 - 请求的资源不支持请求的项目格式
416——请求包含Range请求头数组,当前请求资源范围内没有范围指示值,且请求不包含If-Range请求头数组
417——服务器不满足请求Expect头数组指定的期望值。 如果是代理服务器,可能下一级服务器无法满足请求
500 - 服务器导致内部错误
501 - 服务器不支持请求的功能
502 - 服务器暂时不可用,有时是为了避免系统过载
503 - 服务器过载或暂停修复
504——网关过载,服务器使用另一个网关或服务来响应用户,并且等待时间设置为更长的值
505 - 服务器不支持或拒绝支持请求头中指定的HTTP版本
41.什么是风波流及其传播机制?
事件触发后,从找到目标元素、执行目标元素的风暴、离开目标元素的整个过程称为风暴流。
W3C标准浏览器风暴流的传播分为三个阶段:捕获阶段、目标阶段、冒泡阶段
42.模块句型? commonJS AMD CMD ES6 模块43。 什么是延迟加载和预加载? 预加载:提前加载图片,当用户需要查看时直接从本地缓存中渲染。
两者的行为是相反的,一个提前加载,另一个延迟甚至不加载。 延迟加载对于减轻服务器后端的压力有一定的效果,而预加载则会减轻服务器后端的压力。 预加载广告弹窗等应用程序
44. Token一般存放在哪里? 为什么不把它存储在cookie中呢?
令牌通常放置在本地存储中。 令牌本身的存在只关心请求的安全性,而不关心令牌本身的安全性,因为令牌是在服务器端生成的,可以理解为一种加密技术。 但如果有cookie的话,浏览器的请求默认会在请求头中手动携带cookie,所以很容易被csrf攻击。
45. less 和 sass 有什么区别? 44.浏览器的同源策略机制?
Same Origin Policy,又称为SOP,全称Same Origin Policy,是浏览器最基本的安全功能。 从浏览器的短端来看网页,如果网络上的socket可以被人不受限制、未经授权地调用,那将是一个非常严重的混乱场景。 为了安全有序,浏览器内部实现了同源策略。
同源策略是指浏览器限制当前网页只能访问同源的socket资源。
所谓同源,是指当前页面和请求的socket。 双方必须具有相同的合同、相同的域名、相同的端口。 只要有差异,就会受到浏览器的限制,不允许请求。
但当一个项目变得非常大时,将所有内容放在一个网站或一台服务器中会使网站变得臃肿且性能低下。 因此,在某些场景下,我们需要绕过同源策略,请求不同Source的socket资源,这种场景称为跨域。
跨域大致有以下三种选择:
45.什么是浏览器缓存? 什么时候使用强制缓存? 何时使用协商缓存?
当我们访问同一个页面时,请求资源和数据需要一定的时间。 如果能够缓存一些资源,那么从第二次访问开始,就可以减少加载时间,提高用户体验,同时也可以减少服务器。 压力。
浏览器缓存分为强缓存和协商缓存。 当有缓存时,当客户端第一次向服务器请求数据时,客户端会将其缓存在显存或硬盘中。 当第二次获取相同资源时,强缓存与协商缓存不同。
强缓存:当客户端第二次向服务器请求相同资源时,不会向服务器发送请求,而是直接从显存/硬盘中读取。缓存由缓存的两个数组决定——服务器响应头中的控制和过期
协商缓存:当客户端第二次向服务器请求相同资源时,首先向服务器发送请求“询问”所请求的文件缓存与服务器相比是否被修改,如果修改则更新文件,如果不是就从显存/硬盘读取。协商缓存由last-modified和etag两个数组决定
46.数组方法forEach和map有什么区别?
forEach 和map 都循环遍历链表中的每一项。 forEach()和map()中匿名函数的每次执行都支持三个参数:数组中的当前项item、当前项的索引index以及原始字段输入。 匿名函数中的this指的是Window。 只能遍历链表。
它们的区别是:forEach没有返回值,但map中一定有返回值,它返回处理后所有新元素组成的链表。
作用域是在代码执行过程中形成一个独立的空间,使得空间中的变量不会泄漏到空间之外,也让独立空间中的变量函数在独立空间中运行而不影响外部环境。
作用域分为全局作用域和局部作用域。 也就是说,原本就有一个巨大的空间。 在空间定义的功能内部,生成一个独立的小空间。 全球范围是最大的范围。
但当独立空间内的数据不能满足要求时,可以从外部获取数据。 也就是说,这样的独立空间之间可以存在层级关系。 外部空间无法从内部空间获取数据,但内部空间可以。 当子空间获取父空间中的数据时,如果父空间没有,父空间也会去其父空间中查找数据,得到的链结构称为作用域链。
当变量用作值时,将首先在当前作用域中查找该变量的定义和数据。 如果没有定义,则会在父作用域中查找。 如果父作用域中有一个,则会使用它。 如果父作用域中不存在该值,则会通过父作用域搜索其父作用域,直到找到最大的全局作用域。 如果没有全局作用域,就会报错。
当将变量存储为数据容器时,即赋予变量参数时,必须首先在自己的作用域中查找该变量的定义,如果没有,则在上一级作用域中查找,直到全局没有这个变量的定义是在作用域内的,所以这个变量是全局定义的并且是形参。
48、ES6中Set和Map的原理是什么?
Set 是一个有序列表,没有重复值。 根据`Object.is()`方法判断里面的值不相等,保证不存在重复。 Set 手动删除重复值,因此您可以使用它来过滤链表中的重复值并返回结果。 Set 不是 Field 的子类型,因此您不能随机访问它的值。 但你可以使用`has()`方法来判断Set中是否存在某个值,或者使用`size`属性来查看有多少个值。 Set类型还有一个`forEach()`方法来处理每个值
Map 是一对有序的通配符,其中键可以是任何类型。 与Set类似,重复键是通过调用`Object.is()`方法来确定的,这意味着值5和字符串“5”可以作为两个相对独立的键。 任何类型的值都可以使用“set()”方法与键关联,并且可以稍后使用“get()”方法检索。 地图还有一个“size”属性和一个“forEach()”方法,使项目访问更容易。
49、为什么0.1+0.2不等于0.3,项目中如何处理?
计算机中存储的数据都是以二进制形式存储的。 两个数的物理运算是先将这两个数以2的补码形式存储在计算机中,然后用计算机中的这两个二进制数进行估计。 最后将估计结果的二进制数转换为10的补码并显示。
由于10的补码小数转换为二进制,所以规则是小数部分除以2来判断是否得到整数。 如果得到整数,则转换完成; 如果没有得到整数,则继续除以2进行判断。 因此,当0.1和0.2转换成二进制时,实际上是一个无限循环,即当它们仍然除以2并没有得到整数时,但计算机内部无限循环的数据会根据52保留到标准位。 也就是说,当计算机内部存储0.1和0.2时,本质上是不准确的。 估计出两个不准确的小数后,与准确的结果存在一定的偏差。
项目中处理这些情况有3种方法:
var result = ((0.1 * 10) + (0.2 * 10)) / 10
// result === 0.3
var result = (0.1 + 0.2).toFixed(2)
// result === 0.30
function add(...args){
var num = args.find(item => {
if(item != 0 && !item){
throw new Error("数学运算要使用数字")
}
})
var arr = args.map(item => {
var index = (item+'').indexOf('.')
if(index >= 0){
return (item+'').split('.')[1].length
}
})
arr = arr.filter(item => item)
if(arr.length){
var max = Math.max(...arr)
var data = args.map(item => item * Math.pow(10, max))
var data.reduce((a, b) => a + b) / Math.pow(10, max)
}else{
var data = args
return data.reduce((a, b) => a + b)
}
}
// 调用使用:
var num1 = add(0.1, 0.2)
console.log(num1); // 0.3
var num2 = add(1, 2)
console.log(num2); // 3
var num3 = add(1, 2.1)
console.log(num3); // 3.1
50.什么是模块化思维?
是一种在JS中将不同功能的代码封装在不同文件中的思想,这样相互引用时就不会出现命名冲突。 大多数情况下,一个文件就是一个模块
模块化实施有多种选择:
51.说说如何用js写一个无缝轮播
动态复制所有需要旋转的内容,放入之前的容器中,添加定时器,让整个容器中的内容旋转,当内容旋转到左边值-原内容长度时,快速移动内容切换到left值为0的状态。
52、JS如何实现多线程?
我们都知道JS是单线程语言,甚至有些异步事件都是在JS的主线程上运行的(详细的工作原理可以看我的另一篇博客中的JS代码运行机制)。 像setTimeout、ajax或者一些dom元素的骚乱之类的异步请求都是在JS的主线程上执行的。 这些操作并不会在浏览器中开辟新的线程来执行,而是当此类异步操作被操作或者被触发时就进入到storm队列中,然后开始在JS主线程中运行。
首先我们来说说浏览器的线程。 浏览器中的主线程包括UI渲染线程、JS主线程、GUI风暴触发线程、http请求线程。
JS作为一种脚本语言,主要用于与用户交互以及操作DOM。 这就决定了它只能是单线程的,否则会带来非常复杂的同步问题。 (这里我们不做这类问题的研究)
但对于单线程语言来说,有一个致命的决定。 如果说,执行脚本语言时,某块函数需要花费大量时间来执行,就会造成阻塞。 对于这样的项目来说,用户体验是非常差的,所以在项目的开发过程中是不允许这些现象存在的。
其实JS给我们提供了一个Worker类,它的作用就是解决这些阻塞现象。 当我们使用这个类的时候,它也会向浏览器申请一个新的线程。 该线程用于单独执行一个js文件。 var worker = new Worker(js 文件路径); 那么这句话就会申请一个线程来执行js文件。 这样也实现了js的多线程。
53、闭包的使用场景是什么?
当函数作为值返回时,相当于返回一个通道,可以访问函数词法作用域内的变量,即保存了函数所需的数据结构,以及数据结构中的值是在内部函数执行时创建的,在外部函数执行时销毁的,但由于内部函数以值的形式返回,所以这些值可以被保存。 而且直接访问比较困难,必须通过返回的函数。 这也是隐私。
本来,执行过程和词法作用域是关闭的,而这个返回的函数就像一个虫洞,打开又挂起。
闭包的生成非常简单。 执行过程完成后,返回函数,或者保留函数,即生成闭包。
function debounce(fn, interval) {
let timer = null; // 定时器
return function() {
// 清除上一次的定时器
clearTimeout(timer);
// 拿到当前的函数作用域
let _this = this;
// 拿到当前函数的参数数组
let args = Array.prototype.slice.call(arguments, 0);
// 开启倒计时定时器
timer = setTimeout(function() {
// 通过apply传递当前函数this,以及参数
fn.apply(_this, args);
// 默认300ms执行
}, interval || 300)
}
}
function throttle(fn, interval) {
let timer = null; // 定时器
let firstTime = true; // 判断是否是第一次执行
// 利用闭包
return function() {
// 拿到函数的参数数组
let args = Array.prototype.slice.call(arguments, 0);
// 拿到当前的函数作用域
let _this = this;
// 如果是第一次执行的话,需要立即执行该函数
if(firstTime) {
// 通过apply,绑定当前函数的作用域以及传递参数
fn.apply(_this, args);
// 修改标识为null,释放内存
firstTime = null;
}
// 如果当前有正在等待执行的函数则直接返回
if(timer) return;
// 开启一个倒计时定时器
timer = setTimeout(function() {
// 通过apply,绑定当前函数的作用域以及传递参数
fn.apply(_this, args);
// 清除之前的定时器
timer = null;
// 默认300ms执行一次
}, interval || 300)
}
}
var arr =['aa','bb','cc'];
function incre(arr){
var i=0;
return function(){
//这个函数每次被执行都返回数组arr中 i下标对应的元素
return arr[i++] || '数组值已经遍历完';
}
}
var next = incre(arr);
console.log(next());//aa
console.log(next());//bb
console.log(next());//cc
console.log(next());//数组值已经遍历完
var fn=(function(){
var cache={};//缓存对象
var calc=function(arr){//计算函数
var sum=0;
//求和
for(var i=0;i<arr.length;i++){
sum+=arr[i];
}
return sum;
}
return function(){
var args = Array.prototype.slice.call(arguments,0);//arguments转换成数组
var key=args.join(",");//将args用逗号连接成字符串
var result , tSum = cache[key];
if(tSum){//如果缓存有
console.log('从缓存中取:',cache)//打印方便查看
result = tSum;
}else{
//重新计算,并存入缓存同时赋值给result
result = cache[key]=calc(args);
console.log('存入缓存:',cache)//打印方便查看
}
return result;
}
})();
fn(1,2,3,4,5);
fn(1,2,3,4,5);
fn(1,2,3,4,5,6);
fn(1,2,3,4,5,8);
fn(1,2,3,4,5,6);
function fn(){
var name='hello'
setName=function(n){
name = n;
}
getName=function(){
return name;
}
//将setName,getName作为对象的属性返回
return {
setName:setName,
getName:getName
}
}
var fn1 = fn();//返回对象,属性setName和getName是两个函数
console.log(fn1.getName());//getter
fn1.setName('world');//setter修改闭包里面的name
console.log(fn1.getName());//getter
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
var hasNumber = curryingCheck(/d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
var p1 = "ss";
var p2 = "jj";
function testSetTime(para1,para2){
return (function(){
console.log(para1 + "-" + para2);
})
}
var test = testSetTime(p1, p2);
setTimeout(test, 1000);
setTimeout(function(){
console.log(p1 + "-" + p2)
},1000)
var Singleton = (function () {
var instance;
function createInstance() {
return new Object("I am the instance");
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
54. 常见的兼容性问题有哪些?
document.getElementById('id名')
document.getElementsByTagName('标签名')
document.getElementsByName('name属性值')
document.querySelector('css选择器')
document.querySelectorAll('css选择器')
获取体积的高度
// 当有文档声明的时候
document.documentElement.scrollTop
document.documentElement.srollLeft
// 没有文档声明的时候
document.body.scrollTop
document.body.scrollLeft
解决办法是使用兼容的写法:
// 获取
var t = document.documentElement.scrollTop || document.body.scrollTop
var l = document.documentElement.srollLeft || document.body.scrollLeft
// 设置
document.documentElement.scrollTop = document.body.scrollTop = 数值
document.documentElement.srollLeft = document.body.scrollLeft = 数值
获得风格
// W3C标准浏览器
window.getComputedStyle(元素)
// 低版本IE中
元素.currentStyle
兼容函数封装:
function getStyle(ele,attr){
if(window.getComputedStyle){
return getComputedStyle(ele)[attr]
}else{
return ele.currentStyle[attr]
}
}
事件监听器
// W3C浏览器
ele.addEventListener(事件类型,函数)
// 低版本Ie
ele.attachEvent('on事件类型',函数)
使用函数封装的形式来解决:
function bindEvent(ele,type,handler){
if(ele.addEventListener){
ele.addEventListener(type,handler)
}else if(ele.attachEvent){
ele.attachEvent('on'+type,handler)
}else{
ele['on'+type] = handler
}
}
事件分拆
// W3C浏览器
ele.removeEventListener(事件类型,函数)
// 低版本Ie
ele.detachEvent('on事件类型',函数)
使用函数封装的形式来解决:
function unBind(ele,type,handler){
if(ele.removeEventListener){
ele.removeEventListener(type,handler)
}else if(ele.detachEvent){
ele.detachEvent('on'+type,handler)
}else{
ele['on'+type] = null
}
}
获取事件对象
// W3C浏览器
元素.on事件类型 = function(e){}
元素.addEventListener(事件类型,fn)
function fn(e){
}
// 在低版本IE中
元素.on事件类型 = function(){ window.event }
元素.addEventListener(事件类型,fn)
function fn(){
window.event
}
使用泄漏算子解决:
元素.on事件类型 = function(e){
var e = e || window.event
}
元素.addEventListener(事件类型,fn)
function fn(e){
var e = e || window.event
}
防止默认行为
// W3C浏览器
元素.on事件类型 = function(e){
e.preventDefault()
}
// 在低版本IE中
元素.on事件类型 = function(){ window.event.returnValue = false }
通过包装函数来解决
元素.on事件类型 = function(e){
var e = e || window.event
e.preventDefault?e.preventDefault():e.returnValue=false
}
阻止风暴冒泡
// W3C浏览器
元素.on事件类型 = function(e){
e.stopPropagation()
}
// 在低版本IE中
元素.on事件类型 = function(){ window.event.cancelBubble = true }
通过函数封装解决:
元素.on事件类型 = function(e){
var e = e || window.event
e.stopPropagation?e.stopPropagation():e.cancelBubble=true
}
获取精确的目标元素
// W3C浏览器
元素.on事件类型 = function(e){
e.target
}
// 在低版本IE中
元素.on事件类型 = function(){ window.event.srcElement }
通过泄漏算子解决:
元素.on事件类型 = function(e){
var e = e || window.event
var target = e.target || e.srcElement;
}
获取键码
// W3C浏览器
元素.on事件类型 = function(e){
e.keyCode
}
// 在低版本火狐中
元素.on事件类型 = function(e){
e.which
}
通过泄漏算子解决:
元素.on事件类型 = function(e){
var e = e || window.event
var keycode = e.keyCode || e.which;
}
55. JS中如何停止冒泡?
使用风暴对象来阻止风暴冒泡。 在之前的w3c浏览器中,使用storm对象来阻止它:
事件对象.stopPropagation()
在ie较低版本的浏览器中,使用storm对象的属性来停止:
EventObject.cancelBubble = true
现在的w3c浏览器在ie低版本浏览器中也支持这种写法,所以以前我们阻止风波冒泡的时候,需要考虑兼容的写法,但现在不需要了,直接使用写法即可在ie低版本浏览器中。
56. 两个字段 var A = [1, 5, 6]; var B = [2, 6, 7],实现一种查找仅存在于 A 或仅存在于 B 中的所有数字的方法。
function getDiff(arr, brr){
// 仅存在于arr中的内容
var onlyArr = arr.filter(item => !brr.some(v => item === v))
// 仅存在于brr中的内容
var onlyBrr = brr.filter(v => !arr.some(item => v === item))
// 需要哪个就返回哪个,或者一起返回
return {
"仅存在于arr中的内容": onlyArr,
"仅存在于brr中的内容": onlyBrr
}
}
57.你知道构造函数吗? 什么是班级? 两者有什么区别?
虽然es5中的构造函数是定义一个类,但是它可以实例化对象,虽然es6中的类是构造函数的语法糖。 但还是有一点区别:
58、是否存在a(a==0 && a==1)的值为true的情况?
var value = -1
Object.defineProperty(window,'a',{
get(){
return value+=1;
}
})
if(a===0&&a===1){ // true
console.log('success')
}
59. for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 1000); } 要求:输出0,1,2,3,4
首先这个面试题考察的是对js中异步代码和作用域的理解:
js中常见的异步代码有定时器和ajax。 js执行代码的流程是遇到同步代码就执行,遇到异步代码就交给浏览器的webAPI处理。 当webAPI中的异步执行到期时,webAPI会将需要执行的反弹函数放入任务队列中等待执行,因此js中的所有异步代码总会在所有同步代码执行完成后才执行任务队列中的代码被执行。
本题中,循环是同步代码,而定时器是异步代码,所以定时器代码只有在整个循环执行完之后才能执行。
for循环中var定义的变量是全局变量。 当定时器回调函数中输出变量时,根据作用域规则,首先在当前作用域中定义变量i。 如果没有找到,则进入下一级范围内查找。 这个时候在本地范围内是找不到的。 到上层范围,即全局查找。 全局中 i 的值为 5,因为循环已经执行完毕。
最终会输出五个5。
二是查看类似问题的解决方案javascript的匿名函数,间接判断自己是否出现过类似的情况:
解决这个问题的办法是,在回调函数中输出i时,不要在全局中寻找i,因为循环执行结束后,全局i已经变成了5。 根据这个思路javascript的匿名函数,解决方案有两种:
for(var i = 0;i < 5; i++){
(function(i) {
setTimeout(function() {
console.log(i)
}, 1000)
})(i)
}
原理是自调用函数会形成一个作用域,循环5次会形成5个作用域,每个作用域代码执行时都会通过数组i传递。 所以每个作用域中的i是不同的,分别为:0 1 2 3 4。当执行该作用域内的异步代码时,自己的作用域内没有i变量定义,那么上级作用域就是自己的作用域调用函数,并找到单个 i。 最后可以输出:0 1 2 3 4
将循环代码中的var替换为es6的let
for(let i = 0;i < 5; i++){
setTimeout(function() {
console.log(i)
}, 1000)
}
es6的let有自己的块级作用域。 原理与第一种方案相同。 转换成es5后,代码是一样的。
60. 实现一个add方法,使估计结果满足以下期望: - add(1)(2)(3)() = 6 - add(1,2,3)(4)() = 10
function add (...args) {
if(args.length === 3){
return -(args[0] * args[1] * 2 + args[2] * 2)
}else{
return -args[args.length-1]
}
}
function currying (fn) {
let args = []
return function _c (...newArgs) {
if (newArgs.length) {
args = [
...args,
...newArgs
]
return _c
} else {
return fn.apply(this, args)
}
}
}
let addCurry = currying(add)
var a = addCurry(1)(2)(3)()
console.log(-a); // 10
var b = addCurry(1,2,3)(4)()
console.log(6 - b); // 10