javascript的史上最全面试题

本文详细介绍了JavaScript的基础概念,包括手写节流和防抖、typeof 判断、深拷贝、Ajax使用、数组方法、闭包、内存泄漏、事件冒泡、本地存储与Cookie的区别、面向对象等。还涵盖了ES6的新特性,如var、let、const的区别以及Promise的理解。此外,文章提供了手写简易jQuery的示例,强调了插件和扩展性的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

-------------------JS概念性问题-------------------

手写节流和防抖

该题考察的是性能,体验优化方案,闭包。
防抖:时间被连续触发的情况下,只执行最后一次的事件
节流:时间间隔相等的(频率不变)的执行被频繁触发的事件。

typeof 能判断哪些类型?

typeof 能识别所有值类型,识别函数。
typeof能识别所有引用类型(不能继续识别为null还是object还是array)
考点:JS变量类型(变量类型的检测,对象数组的深浅拷贝等)

// 基本数据类型:string number Boolean object undefined null symbol

typeof 'a'  // 'string'
typeof 1   //  'number'
typeof true // 'boolean'
typeof null // 'object'
typeof {} // 'object'
typeof []  // 'object'
typeof undefined // 'undefined'
typeof Symbol('a')  // symbol
typeof function(){}  // 'function'

何时使用 === 何时使用 ==

考点:强制类型转换(字符串拼接,==,if语句和逻辑运算)

// 字符串拼接
let a = 100 + 10 // 110
let b = 100 + '10' // '10010'
let c = true + '10' // 'true10'

// ==
100 == '100' // true
0 == '' // true
0 == false // true
false == '' // true
null == undefined // true

// 除了==null 之外,其他一律用 ===
const obj = { x: 100 }
if(obj.a == null){} // 相当于 if(obj.a === null || obj.a === undefined)

// 前置知识:truly 变量 和 falsely 变量
// truly变量:!!a === true   (经过两步非运算值为true)
// falsely变量: !!a === false (经过两步非运算值为false)

// 以下是falsely变量,除此之外都是truly变量
!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined === false
!!false === false

// if语句
if(a){} // falsely
if(!!0 === false) // truly 

// 逻辑运算
console.log(10 && 0) // 0
console.log('' || 'abc') // abc
console.log( !window.abc ) // true

手写深拷贝

const obj = {
	name: 'ime',
	age: 18,
	address:{
		city: 'shanghai'
	},
	arr: ['a','b'.'c']
}

/**
 * 深拷贝等
 * @param {Object} obj 要拷贝的对象
*/

function deepClone(obj={}){
	// 如果不是对象直接返回值
	if(typeof obj != 'object' || obj == null){
		return obj
	}

	// 初始化返回结果
	let result
	if(obj instanceof Array){
		result = []
	}else{
		result = {}
	}
	
	for(let key in obj){
		// 保证key不是原型的属性
		if(obj.hasOwnProperty(key)){
			// 递归调用
			result[key] = deepClone(obj[key])
		}
	}
	
	return result
}

const obj2 = deepClone(obj);
obj2.address.city = 'beijing'
console.log(obj.address.city); // shanghai

For循环与map循环有什么区别

  • For 遍历对象自身的和继承可枚举的属性, 也就是说会包括哪些原型链上的属性
  • Map 方法不会对空数组进行检测,map 会返回一个新数组,不会对原数组产生影响

Ajax如何使用

请看我的另外一篇文章:ajax的详细介绍

一个完整的AJAX请求包括五个步骤:

1.创建XMLHTTPRequest对象
2.使用open方法创建HTTP请求,并设置请求地址

  • xhr.open(get/post,url,async,true(异步),false(同步))

使用前三个参数,设置发送的数据,用send发送请求。注册事件(给ajax设置事件onreadystatechange),获取响应并更新页面。

(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.

数组方法有哪些请简述

  • push() 从后面添加元素,返回值为添加完后的数组的长度
  • pop() 从后面删除元素,只能是一个,返回值是删除的元素
  • shift() 从前面删除元素,只能删除一个,返回值是删除的元素。
  • unshift() 从前面添加元素,返回值是添加完成后的数组的长度。
  • splice(i,n) 删除从i开始之后的那个元素。返回值是删除的元素。
  • concat() 链接两个数组,返回值为连接后的新数组
  • split() 将字符串转换为数组,返回值是一个数组
  • sort() 将数组进行排序,返回值是排好的数组。默认是升序排序,参数是排序函数
  • reverse() 将数组反转,返回值是反转后的数组
  • slice(start, end) 切去start到end的数组,不包含end索引的值,返回值是切出来的数组
  • forEach(callback) 遍历数组,无return,即使有return,也不会返回任何值,并且会影响原来的数组
  • map(callback) 映射数组(遍历数组),有return返回一个新数组
  • filter(callback) 过滤数组,返回一个满足要求的数组

如何判断一个数据是NaN

NaN 非数字 但是typeof检测是number类型

利用NaN的定义, 用typeof判断是否为number类型,并且判断是否满足isNaN

利用NaN是唯一一个不等于任何自身的特点 n !== n

利用ES6中提供的Object.is()方法(判断两个值是否相等) n == NaN

Js中null与nudefined区别:

相同点: 用if判断时,两者都会被转换成false
不同点: number转换的值不同,Number(null)为0,Number(undefined)NaN

Null表示一个值被定义了,但是这个值是空值。
Undefined变量声明但未赋值。

闭包是什么?有什么特性?对页面会有什么影响

闭包可以简单理解成:定义在一个函数内部的函数,且这个内部函数在包含他们的外部函数之处被调用时,就会形成闭包。

特点:

  1. 函数嵌套函数
  2. 函数内部可以引用外部的参数和变量。
  3. 参数和变量不会被垃圾回收机制回收。

使用:

  1. 读取函数内部的变量
  2. 这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。

优点:

  1. 变量长期驻扎在内存中
  2. 避免全局变量的污染
  3. 私有成员的存在

Js中常见的内存泄露

  1. 意外的全局变量
  2. 被遗忘的计时器或回调函数
  3. 脱离DOM的引用
  4. 闭包

什么是事件冒泡

一个事件触发后,会在子元素和父元素之间传播,这种传播分为三个阶段

  • 捕获阶段–从外到里(从window对象传导目标节点,这个阶段不会响应任何事件)
  • 目标阶段(在目标节点上触发)
  • 冒泡阶段–从里到外(从目标节点传导回window对象)

事件委托/事件代理就是利用事件冒泡的机制把里层需要响应的事件绑定到外层。

本地存储与cookie的区别

Cookie是小甜饼的意思。顾名思义,cookie确实非常小,它的大小限制在4KB左右。它的主要用途有保存登录信息,比如你登录某个网站可以看到“记住密码”,这通常就是通过在cookie中存入一段辨别用户的数据来实现的。

sessionStorage与localStorage的接口类似,但保存数据的生命周期与localStorage不同。做过后端开发的同学应该知道Session这个词的意思,直译过来就是“会话”。而sessionStorage是一个前端的概念,它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但当页面关闭后,sessionStorage中的数据就会被清空。它的大小限制在5MB左右。

什么是面向对象请简述

面向对象是一种思想,是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节;
这种思想是将数据作为第一位,这是对数据的一种优化,操作起来更加的方便,简化了过程。

Js的每个函数都有一个prototype属性,prototype指向一个对象,当函数作为构造函数时,prototype就起到类似于class的作用。

面向对象有三个特点:

  • 封装(隐藏对象的属性和实现细节,对外提供公共访问方式)
  • 继承(提高代码的复用性,继承是多态的前提)
  • 多态(是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象)

普通函数和构造函数的局别

  1. 构造函数也是一个普通函数,创建方式和普通函数一样,但是构造函数式习惯上首字母大写
  2. 调用方式不一样,普通函数直接调用,构造函数用关键字new来调用
  3. 调用时,构造函数内部会创建一个新对象,就是实例,普通函数不会创建新对象。
  4. 构造函数内部的this指向实例,普通函数内部的this指向调用函数的对象(如果没有对象调用,默认为window对象)
  5. 构造函数的返回值是创建的对象(也即是实例),普通函数的返回值由return语句决定。
  6. 构造函数的函数名与类名相同

请简述原型/原型链/(原型)继承

详细介绍请点击查看

什么是原型?

任何对象实例都有一个原型,也叫原型对象,这个原型对象由对象的内置属性__proto__指向它的构造函数的prototype指向的对象,即任何对象都是由一个构造函数创建的。

什么是原型链?

原型链基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。我们知道,每个构造函数都有一个原型对象,每个原型对象都有一个指向构造函数的指针construct,而实例又包涵一个指向原型对象的内部指针__proto__


原型链的核心就是依赖对象的`__proto__`的指向,当自身不存在的属性时,就会沿着原型链一层层的去扒出创建对象的构造函数,直到`Object`时,就没有`__proto__`指向了。因为`__proto__`实质找的是`prototype`,所以我们只要找到这个链条上的构造函数的`prototype`。其中`Object.prototype`是没有`__proto__`属性的,它==null

什么是原型继承?

原型继承是JS的一种继承方式,原型链作为实现继承的主要方法,其基本思路是利用原型让一个引用类型继承另外一个引用类型的属性和方法。
原型继承:利用原型中的成员可以被和其相关的对象共享这一特性,可以实现继承,这种实现继承的方式,就叫做原型继承。

new 操作符做了哪些事情

new 操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象。

改变函数内部this指针的指向函数(bind,apply,call )的区别

通过 apply 和 call 改变函数的 this 指向,他们两个函数的第一个
参数都是一样的表示要改变指向的那个对象, 第二个参数, apply 是
数组,而 call 则是 arg1,arg2...这种形式。通过 bind 改变 this 作
用域会返回一个新的函数,这个函数不会马上执行。

比比clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop 的区别 ?

clientHeight:表示的是可视区域的高度,不包含 border 和滚动条
offsetHeight:表示可视区域的高度,包含了 border 和滚动条
scrollHeight:表示了所有区域的高度,包含了因为滚动被隐藏的部分。
clientTop:表示边框 border 的厚度,在未指定的情况下一般为0
scrollTop:滚动后被隐藏的高度,获取对象相对于由 offsetParent 属
性指定的父坐标(css 定位的元素或 body 元素)距离顶端的高度。

拖拽功能的实现

首先是三个事件,分别是 mousedown,mousemove,mouseup 当鼠
标点击按下的时候,需要一个 tag 标识此时已经按下,可以执行mousemove 里面的具体方法。
clientX,clientY 标识的是鼠标的坐标,分别标识横坐标和纵坐标, 
并且我们用 offsetX 和 offsetY 来表示元素的初始坐标,
移动的举例应该是:鼠标移动时候的坐标-鼠标按下去时候的坐标。
也就是说定位信息为:鼠标移动时候的坐标-鼠标按下去时候的坐标+元素初始情况下的 offetLeft.
还有一点也是原理性的东西, 也就是拖拽的同时是绝对定位,我们改变的是绝对定位条件下的 left 以及 top等等值。

补充:也可以通过 html5 的拖放(Drag 和 drop)来实现

JS的垃圾回收机制

必要性:由于字符串、对象和数组没有固定大小,所有当他们的大小已知
时,才能对他们进行动态的存储分配。JavaScript 程序每次创建字符串、
数组或对象时,解释器都必 须分配内存来存储那个实体。只要像这样动
态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,
JavaScript 的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
这段话解释了为什么需要系统需要垃圾回收,JS 不像 C/C++,他有自己
的一套垃圾回收机制(Garbage Collection) 。JavaScript 的解释器可以
检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时
候, 他就知道不再需要这个对象, 可以把它所占用的内存释放掉了。 例如:
var a="hello world"; var b="world";
var a=b;
//这时,会释放掉"hello world",释放内存以便再引用垃圾回收的方法:标记清除、计数引用。
// 标记清除
这是最常见的垃圾回收方式,当变量进入环境时,就标记这个变量为”进
入环境“,从逻辑上讲,永远不能释放进入环境的变量所占的内存,永远不
能释放进入环境变量所占用的内存,只要执行流程进入相应的环境,就可
能用到他们。当离开环境时,就标记为离开环境。
垃圾回收器在运行的时候会给存储在内存中的变量都加上标记(所有都
加) ,然后去掉环境变量中的变量,以及被环境变量中的变量所引用的变
量(条件性去除标记) ,删除所有被标记的变量,删除的变量无法在环境
变量中被访问所以会被删除,最后垃圾回收器,完成了内存的清除工作,
并回收他们所占用的内存。

// 引用计数法
另一种不太常见的方法就是引用计数法, 引用计数法的意思就是每个值没
引用的次数, 当声明了一个变量,并用一个引用类型的值赋值给改变量,
则这个值的引用次数为 1,;相反的,如果包含了对这个值引用的变量又
取得了另外一个值,则原先的引用值引用次数就减 1,当这个值的引用次
数为 0 的时候,说明没有办法再访问这个值了,因此就把所占的内存给
回收进来,这样垃圾收集器再次运行的时候,就会释放引用次数为 0 的
这些值。
用引用计数法会存在内存泄露,下面来看原因:
function problem() {
var objA = new Object(); var objB = new Object();
objA.someOtherObject = objB; objB.anotherObject = objA;
}
在这个例子里面,objA 和 objB 通过各自的属性相互引用,这样的话,
两个对象的引用次数都为2,在采用引用计数的策略中,由于函数执行之
后,这两个对象都离开了作用域,函数执行完成之后,因为计数不为 0,
这样的相互引用如果大量存在就会导致内存泄露。
特别是在DOM对象中 ,也容易存在这种问题 :
var element=document.getElementById ( ’‘ );
var myObj=new Object();
myObj.element=element; element.someObject=myObj;
这样就不会有垃圾回收的过程。

onload 和 DOMContentLoaded的区别

考点:页面加载过程

window.addEventListener('load',()=>{
	// 页面的全部资源加载完才会执行,包括图片、视频等
})

document.addEventListener('DOMContentLoaded',()=>{
	// DOM 渲染完即可执行,此时图片、视频可能还没有加载完
})

手写一个简易的jQuery,考虑插件和扩展性

jQuery.dom.js 代码片段

ES6新特性

constlet、模板字符串、箭头函数、函数参数默认值、对象和数组结构、for…of 和 for…in、ES6中的类

var 和 let 和 const 的区别

Var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会
Var 声明的变量存在变量提升,let 和 const 不存在变量提升
同一作用域下 var 可以声明同名变量,let 和 const、不可以
Let 和 const 声明会形成块级作用域
Let 暂存死区
Const 一旦声明必须赋值,不能用 null 占位,声明后不能再修改,如果声明的是复合类型数据,可以修改属性

Promise的理解

什么是Promise

promise是承诺的意思,它承诺过一段时间会给你一个结果。promise是解决异步编程的方案,相比回调函数和事件更合理更强大。
从语法上讲,promise是一个对象,从它可以获取异步操作的消息

promise有那三种状态?

创造promise实例后,它会立即执行。

  • pending初始状态也叫等待状态
  • fulfiled成功状态
  • rejected失败状态

promise的两个特点?

  • promise对象的状态不受外界影响
  • promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可逆。

promise的三个缺点?

  • 无法取消promise,一旦新建它就会立即执行,无法中途取消
  • 如果不设置回调函数,promise内部跑出的错误,不会反映到外部
  • 当处于pending状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成。

promise可以解决什么问题?

  1. 回调地狱,代码难以维护,常常第一个函数的输出是第二个函数的输入
  2. promise可以支持多并发的请求,获取并发请求中的数据。

请简述async的用法

asyncgenerationpromise的语法糖,async就是将generator*换成async,将yiled换成await

函数前必须加一个async,异步操作方法前加一个await关键字,意思就是等一下,执行完了再继续走,注意:await只能在async函数中运行,否则会报错。

Promise如果返回的是一个错误的结果,如果没有做异常处理,就会报错,所以try..catch捕获一下异常就可以了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值