1. 为什么 JS 是单线程的?
因为JS里面有可视的Dom,如果是多线程,这个线程正在删除DOM节点,另一个线程正在编辑Dom节点,导致浏览器不知道该听谁的
2.如何理解同步和异步?
同步:按照代码书写顺序执行处理指令的一种模式,上一段代码执行完才能执行下一段代码。
异步:并行处理的方式,不必等待一个程序执行完,可以执行其它的任务。
JS之所以需要异步的原因:JS是单线程运行的
常用的异步场景有:定时器、ajax请求、事件绑定。
3. JS 是如何实现异步编程的?
1).回调函数callback:将需要异步执行的函数作为回调函数执行
优点:解决了同步的问题
缺点:回调地狱(回调嵌套层数太多,代码结构混乱),不能用 try catch 捕获错误,不能 return
2).Promise对象:有三种状态pending(初始化状态)、fulfilled(成功状态)、rejected(失败状态)
优点:解决了回调地狱的问题
缺点:无法取消 Promise,错误需要通过回调函数来捕获
3).async/await
优点:基于Promise实现的异步函数,代码清晰,处理了回调地狱的问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能降低
注:使用 async 关键声明函数会隐式返回一个Promise。
4).事件监听模式:采用事件驱动的思想,当某一事件发生时触发执行异步函数
缺点:整个代码全部变为事件驱动模式,难以分辨主流程
5).发布订阅模式:当异步任务执行完成时发布消息给信号中心,其他任务通过在信号中心 中订阅消息来确定自己是否开始执行;
6).利用生成器实现
4. 宏任务和微任务都有哪些?
宏任务:script、setTimeOut、setInterval、setImmediate、I/O、页面渲染
微任务:Promise.then、process.nextTick、Object.observe、MutationObserver
注意:Promise是同步任务
5. 宏任务和微任务都是怎样执行的?
执行顺序:主线程任务->宏任务->微任务->微任务里的宏任务->.......->任务全部完成
执行宏任务script,进入script后,所有的同步任务主线程执行,所有宏任务放入宏任务执行队列,所有微任务放入微任务执行队列
先清空微任务队列,再取一个宏任务,执行,再清空微任务队列。依次循环
6. 为什么要使用模块化?
模块化是一种管理代码的方法,可以将代码分割为不同的模块或文件,并通过特定的方式来管理它们之间的依赖关系和导出关系,使得代码的结构更加清晰、易于维护和扩展。
JavaScript没有原生支持模块化时,开发者常使用命名空间和立即执行函数等方式来实现模块化。会出现:命名空间容易造成命名冲突,立即执行函数存在代码冗余和性能问题等。
7.都有哪几种方式可以实现模块化,各有什么特点?
命名空间模式:通过为全局对象添加属性来避免命名冲突,例如将模块的所有函数和变量放在一个对象中,以避免与全局命名空间冲突。
但这种方式会导致命名空间变得臃肿,难以维护。
IIFE 模式:使用立即执行函数表达式来创建私有作用域,从而避免命名冲突。这种方式也称为模块模式,它使用一个匿名函数来封装模块,并返回一个公共接口。
CommonJS 模块化:使用 require() 导入模块,exports 导出模块,使得模块可以在不同的环境中使用。Node.js 采用了 CommonJS 标准来实现模块化。
AMD 模块化:支持异步加载模块,使用 define() 来定义模块,使用 require() 来加载模块。
ES6 模块化:ES6 引入了原生的模块化系统,可以使用 import 和 export 语句导入和导出模块。它提供了静态分析功能,可以在编译时进行优化,使得模块加载更快。
8. JS 模块包装格式有哪些?
Commonjs:同步运行,不适合前端
AMD:异步运行,RequireJS规范
CMD:异步运行,seajs 规范
9. ES6 和 Commonjs 的区别?
Commonjs模块输出的是值的拷贝,ES6输出的值是值的引用
Commonjs是在运行时加载,是一个对象,ES6是在编译时加载,是一个代码块
Commonjs的this指向当前模块,ES6的this指向undefined
10.require/import 之间的区别?
(1)require是CommonJS语法,import是ES6语法;
(2)require只在后端服务器支持,import在高版本浏览器及Node中都可以支持;
(3)require引入的是原始导出值的复制,import则是导出值的引用;
(4)require是运行时动态加载,import是静态编译;
(5)require默认调用不是严格模式,import默认调用严格模式
11. exports 和 module.exports 有什么区别?
导出方式不一样
exports.xxx='xxx'
module.export = {}
exports是module.exports的引用,两个指向的是同一个地址,但require只能看到module.exports
12.闭包:指能够读取其他函数内部变量的函数,或子函数在外调用时, 子函数所在父函数的作用域不会被释放
13.闭包的用途
1).读取函数内部的变量
2).让这些变量的值始终保持在内存中。不会在f1调用后被自动清除。
3).方便调用上下文的局部变量,利于代码封装。
原因:f1是f2的父函数,f2被赋给了一个全局变量,f2始终在内存中,f2的存在依赖f1,因此f1始终存在内存中,不会在调用结束后,被垃圾回收机制回收。
14.闭包的缺点
1).由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2).闭包会在父函数外部,改变父函数内部变量的值。如果把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时不要随便改变父函数内部变量的值。
15.JS 有哪些数据类型?
基本数据类型:Undefined、Null、Boolean、Number、String、Symbol
引用数据类型:只有Object一种,主要包括对象、数组和函数
16.基本数据类型和引用数据类型有什么区别?
两者作为函数的参数进行传递时:
基本数据类型传入的是数据的副本,原数据的更改不会影响传入后的数据。
引用数据类型传入的是数据的引用地址,原数据的更改会影响传入后的数据。
两者在内存中的存储位置:
基本数据类型存储在栈中
引用数据类型在栈中存储了指针,该指针指向的数据实体存储在堆中
17.判断数据类型的方法有哪些?
(1)利用typeof可以判断数据的类型;
(2)A instanceof B可以用来判断A是否为B的实例,但它不能检测 null 和 undefined;
(3)B.constructor == A可以判断A是否为B的原型,但constructor检测 Object与 instanceof不一样,还可以处理基本数据类型的检测。
(4)Object.prototype.toString.call()
18.什么是执行上下文和执行栈?
变量或函数的执行上下文,决定了它们的行为以及可以访问哪些数据。执行上下文是当前正在执行的“代码环境”。(如DOM中全局上下文关联的是window对象)
每个函数调用都有自己的上下文。当代码执行流进入函数时,函数的上下文被推到一个执行栈中。在函数执行完之后,执行栈会弹出该函数上下文,在其上的所有变量和函数都会被销毁,并将控制权返还给之前的执行上下文。JS的执行流就是通过这个执行栈进行控制的。
19.什么是作用域和作用域链?
作用域是标识符所能生效的范围。作用域最大的用处是隔离变量,不同作用域下同名变量不会有冲突。ES6中有全局作用域、函数作用域和块级作用域
当一个变量在当前块级作用域中未被定义时,会向父级作用域寻找。如果父级仍未找到,会再向上寻找,直到找到全局作用域为止。这种一层一层的关系,就是作用域链:
内部作用域->外部作用域-> 全局作用域
20.作用域和执行上下文的区别是什么?
1)函数的执行上下文只在函数被调用时生成,作用域在创建时已经生成;
2)函数的作用域会包含若干个执行上下文(当函数未被调用时有可能是零个)。
21.如何改变 this 指针的指向?
使用apply、call、bind方法改变this指向(并不会改变函数的作用域)。比较如下:
(1)三者第一个参数都是this要指向的对象,即想指定的上下文,上下文就是指调用函数的那个对象(没有就指向全局window);
(2)apply和bind的第二个参数都是数组,call接收多个参数并用逗号隔开;
(3)apply和call只对原函数做改动,bind会返回新的函数(要生效还得再调用一次)。
举例:
const carDetails = {
name: "Ford Mustang",
getName(){
return this.name;
},
isRegistered: true
};
console.log(carDetails.getName()); // Ford Mustang
this 指向的是 carDetails 对象,该对象当前是执行函数的“所有者”对象。
var name = "Ford Ranger";
var getCarName = carDetails.getName;
console.log(getCarName()); // Ford Ranger
在这里没有打印上面的Ford Mustang,因为getCarName 方法有一个不同的“所有者”对象,即 window 对象。在全局作用域中使用 var 关键字声明变量会在 window 对象中附加与变量名称相同的属性。当没有使用 “use strict” 时,在全局作用域中 this 指的是 window 对象。
console.log(getCarName === window.getCarName); // true
console.log(getCarName === this.getCarName); // true
this 和 window 引用同一个对象。
console.log(getCarName.apply(carDetails)); // Ford Mustang
console.log(getCarName.call(carDetails)); // Ford Mustang
apply 和 call 方法期望第一个参数是一个对象,该对象是函数内部 this 的值。
IIFE 或立即执行的函数表达式,在全局作用域内声明的函数,对象内部方法中的匿名函数和内部函数的 this 具有默认值,该值指向 window 对象。
(function (){
console.log(this);
})(); // 打印 "window"
function iHateThis(){
console.log(this);
}
iHateThis(); // 打印 "window"
const myFavoriteObj = {
guessThis(){
function getName(){
console.log(this.name);
}
getName();
},
name: 'Marko Polo',
thisIsAnnoying(callback){
callback();
}
};
myFavoriteObj.guessThis(); // 打印 "window"
myFavoriteObj.thisIsAnnoying(function (){
console.log(this); // 打印 "window"
});
想要获取 myFavoriteObj 对象中的 name 属性(即Marko Polo)的值,则有两种方法可以解决此问题。 一种是将 this 值保存在变量中。
const myFavoriteObj = {
guessThis(){
const self = this; // 把 this 值保存在 self 变量中
function getName(){
console.log(self.name);
}
getName();
},
name: 'Marko Polo',
thisIsAnnoying(callback){
callback();
}
};
第二种方式是使用箭头函数
const myFavoriteObj = {
guessThis(){
const getName = () => {
console.log(this.name);
}
getName();
},
name: 'Marko Polo',
thisIsAnnoying(callback){
callback();
}
};
箭头函数没有自己的 this 。它复制了这个作用域中的 this,this 值在 getName 内部函数之外,也就是 myFavoriteObj 对象。
22. 状态码
101 服务器同意客户端在HTTP协议基础上变更成其他协议
200成功返回响应
204 只有响应头,没有body数据
206 服务器返回一部分body数据
301永久重定向
302临时重定向
304可以在缓存中取数据(协商缓存)
400请求格式错误或没有服务器请求的数据
401没有权限访问
403请求服务器被禁止
404请求URL不存在
405 不允许使用某些方法操作资源
500服务器处理请求出错
501服务器超出能力之外的方法
502 服务器自身工作正常,访问后端服务时出错
503 服务器忙,暂时无法响应服务
504来自网关或代理服务器,请求资源服务器超时
23. 什么是 AJAX?如何实现?
Ajax是用于实现局部网页异步刷新的技术
ajax的实现主要包括四个步骤:
(1)创建核心对象XMLhttpRequest;
(2)利用open方法打开与服务器的连接;
(3)利用send方法发送请求("POST"请求时,还需额外设置请求头)
(4)监听服务器响应,接收返回值。
24.浅拷贝与深拷贝有何区别?如何实现?
浅拷贝:只复制指向某个对象的指针,而不复制对象本身。
浅拷贝的实现方式:
(1)Object.assign():多维时对内层对象是浅拷贝;目标对象只有一层的时候,是深拷贝
(2)扩展运算符
深拷贝:拷贝数据时,将数据的所有引用结构都拷贝一份。
深拷贝的实现方式:
(1)手写遍历递归赋值;
(2)结合使用JSON.parse()和JSON.stringify()方法。
25.原型和原型链是什么?
原型
JavaScript的对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是自身的原型
原型链
当一个对象调用自身不存在的属性/方法时,就会去自己 [proto] 关联的前辈 prototype 对象上去找,如果没找到,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止,从而形成 “原型链”。
26.事件委托
事件委托(事件代理),事件委托利用事件冒泡,把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,委托就无法实现。
27.变量的提升
提升用来描述变量和函数移动到其(全局或函数)作用域顶部。
执行上下文有两个阶段: 编译 和 执行 。
编译-在此阶段,JS 获取所有函数声明,并将其提升到其作用域的顶部,以便稍后可以引用它们,并获取所有使用 var 关键字声明的变量,默认值:undefined
执行-在此阶段,它将值赋给之前提升的变量,并执行或调用函数(对象中的方法)。
注意:函数表达式或箭头函数,let 和 const 声明的变量,都不会被提升
28.高阶函数:将函数作为参数或返回值的函数。
29. undefined 和 null 有什么区别
它们都属于虚值,可使用 Boolean(value) 或 !!value 将其转换为布尔值时,值为 false 。
undefined 是未指定特定值的变量的默认值,或者没有显式返回值的函数
let _thisIsUndefined;
console.log(_thisIsUndefined); // undefined
null 是“不代表任何值的值”。 null 是已明确定义给变量的值。
fs.readFile((e) => {
console.log(e); // 当没有错误发生时,打印 null
});
在比较 null 和 undefined 时,我们使用 == 时得到 true ,使用 === 时得到 false
30.== 和 === 以及 Object.js 的区别
"==="严格运算符:如果两个值的类型不同,返回false;相同类型返回true;
两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象
"=="相等运算符:在比较不同类型的数据时,相等运算符会先将数据进行类型转换,再用严格相等运算符比较
Object.js 主要的区别:+0 != -0,但 NaN == NaN
假设比较 x == y:
1. 如果 x 和 y 的类型相同,会换成 === 操作符进行比较。
2. 如果 x 为 null , y 为 undefined ,则返回 true 。
3. 如果 x 为 undefined 且 y 为 null ,则返回 true 。
4. 如果 x 的类型是 number , y 的类型是 string ,那么返回 x == toNumber(y) 。
5. 如果 x 的类型是 string , y 的类型是 number ,那么返回 toNumber(x) == y 。
6. 如果 x 为类型是 boolean ,则返回 toNumber(x)== y 。
7. 如果 y 为类型是 boolean ,则返回 x == toNumber(y) 。
8. 如果 x 是 string 、 symbol 或 number ,而 y 是 object 类型,则返回 x == toPrimitive(y) 。
9. 如果 x 是 object , y 是 string , symbol 则返回 toPrimitive(x) == y 。
10. 剩下的 返回 false
注:toPrimitive 首先在对象中使用 valueOf 方法,然后使用 toString 方法来获取该对象的原始值
如果使用 === 运算符,则第一个示例以外的所有比较将返回 false ,因为它们的类型不同,第一个示例返回 true 是因为两者的类型和值相同。
31. &&和||
&& 逻辑与,在其操作数中找到第一个虚值表达式并返回它,如果没有找到任何虚值表达式,则返回最后一个真值表达式。它采用短路来防止不必要的工作。
|| 逻辑或,在其操作数中找到第一个真值表达式并返回它。这也使用了短路来防止不必要的工作。
32.使用 + 或一元加运算符是将字符串转换为数字的最快方法吗
+ 是将字符串转换为数字的最快方法,因为如果值已经是数字,它不会执行任何操作。
33. 什么是事件传播?
事件流:从页面中接收事件的顺序(即事件传播)
当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。
在“冒泡阶段”中,事件向上传播至父级,祖父母,祖父母或父级,到达window为止;
在“捕获阶段”中,事件从 window 开始向下触发元素事件或 event.target 。
事件传播有三个阶段:
1.捕获阶段window, document, html, grandparent, parent
2.目标阶段:事件已达到目标元素
3.冒泡阶段child, parent, grandparent, html, document, window
34.event.preventDefault() 和 event.stopPropagation()方法之间有什么区别?
event.preventDefault() 方法可防止元素的默认行为。
在表单元素中使用,它将阻止其提交
在锚元素中使用,它将阻止其导航
在上下文菜单中使用,它将阻止其显示或隐藏
event.stopPropagation() 方法用于阻止捕获和冒泡阶段中当前事件的进一步传播。
35.如何知道是否在元素中使用了event.preventDefault() 方法?
返回一个布尔值false表明在特定元素中调用了event.preventDefault()
36.为什么此代码 obj.someprop.x 会引发错误?
const obj = {};
console.log(obj.someprop.x);
由于尝试访问 someprop 属性中的 x 属性,而 someprop 并没有在对象中,所以值为 undefined 。记住对象本身不存在的属性,并且其原型的默认值为 undefined 。因为 undefined 没有属性 x ,所以试图访问将会报错。
37.为什么在 JS 中比较两个相似的对象时返回 false?
JS 以不同的方式比较对象和基本类型。
在基本类型中,JS 通过值对它们进行比较。
在对象中,JS 通过引用或存储变量的内存中的地址对它们进行比较。
因此,第一个console.log 语句返回 false ,第二个 console.log 语句返回 true
a 和 c 有相同的引用地址,而 a 和 b 没有。
38. !! 运算符能做什么?
!! 运算符可以将右侧的值强制转换为布尔值。
39. 什么是 event.target ?
event.target 是发生事件的元素或触发事件的元素
JS 代码如下:
如果单击 button,即使将事件附加在最外面的 div 上,也打印 button 标签,因此event.target 是触发事件的元素。
40.什么是 event.currentTarget?
event.currentTarget 是在其上显式附加事件处理程序的元素。
JS 代码如下:
如果单击 button,它会打印最外面的 div 标签。因此,event.currentTarget 是附加事件处理程序的元素。
41. 如何在一行中计算多个表达式的值?
可以使用 逗号 运算符在一行中计算多个表达式。它从左到右求值,并返回右边最后一个项目或最后一个操作数的值。
如图的最后x的结果为27
42. JavaScript 中的虚值是什么?如何检查?
const falseValues = ['', 0, null, undefined, NaN, false];
虚值就是在转换为布尔值时变为 false 的值。使用 Boolean 函数或者 !! 运算符检查
43.use strict
使代码在函数或整个脚本中处于严格模式。严格模式帮助我们在代码的早期避免 bug,并为其添加限制:
44. 什么是 IIFE,它的用途是什么?
IIFE或立即调用的函数表达式是在创建或声明后将被调用或执行的函数。创建IIFE的语法是,将 function (){} 包裹在括号 () 内,然后再用另一个括号 () 调用它即(function(){})()
IIFE的一个主要作用是避免与全局作用域内的其他变量命名冲突或污染全局命名空间
IIFE 还可以用来解决一个常见的面试题:
假设我们有一个带有 list-group 类的 ul 元素,它有 5 个 li 子元素。当单击单个 li 元素时,打印对应的下标值。但这里每次点击 li 打印 i 的值都是 5 ,这是由于闭包。闭包只是函数记住其当前作用域,父函数作用域和全局作用域的变量引用的能力。当在全局作用域内使用 var 关键字声明变量时,就创建全局变量 i 。因此,单击 li 元素时,它将打印 5 ,因为这是稍后在回调函数中引用它时 i 的值。
使用 IIFE 可以解决此问题:
该解决方案因为IIFE会为每次迭代创建一个新的作用域,我们捕获 i 的值并将其传递给currentIndex 参数,因此调用IIFE时,每次迭代的 currentIndex 值都是不同的。
45.arguments 的对象是什么?
arguments 对象是函数中传递的参数值的集合,类似数组,因为它有length属性,可以使用数组索引表示法 arguments[1] 来访问单个值,但它没有数组中的内置方法,如: forEach、reduce、filter、map。可以使用 Array.prototype.slice 将其转换成一个数组。
注意:箭头函数中没有arguments对象。
const four = () => arguments;
four(); // Throws an error - arguments is not defined
当调用函数 four 时,它会抛出一个 ReferenceError: arguments is not defined error
使用 rest 语法,可以解决这个问题。
const four = (...args) => args;
这会自动将所有参数值放入数组中。
46.如何创建一个没有 prototype(原型)的对象?
使用 Object.create 方法创建没有原型的对象。
47.为什么在调用这个函数时,代码中的b 会变成一个全局变量?
原因是赋值运算符是从右到左的求值的。这意味着当多个赋值运算符出现在一个表达式中时,它们是从右向左求值的。所以上面代码变成了:
表达式 b = 0 求值,b 没有声明。因此,JS引擎在这个函数外创建了一个全局变量 b ,之后表达式 b = 0 的返回值为 0,并赋给新的局部变量 a 。
可以通过在赋值之前先声明变量来解决:
48. var , let 和const 的区别
let和var的比较是比较“声明”的问题,let 和 const 是值的修改问题
var声明的变量会挂载在window上,而let和const声明的变量不会
var声明变量存在变量提升,let和const不存在变量提升
let和const声明形成块作用域
if(1){
var a = 100;
let b = 10;
const c = 1;
}
console.log(a); // 100
console.log(b) // 报错:b is not defined ===> 找不到b这个变量
console.log(c) // 报错:c is not defined ===> 找不到c这个变量
同一作用域下let和const不能声明同名变量,而var可以
var a = 100;
console.log(a); // 100
var a = 10;
console.log(a); // 10
let a = 100;
const a = 10;
// 控制台报错:Identifier 'a' has already been declared ==> 标识符a已经被声明
暂存死区--let
const
1.一旦声明必须赋值,不能使用null占位
2.声明后不能再修改
3.如果声明的是复合类型数据,可以修改其属性
49.什么是箭头函数
是一种匿名函数写法
1)箭头函数表达式没有自己的this, arguments, super, new.target,不能用作构造函数。
它捕获词法作用域函数的 this 值,在此示例中,addAll 函数将复制computeResult 方法中的 this 值,如果在全局作用域声明箭头函数,this 值为 window 对象
2)可以在箭头函数中使用与函数表达式和函数声明相同的参数。如果在一个箭头函数中有一个参数可以省略括号。
3)箭头函数不能访问 arguments 对象。调用第一个 getArgs 函数会抛出一个错误。可以使用rest参数来获得在箭头函数中传递的所有参数。
50.什么是类
类(class) 是在 JS 中编写构造函数的新方法。它是使用构造函数的语法糖,在底层中使用仍然是原型和基于原型的继承。
51.什么是模板字符串
在 JS 中创建字符串的一种新方法,可以通过使用反引号使模板字符串化。ES6中:
一行:
多行:
在字符串中添加表达式或值:
52. 什么是对象解构?
对象解构是从对象或数组中获取值的一种方法,假设有如下的对象:
从对象获取属性:
还可以为属性取别名:
如果属性值为 undefined 时,可以指定默认值:
53.展开(spread)运算符和剩余(Rest)运算符有什么区别?
展开运算符:’... ’,可以将一个数组转为用逗号分隔的参数序列。展开元素会“展开”数组变成多个元素。
对 nums 数组进行展开,参数 a 的值是 5,b 的值是 6,sum 是 11
剩余运算符:’... ’,用于解构数组和对象。收集多个元素和“压缩”成一个单一的元素
使用剩余操作符提取所有剩余的数组值,将它们放入除第一项之外的其他数组中
54.隐式和显式转换的区别
隐式强制转换是一种将值转换为另一种类型的方法,这个过程自动完成
console.log(1 + '6'); // 16
1被转换成字符串,然后与 + 运算符连接6
console.log(false + true); // 1
将 false 转换为 boolean 值为 0,true 为 1
console.log(6 * '2'); // 12
将 '2' 转换为一个数字,然后6 * 2
显式强制是将值转换为另一种类型的方法,需要手动转换
console.log(1 + parseInt('6'));
使用 parseInt 函数将 '6' 转换为 number,使用 + 运算符将 1 和 6 相加
55. 什么是NaN?以及如何检查值是否为NaN?
NaN 表示“非数字”,该值是将数字转换或执行为非数字值的运算结果
let a;
JS 有一个内置isNaN 方法,用于测试值是否为isNaN值。
所有console.log 语句都返回 true,即使传递的值不是 NaN。在 ES6 中,建议使用 Number.isNaN 方法,因为它会检查该值(如果确实是 NaN)。
在 JS 中,NaN是唯一的值,它不等于自己。
56.判断值是否为数组
使用Array.isArray,如果环境不支持此方法,可以使用 polyfill 实现
还可以使用传统的方法
57.如何在不使用% 模运算符的情况下检查一个数字是否是偶数
使用按位 & 运算符,& 对其操作数进行运算,并将其视为二进制值执行与运算
58.如何检查对象中是否存在某个属性
使用 in 操作符号
使用 hasOwnProperty 方法,返回一个布尔值,指示对象自身属性中是否具有指定的属性
使用括号符号obj["prop"]。如果属性存在,返回该属性的值,否则返回undefined
59. in 运算符和 Object.hasOwnProperty 方法有什么区别
hasOwnPropert() 返回一个布尔值,指示对象自身属性中是否具有指定的属性,因此会忽略那些从原型链上继承的属性。在函数原型上定义一个变量 phone, hasOwnProperty 方法会直接忽略
in 运算符,如果指定的属性在指定的对象或其原型链中,返回 true
60. 函数表达式和函数声明有什么区别
notHoistedFunc 调用抛出异常,hoistedFunc 调用不会,因为 hoistedFunc 会被提升到作用域的顶部,而 notHoistedFunc 不会。
61.什么是缓存及它有什么作用?
缓存是建立一个函数的过程,这个函数能够记住之前计算的结果或值。使用缓存函数是为了避免在最后一次使用相同参数的计算中已经执行的函数的计算。这节省了时间,但也有不利的一面,会消耗更多的内存来保存以前的结果。
62.为什么typeof null 返回 object?如何检查一个值是否为 null?
typeof null == 'object' 返回 true ,因为这是自 JS 诞生以来 null 的实现。
使用严格相等运算符 === 来检查值是否为 null
63.new 关键字的作用
1.创建空对象 {}
2.将空对象分配给 this 值
3.将空对象的 __proto__ 指向构造函数的 prototype
4.如果没有使用显式 return 语句,则返回 this
64.垃圾回收机制
在java中,程序员不需要自己去释放一个对象的内存,而是由虚拟机自行执行。在JVM中,垃圾回收线程是低优先级的,在正常情况下不执行,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫描那些没有被任何引用的对象,将它们添加到要回收的集合中进行回收。
65.location对象的常用属性和方法
什么是location对象?location对象的目的是获得或者设置URL
window对象提供了location,用于获取或设置窗体的URL,还可以用于解析URL。因为这个属性返回的是一个对象,所以将这个属性也称为location对象。
66.typeof 和 instanceof
基础数据类型:undefined,number,boolean,string,symbol,bigint,null
引用数据类型:Object、Function、Array
1.作用点不同:
typeof用来判断基础数据类型,instanceof用来判断引用数据类型。
2.底层逻辑不同:
typeof根据数据在存储单元中的类型标签来判断数据类型,instanceof根据函数的prototype属性值是否存在于对象的原型链上来判断数据类型。
67.webpack 和 treeshaking
Tree-Shaking会在运行过程中静态分析模块之间的导入导出,确定 ESM 模块中哪些导出值未曾被其它模块使用,并将其删除,以此实现打包产物的优化。
Webpack :所有模块的js代码混写在一起非常混乱,不具备代码层次化,难于调试,就进行模块的拆分,由于增加了js文件的数量,所以网页需要发起更多次的http请求,导致了页面加载速度变慢。无法直接在js代码中看出js文件之间相互的存储位置的关系,必须要通过dist.html文件才能查看。js文件必须按规定顺序加载。因此使用webpack进行文件打包,可以解决以上的缺点。
68.数组去重的方法
1.利用ES6中 Set去重
2.利用for双重循环,splice去重(双指针先后遍历数组,看是否有相等,相等则删除后者)
3.利用indexOf去重,新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组
4.利用includes
5.利用filter
69.vuex关键节点
vuex是一个专为 Vue.js 应用程序开发的状态管理模式, 采用集中式存储管理应用的所有组件的状态,解决多组件数据通信
1.state 统一定义管理公共数据
2.mutations: 使用它来修改数据
3.getters: 类似于vue中的计算属性
4.actions: 类似于methods,用于发起异步请求,比如axios
5.modules: 模块拆分
70.axios既可以当函数用,又可以当对象用Eg.axios({ }) 和 axios.get
Axios本质是函数,赋值了一些别名方法可被调用,最终调用的还是axios.prototype.request函数,最终返回的是promise链式调用
71. js中super关键字
super 关键字用于访问和调用一个对象的父亲的函数。。
在构造函数中使用时,super关键字将单独出现,并且必须在使用 this 关键字之前使用。super 关键字也可以用来调用父对象上的函数。
super(arguments); // 调用父构造函数
super.parentMethod(arguments); // 调用父方法