js基础面试题

1 判断null

typeof null的结果为object

如何判断一个数是否为null:
1、nums===null
2、isNan()

2 instanceof原理

用于检测某个实例对象是否是这个构造函数的实例

function instance_of(obj1,obj2){
	let proto=obj.__proto__
	let prototype=obj2.prototype
	let queue=[proto]
	while(queue.length){
		let temp=queue.shift()
		if(temp===null)return false
		if(temp===prototype)return true
		queue.push(temp.__proto__)
	}
}

3 类型转换

(1)强制转换

  • undefined、null、false、NaN、‘’、0、-0都会转换为false
  • 其他所有值都转为true,包括所有对象

undefined转换为数字结果为NaN,字符串如果内容为数字或进制值就正常转换,否则为NaN

(2)隐式转换

将对象转换为基本类型

  • 调用symbol.toPrimitive,转成功就结束
  • 调用valueOf,转成功就结束
  • 调用toString,转成功就结束
  • 报错

[] == ![] 结果为true
undefinednull 结果为true
null
null 结果为true

4 new操作符

new操作符执行步骤可分为以下几步:

  1. 生成一个新对象
  2. 将对象的原型连接到构造函数的原型上,并绑定this
  3. 判断返回值,如果没有返回对象,则返回创建的对象
function myNew(fn,...args){
	let obj=Object.create(fn.prototype)
	let res=fn.call(obj,...args)
	
	return res && typeof res==='object'? res:obj 
}

5 什么是作用域?什么是作用域链?

作用域是某些特定部分中变量,函数和对象的可访问性

当js需要查找某个变量的值时,它会从当前作用域查找,如果没有再去上级作用域中查找,直到查到全局作用域,这样一个过程形成的链条就叫作用域链

6 原型

  • 所有对象都有一个属性_proto_指向一个对象,也就是原型
  • 每个对象的原型都可以通过constructor找到构造函数,构造函数可以通过prototype找到原型
  • 每个对象都有其原型对象,这样形成一个关联一个、层层相互依赖的 从属关系,这就是原型链,原型链后面的对象可以使用前面的对象的属性和方法

7 继承

(1)原型链继承

function parent(){
	this.age=32
	this.arr=[1,2]
}
function child(){
	this.name='xie'
}
child.prototype=new parent()

let s1=new child()
let s2=new child()
s1.arr.push(3)

缺点:虽然父类的方法和属性能够访问,但是原型属性是共享的

(2)构造函数继承

function parent(){
	this.age=32
	this.arr=[1,2]
}

parent.prototype.sex='boy'
function child(){
	parent.call(this)
}

let s=new child()

缺点:无法继承父类的原型属性和方法

(3)类继承

class person{
	constructor(name){
		this.name=name
	}
}

class child extends person{
	constructor(name){
		super(name)
	}
}

const s=new child('xie')

通过原型实现的继承和class实现的继承有什么不同之处?

1、getPrototypeOf结果不同
2、this创造顺序不同,ES5的继承是先创建子类实例对象的this,然后再将父类的方法添加到this上,ES6的继承先将父类实例对象的属性加到this上,然在再在子类中修改this
3、class子类实例的构建,基于父类实例,只有super方法才能调用父类实例

8 深拷贝和浅拷贝

(1)浅拷贝:两个对象第一层的引用不相同

  • Object.assign(target,source)可实现浅拷贝
  • 扩展运算符:b={…a}

(2)深拷贝:两个对象内部所有引用都不相同

  • JSON.parse(JSON.stringfy(object))

function deeoclone(source){
	//判断是否为基本数据类型
	const isPrimitive=(value)=>{
		return /Number|Boolean|String|Null|Undefined|Symbol|/.test(Object.prototype.toString.call(value))
	}
}

const checkType=(value)=>{
	return Object.prototype.toString.call(value)
	}
	let res=null
	if(isPrimitive(source)){
		res=source
	}else if(checkType(source)==='[object Date]'){	
	res=new Date(source)
	}else if(checkType(source)==='[object RegExp]'){
		res=new RegExp(source)
	}else if(checkType(source)==='[object Set]'){
		res=new Set()
		for(let v of source){
			res.add(deepclone(v))
			}
	}else if(checkType(source)==='[object Map]'){
		res=new Map()
		for(let [key,value] of source.entires()){
			res.set(key,deepclone(value))	
		}
	}else if(checkType(source)==='[object Array]'){
		source.forEach((value,index)=>{
			res[index]=deepclone(value)
		})
	}else if(checkType(source)==='[object Object]'){
		res={}
		Object.entires(source).forEach(([key,value])=>{
			res[key]=deepclone(value)
			})
		}
	
	return res

9 手写promise.all和promise.race

function myAll(_promises){
	return new Promise((resolve,reject)=>{
		const promises=Array.from(_promises)
		const len=promises.length
		const res=[]
		let count=0
		for(let i=0;i<len;i++){
			Promise.resolve(promises[i]).then(o=>{
				res[i]=o
				count++	
				if(count===len){
				resolve(res)	
				}	
			}).catch(e=>reject(e))
		}
	})

}
function myRace(_promises){
	return new Promise((resolve,reject)=>{
		let promises=Array.from(_promises)
		promises.forEach(p=>{
			Promise.resolve(p).then(o=>resolve(o)).catch(e=>reject(e))
		})
	})
}

10 promise all错误处理

11 promise all若其中一个失败了,如何让其他成功的请求返回

let p1=Promise.resolve(1)
let p2=Promise.resolve(2)
let p3=Promise.resolve(3)
let p4=Promise.resolve(4)
let p5=Promise.reject(5)

const arr=[p1,p2,p3,p4,p5]
let all=Promise.all(arr.map(p=>p.catch(e=>console.log(e))))
all.then(res=>console.log(res)).catch(e=>console.log(e))

12 promise的串行实现

function runPromise(_promises){
	return _promises.reduce((prev,next)=>{
		prev.then(()=>next())
	},Promise.resolve())
}

async function runPromise(_promises){
	for(let p of _promises){
		await p()	
	}
}

13 await的优缺点

优点:在处理then的调用时,能够更清晰准确的写出代码

缺点:滥用await会导致性能问题,因为await会阻塞代码,导致代码失去了并发性

14 什么是事件循环?微任务和宏任务

事件循环是浏览器解决js单线程运行不会阻塞的一种机制。由于js为单线程的,前一个任务执行完后才能执行下一个任务。任务可分为同步任务和异步任务。

事件循环的流程为

  1. 整体script开始执行,将代码分为同步任务和异步任务
  2. 同步任务进入主线程依次执行
  3. 异步任务又分为微任务和宏任务
  4. 微任务和宏任务进入事件表中并注册相应的回调函数,每当指定的事件完成,事件表会将这个函数移到事件队列中
  5. 当主线程执行完毕,主线程为空时,会检查微任务的事件队列,如果有则全部执行,如果没有则执行下一个宏任务

微任务:promise的then方法
宏任务:setTimeout回调、setInternal回调、IO、postMessage

15 模块化

commonJS

module.exports={
	a:1
}

exports.a=1

var module=require('./a.js')
module.a

commonJs和ESM的区别

  • commonJS支持动态导入(require),ESM使用import异步加载
  • commonJS输出的是值拷贝,ESM输出的是值的引用
  • commonJS是运行时加载,ESM是编译时输出接口

16 垃圾回收

(1)清楚标记

  • 垃圾回收器给内存中存储的所有变量加上标记
  • 去除环境中的变量和被环境中的变量引用变量的标记
  • 剩下还被标记的变量就是需要清楚的变量
  • 垃圾回收器销毁带标记的值并回收它们占用的内存空间

(2)引用计数:跟踪每个值被引用的次数,回收引用次数为0的变量所占用的内存

缺点:当存在循环引用时,会导致内存泄漏

17 0.1+0.2!==0.3

原因:浮点数用而二进制表示的时候是无穷的,因为精度问题,两个浮点数相加会造成截断丢失精度

解决办法:先求出两个浮点数的最大小数长度x,在将两个数分别乘以10的x次方幂,相加再除以10的x次方幂

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值