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
null和undefined的区别
null表示空,一个空对象,空指针
undefined表示未定义,声明一个变量但是没有赋值,或者没有声明变量却直接使用该变量
使用==无法区分null和undefined
js的基本数据类型有哪些?
基本类型:number,string,null,undefined,boolean,symbol,bigint
引用数据类型:array,object,function
其中array,function都是属于object的
==和===的区别
==判断值是否相等
===判断值和类型是否相等
其中==会有隐式转换
console.log('1'==1)//true
console.log(false==0)//true
console.log(['1','2']=='1,2')//true
string转number
boolean转number
object转number
后台自动调用valueOf进行转换数据类型,不会再代码中显示
判断是否是数组有哪些方法
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,因为数组也是对象的一种
js的执行顺序
因为js主要是用来操作dom,为了防止同时对dom进行删除或者修改操作,设置js为单线程语言
执行顺序
同步任务>事件循环
同步任务>微任务>宏任务
同步任务有:promise
微任务:primise.then
宏任务:定时器
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]
原型和原型链
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
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]
js数组去重
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]
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]
使用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)
})
}
判断二维数组中的最大值
使用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]
给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
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]
}
}
实现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,'哈哈哈'))
闭包
一个作用域可以访问另一个函数内部的局部变量,形成闭包
function fu(){
let i=0
console.log('闭包')
function fun(){
i++
console.log(`i变成了${i}`)
}
return fun
}
let result=fu()//执行fu全部函数的全部内容
result()//执行了fu函数里面的fun方法
内存泄漏
只要有其他函数调用闭包,闭包内的变量就会一直存在内存中,等到没有函数调用闭包时,才会销毁。若一直存在内存中,会造成内存泄漏。
修改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返回函数,不会立即执行
深拷贝和浅拷贝
浅拷贝:拷贝地址(引用)
深拷贝:完全复制出来新的对象
对对象类型和数组类型进行简单深拷贝代码实现
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、如果不是基本数据类型,递归此函数
localStorage、sessionStorage、cookie 的区别
共同点
都用于浏览器存储数据,同源
不同点:
cookie会在发送http时携带,大小不超过4K,local Storage,sessionStorage不会携带过去
cookie会有过期时间,常用于存储会话(token),身份验证信息等,localStorage一直存在浏览器中,sessionStorage页面关闭之后就会消失。
sessionStorage是不共享的,其他两个是共享的