前端JS面试重点
文章目录
- 前端JS面试重点
- 一、js的数据类型
- 二、什么是闭包
- 三、什么情况下会产生内存泄漏,如何解决内存泄漏问题
- 四、 == 和 === 的区别
- 五、js的遍历方法有哪些
- 六、map和forEach的区别
- 七、同源策略
- 八、如何解决跨域
- 九、es6的新增特性
- 十、let const 和var 的区别
- 十一、箭头函数与普通函数的区别
- 十二、什么是async和await/他们有哪些作用
- 十三、常用的数组有哪些方法
- 十四、this指向问题
- 十五、JavaScript 深浅拷贝
- 十六、防抖节流
- 十七、js的垃圾回收机制
- 十九、什么是原型 原型链 和继承
- 二十、什么是作用域和作用域链
- 二十一、js的运行机制
- 二十二、说一下宏任务和微任务?
- 二十三、什么是promise/promise有哪些方法
- 二十四、http和https的区别
- 二十五、null和 undefined的区别
- 二十六、事件代理和事件冒泡
- 二十七、图片懒加载和预加载
- 二十八、get/post区别
- 二十九、后台如何鉴别权限
- 三十、什么是回调地狱/如何解决
- 三十一、js中的继承有哪些
- 总结
一、js的数据类型
1.基础数据类型
Number Boolean String Undefined Null
2.引用数据类型
Object Date Array Function
- 强制转换数据类型的方法
1.toString()
2.Number()
3.Boolean() - 基础数据类型和引用数据类型有什么不同?
1.存放的位置不同
基础数据类型存放在栈区,引用数据类型存放在堆区,然而在栈内存中保存了堆内存中引用类型的地址
二、什么是闭包
1.闭包就是可以读取其他函数内部变量的函数
2.闭包就是一个函数内部嵌套一个函数
- 优点:
1.可以读取其他函数内部的变量
2.避免了全局污染
- 缺点:
1.变量不会被回收
2.会造成内存泄漏问题
- 闭包为什么会产生内存泄漏
原因:因为闭包可以读取函数内部变量,然后这些变量会一直存放在内存中。使用结束没有局部清理,就会产生内存泄漏
三、什么情况下会产生内存泄漏,如何解决内存泄漏问题
- 产生内存泄漏
1.未声明的变量
2.闭包引起的内存泄漏
3.遗忘掉的定时器或回调
4.没有清理的DOM元素
5.全局变量造成泄漏 - 解决方法
1.避免创建全局变量
2.闭包使用结束后,解除闭包
3.手动清理定时器和DOM
4.手动删除DOM(elements.btn = null)
5.关闭页面的时候手动清理
四、 == 和 === 的区别
1.== 是非严格意义上的相等,比较值相等就相等
2.=== 是严格意义上的相等,比较值和数据类型全部相等才相等
五、js的遍历方法有哪些
链接:link
1.for
2.forEach
3.for in
4.for of
5.map
6.some
7.every
8.filter
六、map和forEach的区别
- forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。
- map()方法会得到一个新的数组并返回。
七、同源策略
同源指的是域名 协议 端口号相同
八、如何解决跨域
1.JSONP
2.CORS
3.PROXY代理
九、es6的新增特性
链接:link
1.新增了let 和 const 的声明变量
2.箭头函数
3.模板字符串
4.函数参数的默认值
5.扩展运算符 Spread /Rest
6.解构赋值
7.for…of和fo…in
8.class类
9.提出promise解决回调地狱
十、let const 和var 的区别
链接:link
1.var可以变量提升;let和const不能变量提升
2.var属于全局作用域;let和const是块级作用域
3.var可以多次声明;let和const在统一作用域不可以多次声明
4.var和let可以修改生命的变量;const只能通过方法进行修改,直接修改会报错
十一、箭头函数与普通函数的区别
链接:link
1.箭头函数带箭头
2.箭头函数属于匿名函数
3.箭头函数不能用于构造函数,不能使用new
4.箭头函数this指向父级,普通函数this指向全局或者调用它的对象
5.call() bind() apply()无法改变箭头函数的this指向
十二、什么是async和await/他们有哪些作用
链接:link
- async和await属于promise的语法糖,asnyc作为一个关键字放在函数前面,表示该函数是一个异步函数,意味着该函数执行时不会堵塞后边代码。它返回的是一个promise对象。
- await用于接收promise对象,他只能在异步函数中使用,否则会报错。
十三、常用的数组有哪些方法
1.isArrary()判断参数是不是数组;返回布尔值
2.toString()将数组转换成字符串
3.push()在数组末尾添加元素
4.unshift()在数组开头添加元素
5.pop()删除数组末尾的值,并返回删除的值
6.shift()删除数组开头的值,并返回删除的值
7.reverse()翻转数组
8.join(‘符号’)拼接字符并且以‘符号’隔开
9.sort()排序
10.concat 拼接数组
11.slice()
- slice(开始索引值,结束索引值) 数组截取(下标)包括左边不包括右边
- 如果第二个数是正数, 返回从开始位置到结束的位置(但不包括最后结束的位置)
- 如果第二个数是负数,返回的是开始位置到结束位置减一
- 如果第二个数是0或者两个数都是负数,则返回[]
var nums = [1,2,3,4,5,6,7,8,9]
console.log(nums.slice(0,4))//1,2,3,4
console.log(nums.slice(2))//3,4,5,6,7,8,9
console.log(nums.slice(2,nums.indexOf(6)))//3,4,5
console.log(nums.slice(2,0))//[]
console.log(nums.slice(2,-1))//3,4,5,6,7,8
console.log(nums.slice(2,-2))//3,4,5,6,7
12.indexOf和lastIndexOf
- 数组名.indexOf():某个元素从左到右第一次出现的下标
- 数组名.lastIndexOf:某个元素从左到右最后一次出现的下标
var nums = [1,5,1,16,56,6,5,66]
var index = nums.indexOf(5)
console.log(index)//1
index = nums.lastIndexOf(5)
console.log(index)//6
13.filter()过滤
过滤后形成新的数组
不会影响原数组
- 筛选单个值
var money = [1500, 1200, 2000, 2100, 1800]
var arrs = money.filter(function(v) {
//输出数组里小于2000的元素
return v <= 2000
})
console.log(arrs.toString())//1500,1200,1800
- 筛选对象
//筛选出工资大于4000的员工信息
var people = [
{name:'赵俊',age:18,salary:8000},
{name:'张三',age:18,salary:4000},
{name:'李四',age:18,salary:6000},
{name:'王麻子',age:18,salary:3000},
{name:'刘辰',age:18,salary:2200}
]
var arrs = people.filter(function(v){
if(v.age >= 18)
return v.salary >= 4000
})
console.log(arrs)//Array(3)
14.find()和findIndex()
- 数组名.find():查找数组中满足条件的第一个元素,返回该元素,没找到输出undefinded
- 数组名.findIndex():查找数组中满足条件的第一个元素,返回该元素下标,没找到输出-1
//find------找到是否存在值,找到输出查询的元素,没找到输出undefinded
var nums = [15,56,44,2,36]
var a = nums.find(function(v){
return v == 15
})
console.log(a)//0
//findIndex------找到是否存在值的下标,找到输出元素的下标,没找到输出-1
a = nums.findIndex(function(v){
return v == 15
})
console.log('下标是:'+a)//下标是:0
15.map()
返回一个新的数组
var salary = [1500,1200,2000,2100,1800]
//给每个员工涨工资1000
var arrs = salary.map(function(v){
return v*2
})
console.log(salary.toString())//1500 1200 2100 1800
console.log(arrs.toString()) //3000 2400 4000 3600
16.every和some
- 数组名.every()—全部满足条件 全部都满足则返回true,否则返回false 相当于&&
- 数组名.some()—只要有一个满足就返回true 全部都不满足则返回false 相当于||
var people = [ {name: '赵俊',age: 18,year: 1,salary: 8000},
{name: '张三',age: 18,year: 2,salary: 4000},
{name: '李四',age: 18,year: 3,salary: 6000},
{name: '王麻子',age: 18,year: 4,salary: 3000},
{name: '刘辰',age: 18,year: 6,salary: 2200}
]
//every()---全部都 全部都满足则返回true,否则返回false
var a = people.every(function(v){
return v.salary >2000
})
if(a){
console.log('公司所有人工资都大于2000')//公司所有人工资都大于2000
}else{
console.log('公司有人工资小于2000')
}
//some()---只要有一个满足就返回true 全部都不满足则返回false
a = people.some(function(v){
return v.age <18
})
if(a){
console.log('公司有人未满十八')
}else{
console.log('公司所有人都是十八岁及以上')//公司所有人都是十八岁及以上
}
17.reduce()
- 求和
var nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var sum = nums.reduce(function(tatol, v) {
return tatol + v
},0)
console.log(sum) //45
- 二维数组转一维数组
nums[
[1,2,3],
[4,5,6],
[7,8,9]
]
var arrs = nums.reduce(function(nums2,i){
return nums2.concat(i)
},[])
console.log(arrs.toString())// [1,2,3,4,5,6,7,8,9]
- 统计一个数组中有多少个不重复的单词
var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){
return arr.reduce(function(prev,next){
prev[next] = (prev[next] + 1) || 1;
return prev;
},{});
}
18.splice(start,number,value…): 返回删除元素组成的数组,value 为插入项,改变原数组
十四、this指向问题
1.全局作用域中
this指向window
2.普通函数
谁调用我this就指向谁
var obj = {
fn1:function() {
console.log(this);
},
fn2:function(){
fn3()
}
}
function fn3() {
console.log(this);
}
fn3();//this->window
obj.fn1();//this->obj
obj.fn2();//this->window
3.箭头函数
this是上下文中定义的this
var div = document.querySelector('div');
var o={
a:function(){
var arr=[1];
//就是定义所在对象中的this
//这里的this—>o
arr.forEach(item=>{
//所以this -> o
console.log(this);
})
},
//这里的this指向window o是定义在window中的对象
b:()=>{
console.log(this);
},
c:function() {
console.log(this);
}
}
div.addEventListener('click',item=>{
console.log(this);//this->window 这里的this就是定义上文window环境中的this
});
o.a(); //this->o
o.b();//this->window
o.c();//this->o 普通函数谁调用就指向谁
4.事件绑定中的this
-
事件源.onclik = function(){ } //this -> 事件源
-
事件源.addEventListener(function(){ }) //this->事件源
var div = document.querySelector('div');
div.addEventListener('click',function() {
console.log(this); //this->div
});
div.onclick = function() {
console.log(this) //this->div
}
5.定时器中的this
- 定时器中的this->window,因为定时器中采用回调函数作为处理函数,而回调函数的this->window
6.构造函数中的this - 构造函数配合new使用, 而new关键字会将构造函数中的this指向实例化对象,所以构造函数中的this->实例化对象
十五、JavaScript 深浅拷贝
- 浅拷贝
直接赋值
Object.assign - 深拷贝
1.可以通过 JSON.parse(JSON.stringify(object)) 来解决
2.第三方库lodash通过 _.cloneDeep(a)来解决 - JSON.parse(JSON.stringify(object))的缺点
1.这种方法不能用在函数、undefined、循环引用中 - 深拷贝和浅拷贝的区别
1.深拷贝和浅拷贝只支持引用类型
2.浅拷贝只复制对象的指针而不是本身,新旧对象会共享一个内存,新旧对象会相互影响
3.深拷贝会创造一个一摸一样的对象,新旧对象不会共享内存,不会相互影响
十六、防抖节流
链接:link
-
什么是防抖节流
1.防抖: 在 n 秒内被重复触发,则重新计时,在结束后生效
2.节流: 在 n 秒内重复触发,只有一次生效 -
相同点
1.都可以使用setTimeout来实现
2.降低回调执行频率,节省计算资源 -
不同点
1.防抖:在连续操作结束后,执行一次;节流:一段时间内执行一次 -
应用场景
防抖:
1.搜索框搜索输入。只需用户最后一次输入完,再发送请求
2.手机号、邮箱验证输入检测
节流:
1.滚动加载,加载更多或滚到底部监听
2.搜索框,搜索联想功能
十七、js的垃圾回收机制
- 什么是垃圾回收机制
垃圾回收机制是为了防止内存泄漏,内存泄漏是指当已经不需要某块内存时,该内存还存在;垃圾回收机制就是不定时地寻找不再使用的变量并释放内存。 - 垃圾回收方法
- 1.标记清除
当在函数声明变量的时候,垃圾回收器将其标记为“进入环境”,当函数执行结束的时候,垃圾回收器将其标记为“离开环境”;
垃圾回收器会将所有变量标记,将不需要的变量进行回收。 - 2.引用计数
跟踪一个值的引用次数,当声明一个变量并将一个引用类型赋值给这个变量的时候引用计数+1;
十九、什么是原型 原型链 和继承
链接:link
- 原型:
每一个构造函数都会有一个prototype属性,这个属性会在生成实例的时候,成为实例对象的原型对象,js的每一个对象都继承另一个对象,而另一个对象就是原型。除了null之外所有对象都有原型对象。 - 原型链:
每个对象都可以有一个原型,这个原型还可以有它自己的原型,以此类推,形成一个原型链 - 继承
继承是指一个对象直接使用另外一个对象的属性和方法
二十、什么是作用域和作用域链
- 作用域:
作用域指的是一个变量和函数的作用范围。
作用域分为:可分为全局作用域和局部作用域 - 作用域链:
当查找变量的时候,会先从当前作用域的变量对象中查找,如果没有找到,就会从父级作用域(上层环境)的变量对象中查找,一直找到全局作用域的变量对象,也就是全局对象。这样由多个作用域的变量对象构成的链表就叫做作用域链。
var a = 20;
function test() {
var b = a + 10;//当前作用域没有a,往上找,找到a = 20;
function innerTest() {
var c = 10;
return b + c;//当前作用域没有b,往上找,找到b = a+10;
}
console.log(d);//Uncaught ReferenceError: d is not defined,一直往上找,最终也没找到d,所以报错
return innerTest();
}
test();//40
二十一、js的运行机制
- js是一个单线程的执行机制;所有任务可以分为“同步任务”和“异步任务”;
- 同步任务指的是在主线程上排队执行的任务,只有上一个任务自行完毕才能进入下一个任务;
- 异步任务指的是,不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,异步任务可以执行了,才能进入主线程进行排队。
二十二、说一下宏任务和微任务?
链接:link
宏任务:定时器和用户交互事件
微任务:promise
在js中分为同步任务和异步任务,异步任务分为宏任务和微任务。在代码执行时,先执行同步任务,后执行异步任务。
在异步任务执行时,先执行微任务后执行宏任务。
二十三、什么是promise/promise有哪些方法
链接:link
-
什么是promise
1.promise可以解决异步回调地狱
2.它有两种参数resolve和reject;异步调用成功时用resolve,反之用reject
3.有三种状态,pending(进行中)、fulfilled(已成功)、rejected(已失败);且状态不可逆 -
promise的方法
1.promise.all
只有所有promise成功了,才会执行resolve,并返回成功的值,状态为成功
只有一个promise失败了,会执行reject,返回失败的值,状态为失败
有多个promise失败了,会执行reject,返回第一个失败的值
2.promise.race
谁第一个完成就返回谁的结果,如果第一个是失败这个 Promise 就失败,如果第一个是成功就是成功; -
promise.all
-
promise.race
-
promise.any
-
promise.allSettled
二十四、http和https的区别
链接:link
- HTTP
HTTP是超文本传输协议,是客户端和服务器之间的通信的响应协议,它用于客户端和服务端之间的请求 - HTTPS
由于HTTP协议传输数据都是未加密的,对于隐私信息非常不安全,HTTPS通过HTTP+SSL协议对传输的数据进行加密,增加安全性 - 区别
1.HTTPS需要申请证书,一般需要费用
2.HTTPS是通过SSL协议进行加密处理的,安全性高;HTTP协议是超文本传输协议,是明文的,安全性不高
3.HTTPS和HTTP使用不同的链接方式,端口也不同,HTTP是80;HTTPS是443
二十五、null和 undefined的区别
- undefined:
1.表示一个变量没有被声明,或者声明了没有被覆值
2.类型是undefined
3.默认转成NAN - null:
1.表示什么都没有
2.类型为Object
3.默认转成0
二十六、事件代理和事件冒泡
- 事件代理
事件代理是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。 - 事件冒泡
在一个层层嵌套的HTML元素中,最里面的元素触发了某个事件,之后会从里到外相继触发这个事件,这叫做事件冒泡。 - 如何阻止事件冒泡
1.阻止事件冒泡可以在调用函数的时候传入event对象,然后在函数里调用event对象的stopPropagation方法。
2.如果是Vue的话,直接在绑定事件的时候事件名后面加上个.stop就可以阻止事件冒泡了。(.stop事件修饰符)
二十七、图片懒加载和预加载
链接:link
- 图片懒加载
对于图片过多的页面,为了加快页面加载速度,需要将页面未出现的区域图片不进行加载,但它出现在可视区域内时,在进行加载;减轻了服务器的压力 - 实现原理
懒加载的原理首先将页面上的图片的 src 属性设为空字符串,而图片的真实路径则设置在data属性中,当页面滚动的时候需要去监听scroll事件,在scroll事件的回调中,判断我们的懒加载的图片是否进入可视区域,如果图片在可视区内将图片的 src 属性设置为data的值,这样就可以实现延迟加载。 - 图片预加载
提前加载所有图片,当用户需要查看图片时直接从缓存中获取;加强了服务器的压力
二十八、get/post区别
1.get用来获取数据;post用来提交数据
2.get参数有长度限制
3.get和post在本质上都是tcp链接,最大的区别在于get请求时发送一个数据包,而post会产生两个数据包,会先发送header,服务器返回100后在发送data,服务器返回200
4.get传送数据量较小,不能大于2KB;post传送量较大
5.get安全性低;post安全性高
6.get服务器端用Request.QueryString获取变量值;post用Request.From获取提交的数据
二十九、后台如何鉴别权限
- 当用户填完账号和密码进行验证的时候,服务端会返回一个token值,将token来存放在cookie中,通过router.beforEach(路由拦截)来判断是否登陆成功,根据token值拉取user_info中的用户信息,通过用户信息来算出该用户的权限,通过权限用Router.addRoutes来挂载路由。
三十、什么是回调地狱/如何解决
- 回调函数嵌套回调函数就叫做回调地狱
- 通过es6新增属性Promise解决回调地狱问题
三十一、js中的继承有哪些
链接:link
- 原型链继承
- es6继承
- 构造函数继承
- 实例继承
- 组合式继承
总结
祝我早日找到管饭的公司