前端面试小常识记录
前端面试题(JavaScript)
1、get 请求传参长度的误区
误区:我们经常说get 请求参数的大小存在限制,而post 请求的参数大小是无限制的。
实际上HTTP 协议从未规定GET/POST 的请求长度限制是多少。对get 请求参数的限制
是来源与浏览器或web 服务器,浏览器或web 服务器限制了url 的长度。为了明确这个
概念,我们必须再次强调下面几点:
HTTP 协议未规定GET 和POST 的长度限制
GET 的最大长度显示是因为浏览器和web 服务器限制了URI 的长度
不同的浏览器和WEB 服务器,限制的最大长度不一样
要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度8182byte
2、补充get 和post 请求在缓存方面的区别
post/get 的请求区别,具体不再赘述。
补充补充一个get 和post 在缓存方面的区别:
get 请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
post 不同,post 做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get 请求适合于请求缓存。
3、说一下闭包
一句话可以概括:闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放
4、如何解决异步回调
promise、generator、async/await
5、说说前端中的事件流
HTML 中与javascript 交互是通过事件驱动来实现的,例如鼠标点击事件onclick、页面
的滚动事件onscroll 等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。
想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。
什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2 级事件流包括下面几个阶段。
事件捕获阶段
处于目标阶段
事件冒泡阶段
addEventListener:addEventListener 是DOM2 级事件新增的指定事件处理程序的操作,这个方法接收3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
IE 只支持事件冒泡。
6、说一下图片的懒加载和预加载
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。
懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
7、mouseover 和mouseenter 的区别
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave
8、JS 的new 操作符做了哪些事情
new 操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象。
9、改变函数内部this 指针的指向函数(bind,apply,call 的区别)
通过apply 和call 改变函数的this 指向,他们两个函数的第一个参数都是一样的表示要
改变指向的那个对象,第二个参数,apply 是数组,而call 则是arg1,arg2...这种形式。通过bind 改变this 作用域会返回一个新的函数,这个函数不会马上执行。
10、JS 的各种位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop,
offsetTop,clientTop 的区别?
clientHeight:表示的是可视区域的高度,不包含border 和滚动条
offsetHeight:表示可视区域的高度,包含了border 和滚动条
scrollHeight:表示了所有区域的高度,包含了因为滚动被隐藏的部分。
clientTop:表示边框border 的厚度,在未指定的情况下一般为0
scrollTop:滚动后被隐藏的高度,获取对象相对于由offsetParent 属性指定的父坐标(css定位的元素或body 元素)距离顶端的高度。
11、Ajax 解决浏览器缓存问题
在ajax 发送请求前加上anyAjaxObj.setRequestHeader("If-Modified-Since","0")。
在ajax 发送请求前加上anyAjaxObj.setRequestHeader("Cache-Control","no-cache")。
在URL 后面加上一个随机数: "fresh=" + Math.random()。
在URL 后面加上时间搓:"nowtime=" + new Date().getTime()。
如果是使用jQuery,直接这样就可以了$.ajaxSetup({cache:false})。这样页面的所有ajax
都会执行这条语句就是不需要保存缓存记录。
12、JS 中的垃圾回收机制
必要性:由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他
们进行动态的存储分配。JavaScript 程序每次创建字符串、数组或对象时,解释器都必
须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript 的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
这段话解释了为什么需要系统需要垃圾回收,JS 不像C/C++,他有自己的一套垃圾回收机制(Garbage Collection)。JavaScript 的解释器可以检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时候,他就知道不再需要这个对象,可以把它所占用的内存释放掉了。例如:
var a="hello world";
var b="world";
var a=b;
//这时,会释放掉"hello world",释放内存以便再引用
垃圾回收的方法:标记清除、计数引用。
标记清除
这是最常见的垃圾回收方式,当变量进入环境时,就标记这个变量为”进入环境“,从逻
辑上讲,永远不能释放进入环境的变量所占的内存,永远不能释放进入环境变量所占用
的内存,只要执行流程进入相应的环境,就可能用到他们。当离开环境时,就标记为离
开环境。
垃圾回收器在运行的时候会给存储在内存中的变量都加上标记(所有都加),然后去掉环境变量中的变量,以及被环境变量中的变量所引用的变量(条件性去除标记),删除所有被标记的变量,删除的变量无法在环境变量中被访问所以会被删除,最后垃圾回收器,完成了内存的清除工作,并回收他们所占用的内存。
引用计数法
另一种不太常见的方法就是引用计数法,引用计数法的意思就是每个值没引用的次数,当声明了一个变量,并用一个引用类型的值赋值给改变量,则这个值的引用次数为1,;相反的,如果包含了对这个值引用的变量又取得了另外一个值,则原先的引用值引用次数就减1,当这个值的引用次数为0 的时候,说明没有办法再访问这个值了,因此就把所占的内存给回收进来,这样垃圾收集器再次运行的时候,就会释放引用次数为0 的这些值。
用引用计数法会存在内存泄露,下面来看原因:
function problem() {
var objA = new Object();
var objB = new Object();
objA.someOtherObject = objB;
objB.anotherObject = objA;
}
在这个例子里面,objA 和objB 通过各自的属性相互引用,这样的话,两个对象的引用次数都为2,在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,因为计数不为0,这样的相互引用如果大量存在就会导致内存泄露。特别是在DOM 对象中,也容易存在这种问题:
var element=document.getElementById(’‘);
var myObj=new Object();
myObj.element=element;
element.someObject=myObj;
这样就不会有垃圾回收的过程。
13、对象深度克隆的简单实现
function deepClone(obj){
var newObj= obj instanceof Array ? []:{};
for(var item in obj){
var temple= typeof obj[item] == 'object' ? deepClone(obj[item]):obj[item];
newObj[item] = temple;
}
return newObj;
}
ES5 的常用的对象克隆的一种方式。注意数组是对象,但是跟对象又有一定区别,所以
我们一开始判断了一些类型,决定newObj 是对象还是数组。
14、==和===、以及Object.is 的区别
(1) ==
主要存在:强制转换成number,null==undefined
" "==0 //true
"0"==0 //true
" " !="0" //true
123=="123" //true
null==undefined //true
(2)Object.js
主要的区别就是+0!=-0 而NaN==NaN
(相对比===和==的改进)
15、setTimeout、setInterval 和requestAnimationFrame 之间的区别
与setTimeout 和setInterval 不同,requestAnimationFrame 不需要设置时间间隔,
大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60 次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms。
RAF 采用的是系统时间间隔,不会因为前面的任务,不会影响RAF,但是如果前面的
任务多的话,会响应setTimeout 和setInterval 真正运行时的时间间隔。
特点:
(1)requestAnimationFrame 会把每一帧中的所有DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率。
(2)在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的CPU、GPU 和内存使用量
(3)requestAnimationFrame 是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU 开销。
16、实现JS 中所有对象的深度克隆(包装对象,Date 对象,正则对象)
通过递归可以简单实现对象的深度克隆,但是这种方法不管是ES6 还是ES5 实现,都有
同样的缺陷,就是只能实现特定的object 的深度复制(比如数组和函数),不能实现包
装对象Number,String , Boolean,以及Date 对象,RegExp 对象的复制。
(1)前文的方法
function deepClone(obj){
var newObj= obj instanceof Array?[]:{};
for(var i in obj){
newObj[i]=typeof obj[i]=='object'?
deepClone(obj[i]):obj[i];
}
return newObj;
}
这种方法可以实现一般对象和数组对象的克隆,比如:
var arr=[1,2,3];
var newArr=deepClone(arr);
// newArr->[1,2,3]
var obj={
x:1,
y:2
}
var newObj=deepClone(obj);
// newObj={x:1,y:2}
但是不能实现例如包装对象Number,String,Boolean,以及正则对象RegExp 和Date 对象的
克隆,比如:
//Number 包装对象
var num=new Number(1);
typeof num // "object"
var newNum=deepClone(num);
//newNum -> {} 空对象
//String 包装对象
var str=new String("hello");
typeof str //"object"
var newStr=deepClone(str);
//newStr-> {0:'h',1:'e',2:'l',3:'l',4:'o'};
//Boolean 包装对象
var bol=new Boolean(true);
typeof bol //"object"
var newBol=deepClone(bol);
// newBol ->{} 空对象
....
(2)valueof()函数
所有对象都有valueOf 方法,valueOf 方法对于:如果存在任意原始值,它就默认将对象
转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值,
因此默认的valueOf()方法简单地返回对象本身,而不是返回一个原始值。数组、函数和
正则表达式简单地继承了这个默认方法,调用这些类型的实例的valueOf()方法只是简单
返回这个对象本身。
对于原始值或者包装类:
function baseClone(base){
return base.valueOf();
}
//Number
var num=new Number(1);
var newNum=baseClone(num);
//newNum->1
//String
var str=new String('hello');
var newStr=baseClone(str);
// newStr->"hello"
//Boolean
var bol=new Boolean(true);
var newBol=baseClone(bol);
//newBol-> true
其实对于包装类,完全可以用=号来进行克隆,其实没有深度克隆一说,
这里用valueOf 实现,语法上比较符合规范。
对于Date 类型:
因为valueOf 方法,日期类定义的valueOf()方法会返回它的一个内部表示:1970 年1 月
1 日以来的毫秒数.因此我们可以在Date 的原型上定义克隆的方法:
Date.prototype.clone=function(){
return new Date(this.valueOf());
}
var date=new Date('2010');
var newDate=date.clone();
// newDate-> Fri Jan 01 2010 08:00:00 GMT+0800
对于正则对象RegExp:
RegExp.prototype.clone = function() {
var pattern = this.valueOf();
var flags = '';
flags += pattern.global ? 'g' : '';
flags += pattern.ignoreCase ? 'i' : '';
flags += pattern.multiline ? 'm' : '';
return new RegExp(pattern.source, flags);
};
var reg=new RegExp('/111/');
var newReg=reg.clone();
//newReg-> /\/111\//
17、JS 判断类型
判断方法:typeof(),instanceof,Object.prototype.toString.call()等
18、数组常用方法
push(),forEach(),pop(),shift(),unshift(),splice(),sort(),reverse(),map()等
19、数组去重
法一:indexOf 循环去重
法二:ES6 Set 去重;Array.from(new Set(array))
法三:Object 键值对去重;把数组的值存成Object 的key 值,比如Object[value1] = true,
在判断另一个值的时候,如果Object[value2]存在的话,就说明该值是重复的。
20、去除字符串首尾空格
使用正则(^\s*)|(\s*$)即可
21、性能优化
减少HTTP 请求
使用内容发布网络(CDN)
添加本地缓存
压缩资源文件
将CSS 样式表放在顶部,把javascript 放在底部(浏览器的运行机制决定)
避免使用CSS 表达式
减少DNS 查询
使用外部javascript 和CSS
避免重定向
图片lazyLoad
22、JS 基本数据类型
基本数据类型:undefined、null、number、boolean、string、symbol
23、跨域的原理
跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript 实施的安全限制,那么只要协议、域名、端口有任何一个不同,都被当作是不同的域。跨域原理,即是通过各种方式,避开浏览器的安全限制。
24、let const var 的区别,什么是块级作用域,如何用ES5 的方法实现块级作
用域(立即执行函数),ES6 呢
提起这三个最明显的区别是var 声明的变量是全局或者整个函数块的,而let,const 声明的变量是块级的变量,var 声明的变量存在变量提升,let,const 不存在,let 声明的变量允许重新赋值,const 不允许。