闲了很久,来看看JS面试题
涩涩狗子镇楼! 1:延迟加载JS的方式? async 与 defer
async 是解析与渲染DOM结构同时进行
defer虽然也是解析script脚本与渲染DOM同时进行,但是会等待DOM结构渲染完成以后再去加载script
1 2 <script defer src="../"><script> <script async src="../"><script>
2:数据类型 javascript数据类型分为基本类型与引用类型两大类
基本类型:string,number,boolean,null,undefined,symbol,bigInt(存在争论,有些人认为不应该)
引用类型:object(object是一个大类,包含对象,数组,函数等引用类型)
3:null和undefined的区别 最初javascript木有undefined类型,是作者后续添加的,他本人认为不能将一切表示为无的值都设置为null,可以将无的基本类型设置为undefined
4:== 与 === 的区别 == 在数据比较时,会隐式转换(调用valueof进行转换对比),只比较值不比较类型
而===比较类型也比较值 必须到完全的相同,因此在项目中比较两个值应该采用===的方式
1 2 3 4 let a = 2 if(a == '2'){ console.log(' a = '2' ') }
5:微任务和宏任务 script脚本中包含同步与异步逻辑,页面会首先执行同步代码,再执行异步代码
而异步代码又区分为微任务和宏任务,与事件循环机制相关
在执行宏任务之前,会查看页面有没有未执行的微任务,先清空页面微任务再执行宏任务
微任务:Promise.then
宏任务:定时器,事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 setTimeout(()=>{ console.log('123') },2000) new Promise((resolve,reject)=>{ console.log('promise') resolve() }).then(()=>{ console.log('then1') resolve() }).then(()=>{ console.log('then2') resolve() }) console.log('同步') result: 'promise' '同步' 'then1' 'then2' '123'
6:作用域 :smile_cat: 1:除了函数外 js是没有块级作用域的
1 2 3 4 5 6 7 8 9 10 function aa(){ let a = 10 } aa() console.log(a) ::a is not defined for(var i = 1; i < 10 : i++){ } console.log(i) : i = 10
2:作用域链 内部可以访问外部变量 外部无法访问内部变量 从内到外依次查找 遵循内部优先
1 2 3 4 5 6 7 8 let a = 10 function fn(){ function fnn(){ console.log(a) } fnn() } fn() : 10
3;声明变量不带var 那么就是挂载window上 任意位置都能访问
var a = b = 10 === var a = 10 window.b = 10
var a,b = 10 === var a = 10 var b = 10
4:js有变量提升 国外称为变量悬挂声明
1 2 3 4 5 6 function fn(){ //var str console.log(str) // undefined var str = 20 } fn()
5:优先级
声明变量 > 声明普通函数 > 参数 > 变量提升
7:对象 1 2 3 4 5 6 7 console.log([1,2,3] === [1,2,3]) //false 因为两个对象都是new出来的并不相同 var a = { a:1 } var b = a console.log(a === b) //true 因为史诗同一个引用对象 因此相同
对象的key都是字符串类型
1 2 3 4 5 6 7 8 9 10 11 有趣的面试题 var a = {} var b = { key:'a' } var c = { key:'c' } a[b] = '123' // a[obj obj] = '123' a[c] = '456' // a[obj obj] = '456' console.log(a[b]) //console.log(a[obj obj])
对象是如何查找某个属性? 对象是通过构造函数生成的
1 2 3 4 5 6 7 8 9 function Fun(){ this.a = 'fun' } Fun.prototype.a = 'fun原型' let obj = new Fun() obj.a = '对象本身' obj.__proto__.a = '对象原型' console.log(obj.a) // '对象本身' 先查找对象本身 => 构造函数的内部 => 对象的原型 => 构造函数的原型 => 对象上一层原型
8:作用域 + this指向 + 原型 Loading——
9:判断数组方法 使用场景:虚拟dom判断子节点是不是数组
1:isArray
2:instanceof [坑多,typeof更多 不建议用]
3:原型判断
4::isPrototypeOf()
5:constructor
1 2 3 4 5 6 let arr = [1,2,3] console.log(Array.isArray(arr)) //true console.log(arr instanceof Array) //true console.log(Object.prototype.toString.call(arr).indexOf('Array') != -1) //8 [Object Array] console.log(Array.prototypee.isPrototypeOf(arr)) console.log(arr.constructor.toString().indexOf('Array') > -1)
10:slice与splice slice的作用,splice是否会改变原数组
1 2 3 4 5 6 7 8 //slice截取作用 参数可以写一个 代表从该参数位置开始截取到最后 let arr = [a,b,c,d] let arr2 = arr.slice(1,3) //从索引1开始 截取到3之前一位 [b,c] 返回新数组 //splice 删除 ,插入,替换 会改变原数组 let arr2 = [a,b,c,d] let arr3 = arr2.splice(1,1) // b 返回删除的元素数组 原本的arr2 [a,c,d] let arr4 = arr2.splice(1,1,'你好') // 从1开始 删除一个元素 在原本位置插入你好
11:多维数组最大值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let arr = [ [4,4,5], [10,123,123], [123123,12,323,123] ] 分别找到每个数组最大的值 输出[5,123,123123] code: function(arr){ let newArr = [] arr.forEach((iitem,index)=>{ newArr.push(Math.max(...item)) }) reurn newArr }
12:字符串新增方法实现某些功能 给字符串定义一个方法addStart,当传入该方法一个字符串时,返回当前字符串+参数前缀
1 2 3 String.prototype.addStr = function(str){ return str + this }
13:找出字符串出现最多次数字符和次数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1: var str = '111222333333444444444444' var obj = {} for(var i = 0; i < str.length; i++){ if(obj[str[i]]){ obj[str[i]]++ }else{ obj[str[i]] = 1 } } console.log(obj) // {a:b:c:d:} ///统计最大值 var max = 0 for(var k in obj){ if(max < obj.key){ max = obj.key } } for(var key in obj){ if(mx == obj[key]){ console.log(obj[key]) // 最多次数的字符 console.log(max) //最次数 } }
14:new操作符 1:创建一个空的对象
2:将空对象的原型指向构造函数原型
3:将空对作为构造函数上下文(改变this指向)
4:对构造函数有返回值的处理判断 如果这个构造函数返回基本类型 那么会忽略 如果是引用类型则会返回这个引用类型 new失效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function Fo(){ } console.log(new Fo()) //obj 创建一个空的对象 console.log(new Fo.__proto__ === Fo.prototype) console.log() 实现一个相同的函数 function Fun(age,name){ this.age = age this.name = name } function create(fn,..args){ //创建空对象 var obj = {} //将空对象原型指向构造函数原型 Object.setPrototypeOf(obj,fn.prototype) //改变this指向 var result = fn.apply(obj.args) //最后处理 return result instanceof Object ? result : obj } function(create(Fun,18,'lisi'))
15:闭包 1:闭包是什么
一个函数加上到创建函数作用域的连接 闭包关闭了函数在自由变量
js中尽量不要写全局变量 因为系统并不知道什么时候会垃圾回收
2:闭包可以解决什么问题【优点】
内部函数可以访问到外部函数局部变量
3:闭包的缺点
变量会驻留在内存中 造成内存损耗问题
内存泄露是在ie的情况
解决方式手动清空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function a (){ var b = 10 return function(){ console.log(b) } } a()() // a()执行完以后没有销毁 因此a()()能够打印出b //优点 let lis = document.getElementsByTagName('li') for(var i = 0; i < 3; i++){ lis[i].onclick = function(){ console.log(i) // 3 } } 改造成闭包的形式 for(var i = 0; i < 3; i++){ function((i){ lis[i].onclick = function(){ console.log(i) // 3 } lis[i] = null //手动清空 })(i) }
16:原型链 17:js继承方式 18:call,apply,bind区别 19:sort背后原理 20:深拷贝与浅拷贝 21:本次存储localStorage/sessionStorage与cookie区别 未完待续……