js高频面试题

1、同步和异步的区别

执行顺序不同。同步任务指在主线程上排队进行的任务,只有当前面的任务执行完才会执行下一个任务。异步任务不进入主线程,而是进入任务队列,只有等主线程任务执行完毕,任务队列开始通知主线程,请求执行任务,才会进入主线程。

2、预编译

预编译时候做了哪些事情?
1、创建ao对象
2、找形参和变量声明,作为ao对象的属性名,值为undefined
3、形参和实参相统一
4、找函数声明会替换变量声明

function fn(a,c){
console.log(a);//function a(){};
var a = 123;
console.log(a);//123
console.log(c);//function c(){};
function a(){};//函数声明
if(false){
var d = 678;
}
console.log(d);//undefined
console.log(b);//undefined
var b = function(){};//函数的表达式而不是函数声明
console.log(b);//function(){};
function c(){};//函数声明
console.log(c);//function c(){};
}
fn(1,2)

ao{
a: undefined 1 function a(){};
c: undefined 2 function c(){};
d: undefined
b: undefined
}

3、this指向问题

题目:

var name = 222;
var a = {
name:111,
say:function(){
	console.log(this.name)
}
}
var fun =a.say;
fun();
a.say();
var b = {
name:333,
say:function(fun){
	fun();
}
}
b.say(a.say);
b.say = a.say;
b.say();

1、*在函数中直接使用this

//在函数中直接使用this
function get(content){
	console.log(content);
}
get("你好")
//等同于 get.call(window,"你好")
//函数即对象,有对象就有方法

2、*函数作为对象的方法被调用(谁调用我,我就指向谁)

var person = {
name:"张三",
run:function(time){
	console.log(`${this.name}在跑步,最多${time}分就不行了`)}
}
person.run(30);
//等同于person.run.call(person,30)

题目:

var name = 222;
var a = {
name:111,
say:function(){
	console.log(this.name);
}
}
var fun = a.say;
//var fun = function(){
	//console.log(this.name);
//}
fun();//fun.call(window);  222
a.say();//a.say.call(a);111
var b= {
name:333,
say:function(fun){
	fun();
}
}
b.say(a.say);//fun()  fun.call(window) 222
b.say = a.say;
//var b= {
//name:333,
//say:function(){
//	console.log(this.name);
//}
//}

b.say();//b.say.call(b)  333

箭头函数的this:
箭头函数没有自己的this,导致内部的this就是外层代码块的this,即继承自父执行上下文中的this。所以不能用作构造函数。

var name = 222;
var objj={
	name:111,
	say:()=>{console.log(this.name)
	}
	};
objj.say();//222
var name = 222;
var objj={
	name:111,
	say:function(){
		var a = ()=>{
		console.log(this.name);
		}
			return a();
		}
	};
objj.say();//111

4、深拷贝、浅拷贝、赋值的区别

浅拷贝:重新在堆中创建内存,如果属性是基本类型拷贝的是基本类型的值,如果属性是引用类型拷贝的是内存地址(改变新数据会影响老数据)。

var person={name:"张三",hobby:['学习',['唱歌','跳舞'],'shopping']}
function shallow(obj){
	var target = {};//重新在堆中创建内存
	for (var i in obj){
	if(obj.hasOwnProperty(i)){
	target[i] = obj[i]
	}	
	}
	return target;
}
var person1 = shallow(person);
console.log(person);
person1.name = "李四";//拷贝前后基本数据类型互不影响
person1.hobby[0] = "玩耍";//引用类型共用一块内存互相影响
console.log(person1);
//{hobby: ["玩耍", ['唱歌','跳舞'], "shopping"],
//name: "张三"}
 
//{hobby: (3) ["玩耍", ['唱歌','跳舞'], "shopping"],
//name: "李四"}

深拷贝:将一个对象从内存中完整的拷贝出来,从堆内存中开辟一个新区域存放新对象(改变新数据不会影响老数据)。

var person={name:"张三",hobby:['学习',['唱歌','跳舞'],'shopping']};
let isArray =(arr)=> {
	return Object.prototype.toString.call(arr) === "[object Array]"
}
let isObject = (obj)=>{
	return typeof obj === "object" && obj !==null 
}
function deepClone(obj){
	var target = isArray(obj)?[]:{};
	for (var i in obj){
		if(isObject(obj[i])){
		target[i] = deepClone(obj[i])
		}else{
		target[i] = obj[i]
}
		
}
	return target;
}

赋值:当把一个对象赋值给一个新变量的时候,赋的是该对象在栈中的地址。而不是堆中的数据,两个对象指向的是同一个存储空间。因此两个对象是联动的。

var person={name:"张三",hobby:['学习',['唱歌','跳舞'],'shopping']};
var person1 =person;
person1.name = "李四"
console.log(person);
//{name: "李四", hobby: Array(3)}
console.log(person1);
//{name: "李四", hobby: Array(3)}

5、防抖

是闭包的实际应用
防抖函数:当持续触发的事件,一定时间内没有触发该事件,事件函数才会执行一次。

<div>
	<input id="input"/>
</div>
<script>
	function keyUp(callback){
		let timer;
		return function(value,delay){//闭包
			clearTimeout(timer)
			//清除以前触发的定时器
			timer = setTimeout(
			function(){
				callback(value)
			}
			,delay)
		}
		
	}
	function fn(value){
		console.log(value)
	}
	var input =document.getElementById('input')
	var TimeOut = keyUp(fn);
	input.addEventListener('keyup',function(e){
		TimeOut(e.target.value,1000)
	})
</script>

6、节流

当持续触发事件的时候,保证一段时间内,事件函数只执行一次。
案例:当鼠标持续点击按钮,在n秒内有效点击只有一次。

7、作用域链

会被保存到一个隐式的属性 scope 中,这个属性用户访问不到,但却真实存在,让js引擎来访问,里面存储的就是作用域链。 AO 和 GO的集合。

8、闭包

1、函数嵌套函数
2、函数外部能读取函数内部的值
3、参数和变量不会被垃圾回收机制回收
优点:变量会长期存在内存中
缺点:造成内存泄漏
闭包的实际应用:
setTimeout传递第一个函数是不能带参数,闭包可以解决这个问题。

function f1(a){
	return function f2(){
		console.log(a)
	}
}
var f3 = f1(1);
setTimeout(f3,1000);

9、split、splice、slice区别

split:根据特定字符分割字符串,返回生成的数组。
splice:删除、插入、替换,改变原数组
slice:数组截取,不改变原数组。

10、js运行机制

单线程。同一时间做同一件事。

11、arguments的对象是什么

类数组对象,有length,但没有数组的方法。
类数组转化为数组? es6展开运算符 Array.prototype.slice.call(arguments)
箭头函数是没有arguments的

12、为什么b会变成全局变量

function get(){
	let a=b=0
	//let a=(b=0)
	//赋值从右往左,但b在函数里又没有被定义,所以是个全局变量
}
get();

13、哪些操作会造成内存泄漏?

1、闭包
2、被遗忘的定时器
3、意外的全局变量
4、脱离dom的引用(虽然dom被清除但仍在内存当中,document.getElementById(’’))

14、什么是高阶函数?

将函数作为参数或者返回值的函数

15、手写Array.prototype.map

function map(arr,mapCallback){
		if(!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function' ){
			return []
		}else{
			var result = [];
			for(var i=0;i<arr.length;i++){
		      result.push(mapCallback(arr[i],i,arr))
			  //mapCallback回调函数
			}
		}

		return result;
	}
	console.log(map([1,2,3,4],(item)=>{return item+1}))

16.event-loop

事件循环由三部分组成
调用栈 微任务队列 消息队列
主任务>微任务>消息队列
js中的宏任务一般是:包括整体代码script,setTimeout,setInterval。
微任务是:Promise.then,process.nextTick。
async与await与Promise:
async-await 是Promise和generator的语法糖。为了让代码书写流畅,增加了可读性。
若async有返回值,相当于Promise.resolve(123)

async function demo01(){
	return 123;
}
demo01.then(val=>{console.log(val)})
//123

若没有return相当于执行了Promise.resolve()

async function demo01(){
	console.log(123);
}
demo01.then(val=>{console.log(val)})
//123
//undefined

await必须在async内部使用
await可以使用任何js表达式,但主要意图是等待Promise被resolved。
如果await的是Promise对象,则先执行async之外的代码(注意:Promise内的同步代码会先执行。)
如果await的是一个表达式,则会阻塞await后面的代码,先执行async外面的代码,主线程执行完毕后会再来执行await后面的代码。

17、js单例模式

解决的问题:一个全局使用的类,频繁的创建和销毁
优点:内存只有一个实例,减少了内存的开销
使用场景: 弹窗 全局缓存

var login = (function(){
	  var div
		div = document.createElement("div")
		div.innerHTML="我是登录弹窗";
		div.style.display = "none";
		document.body.appendChild(div);
		return div;
	})()
	
	document.getElementById('btn').onclick = function(){		
		login.style.display="block"
	}

缺点:资源的浪费,一进入页面就创建

var createLogin = (function(){
	  var div
	  return function(){
		if(!div){
		div = document.createElement("div")
		div.innerHTML="我是登录弹窗";
		div.style.display = "none";
		document.body.appendChild(div);
			}
			return div;
		}
		
		return div;
	})()
	
	document.getElementById('btn').onclick = function(){
		var login = createLogin();
		login.style.display="block"
	}

class:es5是没有类的,es6的class是构造函数的语法糖,静态方法static(关键字),静态方法不能被实例调用。用class和static结合实现单例模式。

18、js策略模式

定义一系列算法 并且封装起来 他们之间可以相互替换
核心:将算法的使用和算法的实现分离开
例:年终奖,S的人四倍工资,A的人三倍工资,B的人二倍工资

var strategies = {
		"S":function(salary){
			return salary*4
		},
		"A":function(salary){
			return salary*3
		},
		"B":function(salary){
			return salary*2
		}
	}
	var getSalary = function(strategy,salary){
		return strategies[strategy](salary)
	}
	console.log(getSalary("A",1000))

19、数组扁平化

将一个多维数组变成一维数组。
方法一、flat
arr.flat(Infinity)Infinity是正无穷,不管数组包裹多少层
方法二、递归

var res5 = [];
const fn = arr=>{
	for (var i = 0; i<arr.length;i++){
	if(Array.isArray(arr[i])){
	fn(arr[i])
	}else{
	res5.push(arr[i])
}
}
}
fn(arr);

20、BFC

理解:块级格式化上下文,指一个独立的块级渲染区域
如何创建BFC
1、float的值不是none
2、position的值不是position和static
3、display的值是inline-block、flex、inline-flex
4、overflow:hidden
作用:
1、阻止塌陷
2、阻止元素被浮动元素覆盖

21、js继承的几种方法

1、原型链继承
原来:子类的原型对象指向父类的实例,当子类实例找不到对应的属性和方法时会沿着原型链往上找。

function Parent(){
	this.name = ["aaa"]
}
Parent.prototype.getName = function(){
	return this.name;
}
function Child(){
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const child1 = new Child();
//const child2 = new Child();
//child1.name = ['bbb']
console.log(child1.name)
//console.log(child2.name)

缺点:多个实例指向同一原型,多个实例的值会互相影响。2、无法对父类进行传参。(无法实现super功能)

2、构造函数继承
在子类的构造函数中去执行父类的构造函数,并且为其绑定子类的this,也就是说把父类的属性方法挂到子类上去

function Parent(name){
	this.name = [name];
}
Parent.prototype.getName=function(){
	return this.name;
}
function Child(){
	Parent.call(this,"aaa")
}
const child1 = new Child();
const child2 = new Child();
child1.name = ['bbb']
console.log(child1.name)
console.log(child2.name)
console.log(child2.getName())//报错

缺点:并不能够继承父类原型上的方法和属性
3、组合式继承
缺点:每次创建实例的时候都要new Parent()
4、寄生式继承
特点:子类的prototype指向父类的prototype
缺点:对子类原型的操作会影响到父类的原型
解决方法:拷贝,Object.create(Parent.prototype)(寄生组合式继承)

22、创建对象的几种方法

1、new Oject创建对象
2、字面式创建对象
缺点:这两种用的都是同一个接口,会产生大量重复的代码。
3、工厂模式

function createPerson(name,age,job){
	var o = new Object();
	o.name = name;
	o.age = age;
	o.job = job;
	o.sayName = function(){
		alter(this.name)
	}
	return o;
}
let person = createPerson('mm',19,'active')

缺点:返回的是一个对象,无法判断返回的对象是什么类型
4、构造函数创建对象

function Person(name,age,job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = function(){
 alert(this.name);
 }; 
}
var person1 = new Person('Nike',29,'teacher');
var person2 = new Person('Arvin',20,'student');

对比工厂模式
1、没有显示的创建对象
2、直接将属性和方法赋值给this
3、没有return语句
4、可以识别对象的类型
缺点:每个方法都要在实例上重新创建一遍,占内存。
5、原型创建对象

function Person(){}
Person.prototype.name = 'Nike';
Person.prototype.age = 20;
Person.prototype.jbo = 'teacher';
Person.prototype.sayName = function(){
 alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name ='Greg';
alert(person1.name); //'Greg' --来自实例
alert(person2.name); //'Nike' --来自原型

当为对象添加一个属性时,就会屏蔽原型中的同名属性。
6、组合模式(构造函数模式和原型模式)

23、+=与=+的区别

表达式 A=+B 会进行 B转化为数字 赋值给A
表达式 A+=B 会进行 A = A + B; 也就是js的加法运算

24、三次握手 四次挥手

握手:
1、客户端准备和服务器端进行连接
2、服务器端接收响应
3、客户端收到响应
挥手:
1、客户端已经接近结尾准备和服务器端断开
2、服务器端已经知道客户端没话说了
3、客户端等待服务器端反馈
4、客户端收到,双方确认关闭。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值