7. 说说 JavaScript 中的数据类型?存储上的差别?
• 基本类型:
o Number
o String
o Boolean
o Undefined
o null
o symbol
• 引用类型
o Object
o Array
o Function
• 声明变量时不同的内存地址分配:
o 简单类型的值存放在 栈 中,在栈中存放的是对应的值
o 引用类型对应的值存储在 堆 中,在栈中存放的是指向堆内存的地址
• 不同的类型数据导致赋值变量时的不同:
o 简单类型赋值 ,是生成相同的值,两个对象对应不同的地址
o 复杂类型赋值 ,
▪ 是将保存对象的内存地址赋值给另一个变量。
▪ 也就是两个变量指向堆内存中同一个对象
8. typeof 与 instanceof 区别 ,常见的数据类型有哪些,区 别是什么?
• typeof 会返回一个变量的基本类型,instanceof 返回的是一个布尔值
• instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
• 而 typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类
型中,除了 function 类型以外,其他的也无法判断
JavaScript 中常用的数据类型判断方法有以下 5 种:
⚫ typeof 操作符:可以返回一个字符串,用于表明所操作数的类型。
⚫ instanceof 操作符:可以判断一个对象是否属于某个类(或其子类)。
⚫ Object.prototype.toString() 方法:可以返回一个表示调用它的对象所属类的字符串。
⚫ constructor 属性:可以返回对创建该对象的数组函数的引用。
⚫ Array.isArray() 方法:可以判断一个值是否为数组。
这些方法的区别如下:
⚫ typeof 只能区分基本数据类型,不能区分具体的对象类型。
⚫ instanceof 只能用于判断对象类型,无法判断基本数据类型
⚫ Object.prototype.toString() 方法可以返回对象类型的具体字符串
⚫ constructor 属性可以返回对象所属类的构造函数
⚫ Array.isArray() 可以判断一个值是否为数组
9. 说说你对闭包的理解?闭包使用场景
闭包指在一个函数内部定义的函数可以访问外部函数作用域中的变量,即使外部函数已经执
行完毕,这种能力称为“闭包”。
⚫ 优点:
◼ 封装变量:
◆ 创建私有变量和函数,这样可以避免全局命名冲突,提高代码的可维护性。
◼ 延长变量的生命周期:
◆ 外部函数执行完毕后,其内部变量不会被销毁,而是被内部函数持有
◆ 可以延长变量的生命周期,使得外部函数的变量在内部函数中仍然可用。
◼ 实现柯里化:
◆ 将接受多个参数的函数转化为接受单一参数的函数
◆ 并返回接受剩余参数的函数。
⚫ 缺点:
◼ 内存泄漏:
◆ 持有对外部函数作用域的引用,如果这个引用被持续保留
◆ 那么外部函数中的变量就无法被释放,导致内存占用过高。
◼ 性能问题:
◆ 闭包会增加内存消耗和运行时的开销,因为需要维护额外的作用域链。
◆ 特别是在循环中使用闭包,可能会导致性能问题。
◼ 复杂性:
◆ 过度使用闭包可能会导致代码变得难以理解和维护,
◆ 闭包使得函数之间的关系变得更加复杂,增加了代码的复杂性和理解成本。
10.bind、call、apply 区别?如何实现一个 bind?
• 三者第一个参数都是 this 要指向的对象,如果没有这个参数或参数为 undefined 或
null,则默认指向全局 window
• 三者都可以传参,但是 apply 是数组,而 call 是参数列表,且 apply 和 call 是一次性
传入参数,而 bind 可以分为多次传入
• bind 是返回绑定 this 之后的函数,apply、call 则是立即执行
• 实现 bind 的步骤,我们可以分解成为三部分:
o 修改 this 指向
o 动态传递参数
// 方式一:只在 bind 中传递函数参数
fn.bind(obj,1,2)()
// 方式二:在 bind 中传递函数参数,也在返回函数中传递参数
fn.bind(obj,1)(2)
o 兼容 new 关键字
Function.prototype.myBind = function (context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
}
// 获取参数
const args = [...arguments].slice(1),
fn = this;
return function Fn() {
// 根据调用方式,传入不同绑定值
return fn.apply(this instanceof Fn ? new fn(...arguments) : context,
args.concat(...arguments));
}
}
11.说说你对事件循环的理解,宏任务和微任务的理解?
JavaScript 是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线
程就是阻塞,而实现单线程非阻塞的方法就是 事件循环
在 JavaScript 中,所有的任务都可以分为:
⚫ 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
⚫ 异步任务:异步执行的任务,比如 ajax 网络请求,setTimeout 定时函数等
⚫ 同步任务进入主线程,即主执行栈,
⚫ 异步任务进入任务队列
⚫ 主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。 ⚫ 上述过程的不断重复就事件循环
⚫ 微任务
◼ 一个需要异步执行的函数,执行时机是主函数执行结束之后、当前宏任务结束之前
◼ Promise.then
◼ MutaionObserver
◼ Object.observe(已废弃;Proxy 对象替代)
◼ process.nextTick(Node.js)
⚫ 宏任务
◼ 宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的
需求就不太符合
◼ script (可以理解为外层同步代码)
◼ setTimeout/setInterval
◼ UI rendering/UI 事件
◼ postMessage、MessageChannel
◼ setImmediate、I/O(Node.js)
按照这个流程,它的执行机制是:
⚫ 执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中
⚫ 当前宏任务执行完成后,会查看微任务的事件队列,然后将里面的所有微任务依次执行
完
12.DOM 常见的操作有哪些?
⚫ DOM(文档对象模型) 是用于表示 HTML 和 XML 文档的树状结构,并且允许程序和脚
本动态地访问和更新文档内容、结构和样式。在 JavaScript 中,常见的 DOM 操作包括
但不限于以下几种:
文档对象模型 (DOM) :
⚫ HTML 和 XML 文档的编程接口
⚫ 提供了对文档的结构化的表述
⚫ 定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容
⚫ 任何 HTML 或 XML 文档都可以用 DOM 表示为一个由节点构成的层级结构
<p title="title">content</p >
⚫ p 就是元素节点,
⚫ content 就是文本节点,
⚫ title 就是属性节点
⚫ 创建节点
◼ createElement
◆ 创建新元素,接受一个参数,即要创建元素的标签名
◆ const divEl = document.createElement("div");
◼ createTextNode
◆ 创建一个文本节点
◆ const textEl = document.createTextNode("content");
◼ createDocumentFragment
◆ 用来创建一个文档碎片,它表示一种轻量级的文档,主要是用来存储临时节
点,然后把文档碎片的内容一次性添加到 DOM 中 ◆ const fragment = document.createDocumentFragment();
◆ 当 请 求 把 一 个 DocumentFragment 节点插入文档树时,插入的不
是 DocumentFragment 自身,而是它的所有子孙节点
◼ createAttribute
◆ 创建属性节点,可以是自定义属性
◆ const dataAttribute = document.createAttribute('custom');
⚫ 获取节点
◼ document. getElementById ('id 属性值');返回拥有指定 id 的对象的引用
◼ document. getElementsByClassName ('class 属性值');返回拥有指定 class 的对象集
合
◼ document. getElementsByTagName ('标签名');返回拥有指定标签名的对象集合
◼ document. getElementsByName ('name 属性值'); 返回拥有指定名称的对象结合
◼ document/element. querySelector ('CSS 选择器'); 仅返回第一个匹配的元素
◼ document/element. querySelectorAll ('CSS 选择器'); 返回所有匹配的元素
◼ document. documentElement ; 获取页面中的 HTML 标签
◼ document. body ; 获取页面中的 BODY 标签
◼ document. all ['']; 获取页面中的所有元素节点的对象集合型
⚫ 除此之外,每个DOM 元素还有parentNode、childNodes、firstChild、lastChild、nextSibling、
previousSibling 属性
⚫ 更新节点
◼ innerHTML
◆ 不但可以修改一个 DOM 节点的文本内容
◆ 还可以直接通过 HTML 片段修改 DOM 节点内部的子树
◼ innerText、textContent
◆ 自动对字符串进行 HTML 编码,保证无法设置任何 HTML 标签
◆ innerText 不返回隐藏元素的文本,
◆ textContent 返回所有文本
◼ style
◆ DOM 节点的 style 属性对应所有的 CSS,可以直接获取或设置。
◆ 遇到-需要转化为驼峰命名
⚫ 添加节点
◼ innerHTML
◆ 如果这个 DOM 节点是空的,直接使用 innerHTML = '<span>child</span>'
◆ 修改 DOM 节点的内容,相当于添加了新的 DOM 节点
◼ appendChild
◆ 把一个子节点添加到父节点的最后一个子节点
◼ insertBefore
◆ 把子节点插入到指定的位置,使用方法如下:
◆ parentElement.insertBefore(newElement, referenceElement)
◼ setAttribute(属性名,属性值)
◆ 在指定元素中添加一个属性节点,如果元素中已有该属性改变属性值
⚫ 删除节点
◼ 调用父节点的 removeChild 把自己删掉
◼ 删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到 别的位置
⚫ 替换节点:
◼ parentNode. replaceChild (newNode, oldNode): 替换一个子节点为新的节点。
⚫ 查找相邻节点:
◼ element. nextElementSibling : 获取当前元素的下一个兄弟元素。
◼ element. previousElementSibling : 获取当前元素的上一个兄弟元素。
⚫ 获取元素尺寸和位置:
◼ element. offsetWidth 和 element. offsetHeight : 获取元素的布局宽度和高度,包
含边框和内边距。
◼ element. clientWidth 和 element. clientHeight : 获取元素内容区的宽度和高度,
不包括滚动条、边框和外边距。
◼ element. getBoundingClientRect (): 获取元素相对于视口的位置和尺寸信息。
⚫ 事件处理:
◼ element. addEventListener (eventType, callback): 给元素添加指定事件类型的监听
器。
◼ element. removeEventListener (eventType, callback): 移除元素指定事件类型的监
听器。