前言
我找工作期间遇到的所有前端相关的重点问题,可以说是想找到一个还不错的工作的话,至少以下所有问题都需要会
1.手写深度比较,模拟lodash isEqual
关于深度比较的理解:如果obj1和obj2的属性、值全部相等,但是令他们 = = =的时候,还是返回false,因为他们的地址不同,深度比较就是能对两个地址不同,值相同的对象进行比较,返回true。
function isObject(obj){
return typeof obj === 'object' && obj !== null;
}
function isEqual(obj1,obj2){
if(!isObject(obj1)||!isObject(obj2)){
return obj1 === obj2
}
if(obj1 === obj2) return true;
const objkey1 = Object.keys(obj1);
const objkey2 = Object.keys(obj2);
if(objkey1.length !== objkey2.length) return false;
for(let key in obj1){
const re = isEqual(obj1[key],obj2[key]);
if(!re){
return false
}
}
return true;
}
2.数组的pop push unshift shift
pop:删除末尾元素,返回删除的元素
shift:删除首个元素,返回删除的元素
push:末尾添加元素,返回数组长度
unshift:首位添加元素,返回数组长度
纯函数:不改变源数组(没有副作用),返回一个数组。以下是一些纯函数:
some every
const arr = [10,20,30,40];
const arr1 = arr.concat([50,60,70]);//合并数组
const arr2 = arr.map(num => num*10);//对数组中的每个数执行一个新函数,然后返回新数组
const arr3 = arr.filter(num => num >25);//对数组中的每个数执行新函数,返回值为true的数组成新数组
const arr4 = arr.slice(2,4);//切割数组,返回切下来的部分
一些非纯函数:
pop push unshift shift forEach(如果数组的值全是基本数据类型的话就是纯函数,如果数组中还有引用类型的话,那就不是纯函数)
reverse
reduce
3.数组slice和splice的区别
const arr = [10,20,30,40,50];
const arr1 = arr.slice();//不传参类似于深拷贝
const arr2 = arr.slice(1,4);
const arr3 = arr.slice(2);//截取数组下标为2的一直到最后一个
const arr4 = arr.slice(-2); //截取最后两个
//splice 非纯函数
const spliceRes = arr.splice(1,2,'a','b','c');
const spliceRes2 = arr.splice(1,2)
console.log(spliceRes,arr);//[20,30] [10,'a','b','c',40,50]
console.log(spliceRes2);//返回剪掉的部分
4.[10,20,30].map(parseInt)的返回值
const res = [10,20,30].map(parseInt);
console.log(res);//[10,NaN,NaN]
// 等价于
[10,20,30].map((n, index) =>{
return parseInt(n, index)
})
parseInt的第二个参数如果是0,则以10为基数来解析,如果以0x开头则以16为基数,如果小于2或者大于36,返回NaN.
5.http的八种请求(ajax请求get和post的区别)
1、OPTIONS
返回服务器针对特定资源所支持的HTTP请求方法,也可以利用向web服务器发送‘*’的请求来测试服务器的功能性
2、HEAD
向服务器索与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以再不必传输整个响应内容的情况下,就可以获取包含在响应小消息头中的元信息。
3、GET
向特定的资源发出请求。注意:GET方法不应当被用于产生“副作用”的操作中,例如在Web Application中,其中一个原因是GET可能会被网络蜘蛛等随意访问。Loadrunner中对应get请求函数:web_link和web_url
4、POST
向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 Loadrunner中对应POST请求函数:web_submit_data,web_submit_form
5、PUT
向指定资源位置上传其最新内容
6、DELETE
请求服务器删除Request-URL所标识的资源
7、TRACE
回显服务器收到的请求,主要用于测试或诊断
8、CONNECT
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
注意:
1)方法名称是区分大小写的,当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Mothod Not Allowed);当服务器不认识或者不支持对应的请求方法时,应返回状态码501(Not Implemented)。
2)HTTP服务器至少应该实现GET和HEAD/POST方法,其他方法都是可选的,此外除上述方法,特定的HTTP服务器支持扩展自定义的
GET和POST的区别
1.get是从服务器上获取数据,post是向服务器传送数据。
2.get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTPpost机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。
3.对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。
4.get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。(这里有看到其他文章介绍get和post的传送数据大小跟各个浏览器、操作系统以及服务器的限制有关)
5.get安全性非常低,post安全性较高。
6.函数call和apply的区别
fn.call(this, p1, p2, p3)
fn.apply(this, arguments)
就是传参方式不同
7.new Object()和Object.create()的区别
{}等同于 new Object(),原型是Object.prototype
object.create(null)没有原型
object.create({…})可以指定原型,传入的参数即为新创建的空对象的原型
8.判断字符串以字母开头,后面字母数字下划线,长度6-30(一些简单的正则表达式)
//邮政编码
/\d{
6}/ //\d匹配数字
//小写英文字母
/^[a-z]+$/
//英文字母
/^[a-zA-Z]+$/
//日期格式
/^\d{
4}-\d{
1,2}-\d{
1,2}$/
//用户名(\w 在正则表达式中表示一个“字”(数字,字符,下划线)
/^[a-zA-Z]\w{
5,17}$/
//简单的IP地址匹配
/\d+\.\d+\.\d+\.\d+/
//匹配电话号码
/^1[3456789]\d{
9}$/
//匹配科学计数法
str.replace(/\B(?=((\d{3})+)$)/g,',')
// aaaa-aaa-bbb转为aaaaAaaBbb
var str2 = str.replace(/-\b\w/g,function(th){
th = th.slice(1);
return th.toUpperCase();
})
9.手写字符串trim保证浏览器兼容性
trim就是用于删除字符串两端的空白
String.prototype.trim = function(){
return this.replace(/^\s+/,'').replace(/\s+$/,'');
}
10.如何捕获JS中的异常
//手动捕获
try{
//todo
} catch(ex){
console.error(ex) //手动捕获 catch
}finally {
// todo
}
//自动捕获
window.onerror = function(message, source, lineNum, colNum, error)
//缺点:第一,对跨域的JS,如CDN,不会有详细的报错信息
// 第二,对于压缩的js,还要配合 sourceMap 反查到未压缩的代码的行、列
11.获取当前页面url参数
1.传统方式,查找location.search
2.新API,URLSearchParams
//传统方式
function query(name){
const search = location.search.substr(1); //类似 array.slice,用于去掉问号
const reg = new RegExp('(^|&)${name}=([^&]*)(&|$)','i')
const res = search.match(reg);
if(res == null){
return null
}
return res[2]
}
query('a')
//URLSearchParams
function query(name){
const search = location.search
const p = new URLSearchParams(search);
return p.get(name);
}
console.log(query('a'))
12.手写flatern考虑多层级
function flat(arr){
const isDeep = arr.some(item => item instanceof Array)
if(!isDeep){
return arr
}
const res = Array.prototype.concat.apply([],arr);
//arr作为apply方法的第二个参数,本身是一个数组,数组中的每一个元素(还是数组,即二维数组的第二维)会被作为参数依次传入到concat中,效果等同于[].concat(1, 2, 3, [4, 5, 6], 7, 8, [9, 10])。利用apply方法,我们将单重循环优化为了一行代码
return flat(res)
}
13.数组去重
//传统方式
function unique(arr){
const res = [];
arr.forEach(item => {
if(res.indexOf(item)<0){
res.push(item)
}
});
return res
}
//使用set的方式(set无序,不能重复)
function unique(){
const set = new Set(arr);
return [...set];
}
14.介绍RAF requetAnimationFrame
要想动画流畅。更新频率要60帧/s,即16.67ms更新一次视图
setTimeout 要手动控制频率,而RAF浏览器会自动控制
后台标签或隐藏iframe中,RAF会暂停
,而setTimeout依然执行
//3s把宽度从 100px 变为 640px, 即增加540px
//60帧/s, 3s 180帧 一帧变化3px
//setTimeout
let curWidth = 100,
maxWidth = 640,
div1 = document.getElementById('div1');
function animate(){
curWidth = curWidth + 3;
div1.style.width = curWidth + 'px';
if(curWidth<maxWidth){
setTimeout(animate,16.7) //时间需要自己控制
}
}
animate()
//RAF
function animate(){
curWidth = curWidth + 3;
div1.style.width = curWidth + 'px';
if(curWidth<maxWidth){
window.requestAnimationFrame(animate);//时间不用自己控制
}
}
animate()
15.CSS实现动画
1.transition实现
transition的速度属性:linear(匀速)、ease(从慢到快再变慢)、ease-in(慢开始)、ease-out(慢结束)