面试真题

var和let const的区别
  • var是ES5语法,let const是ES6语法;var有提高变量
  • var和let是变量可修改;const是常量,不可修改
  • let const有块级做用域,var没有
    • *
typeof返回哪些类型
  • undefined
  • string
  • number
  • boolean
  • symbol
  • object(typeof null === 'object')
  • function
    • *
列举强制类型转换和隐式类型转换

强制:parseInt parseFloat toString
隐式:if、逻辑运算、==、+拼接字符串css


手写深度比较isEqualnode

//判断是不是对象或者是数组
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
    }
    //两个都是数组或者对象
    //一、先取出obj1和obj2的keys,比较个数
    const obj1Keys = Object.keys(obj1)
    const obj2Keys = Object.keys(obj2)
    if(obj1Keys.length !== obj2Keys.length){
        return false
    }
    //二、以obj1为基准,和obj2递归依次比较
    for(let key in obj1){
        //比较当前key的value值
        const res = isEqual(obj1[key],obj2[key])
        if(!res){
            return false
        }
    }
    return true
}
const obj1 = {
    a:100,
    b:{
        x:100,
        y:200
    }
}
const obj2 = {
    a:100,
    b:{
        x:100,
        y:200
    }
}

split()和join()的区别ajax

'1-2-3'.split('-')  //[1,2,3]
[1,2,3].join('-')   //'1-2-3'

数组的pop push unshift shift分别是什么
  • pop 在末尾删除一个值,返回的是数组最后的一个值
const arr = [10,20,30,40]
const popRes = arr.pop()
console.log(popRes) //40
console.log(arr)  //[10,20,30]
  • push 在数组末尾插入一个数值,返回的是数组的长度
const arr = [10,20,30,40]
const pushRes = arr.push(50)
console.log(pushRes) // 5
console.log(arr)  //[10,20,30,40,50]
  • unshift 在数组第一位插入一个值,返回的是数组的长度
const arr = [10,20,30,40]
const unshiftRes = arr.unshift(5)
console.log(unshiftRes) // 5
console.log(arr)  //[5,10,20,30,40]
  • shift 删除数组第一个值,返回的是数组的被删除的值
const arr = [10,20,30,40]
const shiftRes = arr.shift(5)
console.log(shiftRes) // 10
console.log(arr)  //[20,30,40]

image.png


纯函数
一、不改变原数组(没有反作用)
二、返回的是一个数组
concat
const arr = [10,20,30,40]
const arr1 = arr.concat([50,60,70])
console.log(arr)  //[10,20,30,40]
console.log(arr1) //[10,20,30,40,50,60,70]
map
const arr = [10,20,30,40]
const arr2 = arr.map(num=>num*10)
console.log(arr)  //[10,20,30,40]
console.log(arr2) //[100,200,300,400]
filter
const arr = [10,20,30,40]
const arr3 = arr.filter(num=>num>25)
console.log(arr)  //[10,20,30,40]
console.log(arr3) //[30,40]
slice
const arr = [10,20,30,40]
const arr4 = arr.slice()
console.log(arr)  //[10,20,30,40]
console.log(arr4) //[10,20,30,40]

slice和splice的区别
slice纯函数
const arr = [10,20,30,40,50]
const arr1 = arr.slice(1,4) //第一个参数为从第几个位置开始,第二个参数为到第几个位置前结束
const arr2 = arr.slice(2)
const arr3 = arr.slice(-2) //截取最后两位
console.log(arr1) //[20,30,40]
console.log(arr2) //[30,40,50]
console.log(arr3) //[40,50]
splice非纯函数
const arr = [10,20,30,40,50]
const spliceRes = arr.splice(1,2,'a','b','c')//参数含义:把第一个参数到第二个参数位置剪切下来,把后面的参数放在剪切区域内
console.log(arr)  //[10,'a','b','c',40,50]
console.log(spliceRes) //[20,30]

[10,20,30].map(parseInt)
const res = [10,20,30].map(parseInt)
console.log(res) //[10,NaN,NaN]
//拆解
[10,20,30].map((num,index) = >{
    return parseInt(num,index)
})

ajax请求get和post的区别
  • get在浏览器回退是无害的,而post则会再次提交请求
  • get产生的url地址能够被收藏,而post不能够
  • get请求会被浏览器主动缓存,而post不会,除非手动设置
  • get请求只能进行url编码,而post支持多种编码方式
  • get请求参数会被完整的保存在浏览器历史记录里,而post参数不会被保留
  • get请求在url中传送参数是有长度限制的,而post没有限制
  • 对参数的数据类型,get只接受ASCLL字符,而post没有限制
  • get比post更不安全,由于参数直接暴漏在url上,不能传递敏感信息
  • get参数经过url传递,post放在request body中
    • *
JavaScript中 call和apply
apply()方法 接收两个参数,一个是函数运行的做用域(this),另外一个是参数数组。
call()方法 第一个参数和apply()方法的同样,可是传递给函数的参数必须列举出来。

阻止事件冒泡和默认行为

event.stopPropagation()
event.preventDefault()正则表达式


查找、添加、移动、删除Dom节点
const div1 = document.getElementById("div1")
//添加新节点
const newP = document.createElement('p')
newP.innerHTML = "this is p1"
div1.appendChild(newP)//添加新建立的元素
//移动已有节点,注意是移动
const p2 = document.getElementById('p2')
div1.appendChild(p2)
//获取子元素列表
const div1 = document.getElementById("div1")
const div1ChildNodes = div1.childNodes
console.log(div1.childNodes)//打印出两种标签,一种是p标签,一种是text标签,由于p标签里面含有文本,text的nodeType为3,p的nodeType为1,因此经过转化为数组过滤。
const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child=>{
    if(child.nodeType == 1){
        return true
    }
    return false
})
//删除子节点
div1.removeChild(div1ChildNodesP[0])
//获取父元素
const div1 = document.getElementById("div1")
const parent = div1.parentNode

如何减小DOM操做
  • 缓存DOM查询结果
  • 屡次DOM操做,合并到一次插入
    • *
函数声明和函数表达式的区别

函数声明function fn(){}
函数表达式const fn = function(){}json


new Object()和Object.create()的区别

{}等同于new Object(),原型链Object.prototype
Object.create(null)没有原型
Object.create({})能够指定原型数组


常见的正则表达式
字符串字母开头,后面字母数字下划线,长度6-30
const res = /^[a-zA-Z]\w{5,29}$/
邮政编码
const res = /\d{6}/
去除两端空格
String.prototype.trim = function(){
    return this.replace(/^\s+/,'').replace(/\s+$/,'')
}

获取多个数字中的最大值
//第一种获取最大值
Math.max(10,20,30,40) //40
//第二种获取最大值
function max(){
    const nums = Array.prototype.slice.call(arguments)//变为数组
  let max = nums.length ? nums[0] : null
  nums.forEach(n=>{
    if(n>max) {
        max = n
    }
  })
  return max
}
//获取最小值
Math.min(10,20,30,40) //10

捕获js异常
//第一种手动捕获异常
try{

}.catch(ex){
    console.error(ex) //手动捕获异常
}.finally{

}

//第二种自动捕获异常
window.onerror = function(message,source,lineNum,colNum,error){
    console.log(message,source,lineNum,colNum,error)
}

json
json是一种数据格式,本质是一串字符串
json格式和js对象结构一致,对js语言更友好
window.json是一个全局对象:JSON.stringify JSON.parse

获取当前页面url参数

location.search
URLSearchParams浏览器

//传统方式
functoin query1(name){
    const search = location.search.substr(1)//去掉前面的‘?’
    const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`,'i')
    const res = search.match(reg)
    if(res == null){
        return null
    }
    return res[2]
}
//URLSearchParams
funcrion query2(name){
    const search = location.search
    const p = new URLSearchParams(search)
    return p.get(name)
}

//将url参数解析为json对象
function queryToObj(){
    const res = {}
    const search = location.search.substr(1)
    search.split('&').forEach(paramsStr=>{
        const arr = paramsStr.split('=')
        const key = arr[0]
        const val = arr[1]
        res[key] = val
    })
    return res
}

数组排平[1,2,[3,4,[5]],6] => [1,2,3,4,5,6]
function flat(arr){
    const isDeep = arr.some(item=>item instanceof Array)
    if(!isDeep){
        return arr
    }
    const res = Array.prototype.concat.apply([],arr)
    return flat(res)
}

数组去重
//第一种
function unique(arr){
    const res = []
    arr.forEach(item=>{
        if(res.indexOf(item)<0){
            res.push(item)
        }
    })
    return res
}
//第二种
function unique(arr){
    const set = new Set(arr)
    return [...set]
}

手写深拷贝

Object.assign为浅拷贝缓存

function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是 null ,或者不是对象和数组,直接返回
        return obj
    }
 
    // 初始化返回结果
    let result
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }

    for (let key in obj) {
    // 保证 key 不是原型的属性
        if (obj.hasOwnProperty(key)) {
        // 递归调用!!!
            result[key] = deepClone(obj[key])
        }
    }

     // 返回结果
    return result
  }
RAF requestAnimationFrame动画
更新频率要60帧/s,即16.67ms跟新一次视图
//设置一个div宽度为100px,3s后变为640px,即增长540px
//60帧/s  3s->180帧 每次变化3px
const $div1 = $("#div1")
let curWidth=100
let maxWidth=640
//第一种
function animate(){
    curWidth =+ curWidth
    $div1.css('width',curWidth)
    if(curWidth<naxWidth){
        setTimeout(animate,16.7)
    }
}
//第二种
function animate(){
    curWidth =+ curWidth
    $div1.css('width',curWidth)
    if(curWidth<naxWidth){
        requestAnimationFrame(animate)
    }
}
animate()