面试宝典
什么是闭包
闭包是指有权访问另外一个函数作用域中的变量的函数
有哪些坑点:
- 引用的变量可能会发生改变
- this的指向问题
- 内存泄漏问题
function f1(){
var a = 1
function f2(){
console.log(a)
}
return f2
}
使用场景:
- setTimeout
function f1(a){
function f2(){
console.log(a)
}
return f2
}
let fun = f1(1)
setTimeout(fun,1000)
- 回调
function changeSize(size){
return function(){
document.body.style.fontSize = size + 'px';
};
}
var size12 = changeSize(12);
var size14 = changeSize(20);
var size16 = changeSize(30);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-20').onclick = size14;
document.getElementById('size-30').onclick = size16;
- 防抖
- 封装私有变量
什么是原型,原型链
prototype
每个函数都有一个 prototype 属性
proto
每一个 javaScript 对象(null除外)都具有的一个属性叫 proto ,这个属性会指向该对象的原型
function person(){}
var nPerson = new Person()
console.log(nPerson.__proto__ === person.prototype); // true
constructor
每一个原型都有一个 constructor 属性指向关联的构造函数,实例原型指向构造函数
function person(){}
console.log(person === person.prototype.constructor); // true
因为每个对象和原型都有原型,对象的原型指向原型对象,而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。
防抖和节流
防抖(debounce):在第一次触发事件时,不立即执行函数,而是给出一个时间段,如果短时间内大量触发同一事件,只会执行一次函数。
debounce(fn, delay = 500){ // 默认500毫秒
let timer = null
return function(){
var _this = this
let args = arguments
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(_this, args) // this指向vue
}, delay)
}
}
节流(throttle):函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活,如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再生效,直至过了这段时间才重新生效。
throttle(fn, delay = 500){
let timer = null
return function(){
var _this = this
let args = arguments
if(!timer){
timer = setTimeout(()=>{
fn.apply(_this, args)
timer = null
},delay)
}
}
}
Promise
Promise是异步编程的解决方案
Promise有三种状态:pending(等待中),fulfiled(成功),rejected(失败)
Promise解决回调地狱,第一个函数的输出是第二个函数的输入的现象
Promise支持多个并发请求,获取并发请求中的数据
.then方法
p.then((res) => {
console.log(res)
}).then((res) => {
console.log(res)
}).then((res) => {
console.log(res)
})
- resolve 成功回调
- reject 失败回调
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
var num = Math.ceil(Math.random()*10); //生成1-10的随机数
if(num<=5){
resolve(num, "执行成功")
} else {
reject("执行失败")
}
},1000)
})
p.then((res)=>{
console.log("resolved", res)
},(err)=>{
console.log("rejected", err)
}
)
.catch方法
效果跟写在.then里的第二个参数里面一样
p.then((res) => {
console.log("resolved", res)
})
.catch((err) => {
console.log("rejected", err)
})
.all
接收一个数组作为参数,要等所有的执行成功才有回调
let p1 = new Promise(function(resolve, reject){})
let p2 = new Promise(function(resolve, reject){})
let p3 = new Promise(function(resolve, reject){})
let p = Promise.all([Promise1, Promise2, Promise3])
p.then((res) => {
// 三个都成功则成功
}, (err) => {
// 只要有失败,则失败
})
.race
有一个执行成功就有回调
let p1 = new Promise(function(resolve, reject){})
let p2 = new Promise(function(resolve, reject){})
let p3 = new Promise(function(resolve, reject){})
let p = Promise.race([Promise1, Promise2, Promise3])
p.then((res) => {
// 有一个成功则成功
}, (err) => {
console.log(err)
})
判断数组的方法
- typeof做判断
let a = [1,2,3];
let b = {0:'a',1:'b',2:'c'};
let c = null;
console.log(typeof(a)); // Object
console.log(typeof(b)); // Object
console.log(typeof(c)); // Object
但是无法区分对象、数组、null
- instanceof判断
console.log(a instanceof Array); // true
console.log(a instanceof Object); // true
console.log(b instanceof Array); // false
- constructor
console.log(a.constructor); // function Array()
console.log(b.constructor); // function Object()
console.log(c.constructor); // 报错
- 用Object的toString方法判断
Object.prototype.toString.call(a); // [Object, Array]
Object.prototype.toString.call(b); // [Object, Object]
Object.prototype.toString.apply(a); // [Object, Array]
Object.prototype.toString.apply(b); // [Object, Object]
- isArray
Array.isArray(a); // true
Array.isArray(b); // false
JS如何区分Array和Object
- Array.isArray
Array.isArray([]) //true
Array.isArray({}) //false
- instanceof
[] instanceof Array //true
{} instanceof Array //false
- constructor
{}.constructor //返回object
[].constructor //返回Array
- Object.prototype.toString.call
Object.prototype.toString.call([]) //["object Array"]
Object.prototype.toString.call({}) //["object Object"]
- isPrototypeOf()
Array.prototype.isPrototypeOf(arr) //true表示是数组,false不是数组
宏任务微任务
宏任务:包括整体代码script,setTimeout,setInterval,setImmediate。
微任务:原生Promise(有些实现的promise将then方法放到了宏任务中)、process.nextTick、MutationObserver
__ 微任务和宏任务属于一个队列,主要区别在于它们的执行顺序(宏任务执行完如果有可执行的微任务则执行完微任务才会继续执行下一个宏任务)__
BFC
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
简述MVVM
MVVM是Model-View-ViewModel的缩写,相当于把MVC的Ctroall演变成了ViewModel。Model是数据层,View是视图层,ViewModel是Model和View之间的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据
vue生命周期
- beforeCreate:在数据观测和初始化事件还未开始,VUE实例的挂载元素$el和数据对象都为undefined
可以进行的事件:加loading事件 - created:完成数据观测,属性和方法的运算,初始化事件,$el属性还没有显示出来
可以进行的事件:结束loading,请求数据为mounted作准备 - beforeMount:在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。($el和data都已经初始化,但仍是虚拟DOM节点)
- mounted:此时可以操作dom,发起请求
- beforeUpdate:在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
- updated:在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
- beforedDestory:在实例销毁之前调用。实例仍然完全可用。
- destroyed:在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
computed和watch
- computed:计算属性,有缓存,只有依赖数据发生改变,才会重新进行计算
- computed:不支持异步
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
- watch:监听属性,不支持缓存,数据有变化就会触发
- watch:支持异步
- watch:两个参数,一个是新值,一个是旧值
- watch:一对多
v-for中key的作用
key的作用主要是为了高效的更新虚拟DOM,提高性能
vue组件的通信方式
双向绑定的原理
vue是通过Object.defineProperty()来实现数据劫持,get和set进行重写,通知订阅者,订阅者会触发它的update方法,对视图进行更新。
nextTick
应用场景:需要在视图更新之后,基于新的视图进行操作。
- 在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中
- 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。
vue3的新特性
JS中map和filter的区别
- map() 会根据提供的函数对指定序列做映射,不会改变原数组
- filter() 用于过滤序列,过滤掉不符合条件的元素,返回由符合条件的元素组成的新列表,不会改变原数组
HTTP状态码
- 1xx:接受请求,正在处理
- 2xx:请求成功
- 3xx:重定向
- 4xx:客户端错误
- 5xx:服务器错误
什么是跨域
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。
如何处理:JSONP、CORS
持续更新~