1. 请描述一下js里的this指向?
答:this指向是根据运行时的执行环境来决定的,谁调用this,this指向谁,如果没有找到直接调用者,则在非严格模式下,指向Window,反之,指向undefined。
2. 说一下箭头函数的this指向?
答:箭头函数本身是没有this的,从自己的作用域链的上一层继承this。
普通函数会在执行的过程中形成自己的执行上下文,其中包含三个重要属性:变量对象,作用域链,this
简单来说,箭头函数的this指向,通过定义箭头函数的地方向上寻找,找到第一个嵌套该箭头函数的普通函数,箭头函数继承该普通函数的this,如果一直找到全局环境,则箭头函数指向该全局对象
3. 说一下浏览器的缓存机制?
答:分为强缓存和协商缓存
- 首先通过Cache-Control验证强缓存是否可用,如果可用,则直接读取缓存
- 如果不可以,那么进入协商缓存,发起HTTP请求,服务器会通过请求头中是否带上Etag / If-None-Match这些请求条件字段检查资源是否更新
- 若资源更新,则返回资源和 200 状态码
- 若资源未更新,那么返回304并告诉浏览器使用缓存获取资源
4. var、let、const 区别?
答:var 存在变量提升(声明提升)、变量覆盖(会引发不可预知的错误)、没有块级作用域
let 必须先声明,有块级作用域,不存在变量覆盖
const 声明之后必须赋值、且定义的值不许更改,常用于声明对象类型,支持let其他属性,常量变量需要大写,常用于全局。
5. 如果通过ES6的相关知识将两个变量值互换,并且不会引用第三方变量?
let a = 3, b = 2
[a,b] = [b, a] // 运用解构的方式将这两个值互换掉
6. 如何运用ES6快速的去重?
let arr = [12, 43, 23, 43, 23, 55]
let item = [...new Set(arr)] // 通过解构 new 一个 Set 就可以完成去重的操作
7. Promise标杆面试题,打印顺序
答:构造函数是同步执行的,.then方法是异步执行
let promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(res => {
console.log(3)
})
console.log(4)
// 输出 1 、2、4、3
8. 深度剖析js闭包
1. 闭包是什么?
答:闭包是方法里面返回一个方法,使用场景:防抖节流、封装库(保证数据私有性,避免变量被污染)
function a() {
let a1 = 1
return function () {
return a1
}
}
2. 闭包存在的意义?
2.1 延长变量的生命周期
2.2 创造私有环境
2.3 避免变量被污染
let makeCounter = function() {
let num = 0
function changBy(val) {
num += val
}
// 给你什么,你才能拿
return {
add: function() {
changBy(1)
},
reduce: function() {
changBy(-1)
},
value: function() {
return num
}
}
}
let counter1 = makeCounter()
let counter2 = makeCounter()
counter1.add()
counter1.add()
counter2.add()
console.log(counter1.value()) // 2
console.log(counter2.value()) // 1
// 都有独立的词法作用域
// 面向对象编程 ---- 数据的隐藏和封装
3. 闭包的缺陷
3.1 闭包会常驻内存,要慎用闭包
9. js的防抖和节流(王者荣耀举例)
9.1 防抖是在一定时间内事件只发生一次,只维护一个定时器,上一个定时器存在,就先清除,重开定时器
应用场景:1.search联想输入 2.window窗口调整 3.防止重复提交 4.王者荣耀的回城
9.2 节流是在一定时间内只调用一次函数,将多个事件合为一个,判断定时器是否存在,不存在才会执行
应用场景:1. 鼠标不断点击触发 2. 高频监听滚动加载事件 3. 王者荣耀的平A
节流有两种实现方法:
- 时间戳 第一次立即执行,最后一次不会触发
- 定时器 第一次不会执行,会在延时结束执行,最后一次触发,因为延时执行的原因,可能还会执行一次
- 原型与原型链
原型:prototype是函数所特有的,普通的对象,数组上面没有原型
原型链:__proto__ => [[prototype]],查找过程:从当前实例属性去查找,如果找到就返回,否则顺着原型链一层一层往上查找,直到找到为止,反之,直到找到最后一层为null为止报错
如何判断这个属性是否是私有属性?
// 通过obj.hasOwnProperty(item))方法
for(let item in person) {
if(person.hasOwnProperty(item)){
console.log(item)
}
}
11. null 与 undefined
console.log(typeof undefined); // undefined 表示“无”的原始值 NaN
console.log(typeof null); // object 表示“无”对象 0
- 什么情况下会出现undefined
// 1. 已声明,未赋值
let o;
console.log(o); // undefined
// 2. 对象某个属性不存在
let obj = {};
console.log(obj.a);
// 3. 少参数
function fn(a, b) {
console.log(a, b);
}
fn(1);
// 4. 方法的默认返回值
function abc() {
console.log(11);
}
console.log(abc());
- 什么情况下会出现null
// 1. 手动释放内存
let obj = {}
obj = null // 告诉垃圾回收机制,这个变量已使用完
// 2. 作为函数的参数(此参数不是对象)
// 3. 原型链的顶端
12. forEach和map的区别?
forEach | map | |
返回值 | 无 | 有 |
break | 不能用break打断,报错 | 也不能 |
遍历 | 遍历的是 value 值 | 遍历 value 和 key |
13. js递归求和1-100
function add(num1, num2) {
let num = num1 + num2
if(num2 + 1 > 100) {
return num
} else {
return add(num, num2 + 1)
}
}
console.log(add(1, 2)) // 5050
14. 说一下bind、call、apply 的区别?
call、apply、bind 方法是每个函数都有的一个方法,允许在调用函数时为函数指定上下文,可以改变this指针指向。
call与apply的第一个参数都是函数执行上下文,也就是指针指向,区别就是 call的其他参数是通过 逗号, 一个个的传入,而apply 是通过数组将剩余参数放在第二个参数一起传入。
- fn.call(this, a, b, c, d) // call使用
- fn.apply(this, [a, b, c, d]). // apply使用
// 举例
let obj = {
a: 'summer'
}
function fn() {
return this.a + [...arguments].join('')
}
// let res = fn.apply(obj, ['小薛', '妹妹']) // summer小薛妹妹
let res = fn.call(obj,'小薛', '妹妹') // summer小薛妹妹
console.log(res)
bind与call、apply还不一样,call和apply立即执行,而bind绑定this以后会返回新的绑定函数,需要再次调用才会执行。
let bindFn = fn.bind(obj, '小薛', '妹妹') //
console.log(bindFn()) // summer小薛妹