js面试题(日更)

  1. async和defer的区别

有一个html文件,里面有引入了script的js代码段,并且使用修饰符修饰

<script async src="xxx.js"></script>
<script defer src="xxx.js"></script>

运行流程为

async 同步解析html,和script,待script解析完成后运行js

defer 同步解析html,和script,待script和html解析完成后,运行js

  1. null和undefined的区别

null表示空,一个空对象,空指针

undefined表示未定义,声明一个变量但是没有赋值,或者没有声明变量却直接使用该变量

使用==无法区分null和undefined

  1. js的基本数据类型有哪些?

基本类型:number,string,null,undefined,boolean,symbol,bigint

引用数据类型:array,object,function

其中array,function都是属于object的

  1. ==和===的区别

==判断值是否相等

===判断值和类型是否相等

其中==会有隐式转换

console.log('1'==1)//true
console.log(false==0)//true
console.log(['1','2']=='1,2')//true

string转number

boolean转number

object转number

后台自动调用valueOf进行转换数据类型,不会再代码中显示

  1. 判断是否是数组有哪些方法

let arr=['1','2','3','4']
console.log(Array.isArray(arr))//true
console.log(arr instanceof Object)//true
console.log(Object.prototype.toString.call(arr).indexOf('Array')>-1)//true
console.log(arr.constructor.toString().indexOf('Array')>-1)//true

其中instanceof中,,arr instanceof object也为true,因为数组也是对象的一种

  1. js的执行顺序

因为js主要是用来操作dom,为了防止同时对dom进行删除或者修改操作,设置js为单线程语言

执行顺序

同步任务>事件循环

同步任务>微任务>宏任务

同步任务有:promise

微任务:primise.then

宏任务:定时器

  1. object对象调用问题

object的key值是string类型

var obj={
  a:1,
  '张三':2
}
for( let key in obj){
  console.log(typeof key)//string
}

使用obect类型作为object的key,会被转为string类型

var obj={
  a:1,
  '张三':2
}
var obj2={
  b:1
}
obj[obj2]='obj2'
for( let key in obj){
  console.log(key)//都为string类型
}

常见面试题1

var a={}
var b={key:'a'}
var c={key:'c'}
a[b]='1'
a[c]='2'
console.log(a[b])
当执行a[b]时,a如下所示
a={
 [object Object]:'1'
}
当执行a[c]时,a发生改变
a={
 [object Object]:'2'
}
所以输出结果为'2'

对象作为key转为[object Object]

  1. 原型和原型链

function Fun(){
  this.say=function(){
    console.log('构造函数的say')
  }
}
let fun=new Fun()
fun.say=function(){
  console.log('对象本身的say')
}
fun.__proto__.say=function(){
  console.log('对象原型的say')
}
Fun.prototype.say=function(){
  console.log('构造函数原型的say')
}
fun.say()

对象本身的say>构造函数的say>构造函数原型的say>对象原型的say>undefined

console.log(fun.__proto__==Fun.prototype)//true
  1. slice和splice对数组的功能

slice用于数组的截取

array.slice(start,end),[start,end)
包括start,不包括end

返回截取出来的数组,不会对原的进行修改

let arr=[1,2,3,4]
let res=arr.slice(1,2)
console.log(res)//[2]

splicec用于替换,删除数组,会修改原数组

array.splice(开始的下标,删除几个 元素),返回值是被删除的元素组成的数组
array.splice(开始的下标,删除几个元素,替换的元素1,替换的元素2,....)将替换的元素和删除的元素进行替换,返回值是被删除的元素组成的数组
let arr=[1,2,3,4,5]
let result=arr.splice(1,2,'哈哈哈','000')
console.log('原数组',arr)//[1,'哈哈哈','000',4,5]
console.log('splice的返回值',result)//[2,3]
  1. js数组去重

  1. set方法

let arr=[1,1,1,2,3,4,5]
let result=Array.from(new Set(arr))
//[...new Set(arr)]es6解构
console.log(result)//[1,2,3,4,5]
  1. indexOf判断

创建新数组,for循环判断新数组中是否已经存在这个元素,不存在就添加进去

let arr=[1,1,1,2,3,4,5,4,5,3]
    function quchong(arr){
        //新数组
        let newarr=[]
        for(let i=0;i<arr.length;i++){
            //判断新数组中是否存在当前项
            if(newarr.indexOf(arr[i])==-1){
                newarr.push(arr[i])
            }
        }
        return newarr;
    }
let result=quchong(arr)
console.log(result)//[1,2,3,4,5]
  1. 使用filter+indexOf

filter函数,若当前项返回true,把当前item值加入到数组中

选出数组中能够被2整除的数,filter函数返回新数组

let array=[1,2,4,6,3]
    function chu2(arr){
        return arr.filter((item)=>{
            // 当前项%2为0,为true,加入数组中
            return item%2===0
        })
    }
console.log(chu2(array))//[2,4,6]

实现数组去重,数组元素第一次开始的 索引(indexOf)===当前索引

let arr=[1,1,1,2,3,4,5,4,5,3]
function quchong(arr){
    return arr.filter((item,index)=>{
        console.log('开始的地方',arr.indexOf(item),'索引',index)
        return arr.indexOf(item)===index
    })
}
let result=quchong(arr)
console.log(result)//[1,2,3,4,5]

4、使用Map()

function quchong(arr){
    let seen=new Map()
    let result= arr.filter((item)=>{
        return !seen.has(item)&&seen.set(item,1)
    })
}
  1. 判断二维数组中的最大值

使用Math.max(解构目标数组)

let arr = [
    [1, 2, 3, 4],
    [2, 3, 4, 1, 2],
    [5, 3, 4, 56, 99, 7]
]
function getmaxlist(arr){
    let newarr=[]
    arr.forEach((item)=>{
        newarr.push(Math.max(...item))//解构
    })
    return newarr
}
let result=getmaxlist(arr)
console.log(result)// [4, 4, 99]
  1. 给string添加方法

例子:'world'.addstring('hello')
返回string helloworld

实现原理,string原型加上addstring方法即可(注意this指向)

String.prototype.addstring=function(str){
    return str+this
}
var str='world'
let result=str.addstring('hello')
console.log(result)//helloworld
  1. 找出字符串每个字符出现的次数出现

遍历字符串,把未出现在对象中的加入对象中,出现过的数值+1

let str='qqqqqqqqjjjjjjjjjj43666vvvvv'
let obj={}
for(let i=0;i<str.length;i++){
    if(obj[str[i]]){
        obj[str[i]]++
    }else{
        obj[str[i]]=1
    }
}
let max=0
//遍历比大小
for(let item in obj){
    if(obj[item]>max){
        max=obj[item]
    }
}
  1. 实现new的原理

new的原理

1、创建了一个对象
2、对象的原型等于构造函数的原型
3、修改对象的this指向为构造函数的this
4、判断返回值类型
// 创建对象,改变this指向,返回对象
function person(age,str){
    this.name='张三'
    this.age=age
    this.str=str
}
function mynew(construtor,...argment){
    // 判断是否是构造函数
    if(!construtor.hasOwnProperty('prototype')){
        return TypeError('传入的不是构造函数')
    }
    let obj={}
    // 空对象原型指向构造函数原型
    obj.__proto__=construtor.prototype
    // 修改this指向,使构造函数的this指向obj
    // 如果构造函数有返回值,result是构造函数的返回值
    let result=construtor.apply(obj,argment)
    // 判断返回类型
    return result instanceof Object?result:obj
}
console.log(mynew(person,18,'哈哈哈'))
  1. 闭包

一个作用域可以访问另一个函数内部的局部变量,形成闭包

function fu(){
    let i=0
    console.log('闭包')
    function fun(){
        i++
        console.log(`i变成了${i}`)
    }
    return fun
}
let result=fu()//执行fu全部函数的全部内容
result()//执行了fu函数里面的fun方法
内存泄漏
只要有其他函数调用闭包,闭包内的变量就会一直存在内存中,等到没有函数调用闭包时,才会销毁。若一直存在内存中,会造成内存泄漏。
  1. 修改this指向

有3个函数可以修改this指向

call , apply , bind
函数.call(obj,1,2,3)
函数.apply(obj,[12,3])
函数.bind(obj,1,2,3)

使得函数里面的this指向obj对象,后面的是函数的参数

let obj={name:'123'}
function fun(age){
    // 修改this,使得obj==this
    this.age=age
    console.log(this)
}
fun()
fun.call(obj,18)//立即执行
fun.apply(obj,[17])//立即执行
fun.bind(obj,19)()//bind返回函数,不会立即执行
  1. 深拷贝和浅拷贝

浅拷贝:拷贝地址(引用)

深拷贝:完全复制出来新的对象

对对象类型和数组类型进行简单深拷贝代码实现
let obj={name:'李四',age:12}
function fun(obj){
    return JSON.parse(JSON.stringify(obj))
}
let result=fun(obj)
result.name='张三'
console.log('拷贝的obj:',result)
console.log('原来的obj:',obj)
let arr=[1,2,3]
let res=fun(arr)
res.push(4)
console.log('拷贝的数组:',res)
console.log('原来的数组:',arr)

返回结果如下图所示

递归完成深拷贝

function copydeep(obj){
    // 1、判断需要进行深拷贝的类型
    let result=null
    if(Array.isArray(obj)){
        result=new Array()
    }else{
        result=new Object()
    }
    // 2、遍历数组or对象
    for(let key in obj){
        // 3、判断是否是基本数据类型
        if(typeof obj[key]=='object'){
            // 4、不是基本数据类型,递归
            copydeep(obj[key])
        }else{
            result[key]=obj[key]
        }
    }
//将结果返回
    return result
}
1、判断需要深拷贝的数据类型(object,array)
2、根据对象类型创建相应的类型数据
3、遍历数组or对象
4、如果是基本数据类型,加到新创建的对象中
5、如果不是基本数据类型,递归此函数
  1. localStorage、sessionStorage、cookie 的区别

共同点

都用于浏览器存储数据,同源

不同点:

  1. cookie会在发送http时携带,大小不超过4K,local Storage,sessionStorage不会携带过去

  1. cookie会有过期时间,常用于存储会话(token),身份验证信息等,localStorage一直存在浏览器中,sessionStorage页面关闭之后就会消失。

  1. sessionStorage是不共享的,其他两个是共享的

猜你喜欢

转载自blog.csdn.net/m0_57108418/article/details/129189922