1、判断变量类型的几种方法(还细问了其它的忘了)
添加链接描述
2、作用域和作用域链
3、遍历对象的方法,区别
添加链接描述
添加链接描述
4、箭头函数和普通函数的区别
摘自ES6标准入门)①(重点)函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象。通常this指向是可变的,但在箭头函数中它是固定的。②不可以当作构造函数。也就是说不能使用new命令,否则会报错。③不可以使用arguments对象,该对象在函数体内不存在。如果要用可以用rest参数代替。④不可以使用yield命令,因此箭头函数不能用作Generator函数。
5、垃圾回收机制
6、异步
7、数组去重,set为什么不可重复原理
8、== 和===
9、var,let,const的区别
同一个作用域下,var可以重复声明变量,let、const不能重复声明变量。
const 声明的变量是不可以改变的,声明的基本数据类型不可改,引用类型可改属性,不可只声明变量而不赋值
10、跨域
11、async和defer
12、https
13、用css画个三角形
14、元素垂直居中
15、vue-router的两种模式以及区别
16、vue响应式原理
17、防抖和节流
18、问浏览器输入 url 到页面显示全过程
19、如何遍历对象的值
20、for of 能遍历对象吗
21、for in 和 for of 的区别
22、for in 的缺点
for-in用来循环对象中的属性,但是通过for-in循环输出的属性名的顺序是不可测的。具体来说,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。
ES5之前:如果表示要迭代的对象的变量值为null或undefined,for-in语句会抛出错误;
ES5之后:这种情况不再抛出错误,而只是不执行循环体。
for-in会遍历原型链上的属性
23、同步任务与异步任务的区别
添加链接描述
24、 Array.map some forEach区别
添加链接描述
25、三次握手
添加链接描述
26、http和https
27、Promise.all和Promise.race
添加链接描述
- Promise.all 将多个实例封装成一个新的实例,如果多个实例都是成功的实例,返回成功结果的数组,如果多个实例中有失败的实例,就返回最先失败实例的结果。
- Promise.race 返回多个实例最先执行完的结果,无论它是成功的还是失败的。
28、js垃圾回收机制
29、 git常用命令,解释一下rebase作用
30、前端性能优化
- 伪类、伪元素
- 盒模型、 box-sizing
- BFC与清除浮动
- 选择器优先级
- min-width、max-width、width的包含(优先级关系)关系
- min-width和max-width的权重高于width,当width的宽度小于min-width的时,将显示min-width的宽度,当width的宽度大于max-width的时,将显示max-width的宽度,
- min-width和max-width同时存在,并且min-width的值大于max-width的值,将显示min-width的宽度。
- 输入URL到渲染页面的全过程
- 8中哪些阶段可以优化提升效率
- 强缓存、协商缓存发生在8中的哪些阶段
- CDN
- TLS/SSL
- vue router 和 route的区别
- vue单向数据流的特点、vueX使用方式
- es6 set和map特点和区别,set和weakset,map和weakmap
添加链接描述
set的结构类似于数组,成员都是不重复的,map是键值对的集合。
set用于数组去重,map用于数据存储。
相同点:WeakSet结构与Set类似,成员都是不重复的
WeakSet的成员只能是对象,而不能是其他类型的值
WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。这个特点意味着,无法引用WeakSet的成员,因此WeakSet是不可遍历的。
WeakMap与Map类似,但有几点区别:
1、WeakMap只接受对象作为key,如果设置其他类型的数据作为key,会报错。
2、WeakMap的key所引用的对象都是弱引用,只要对象的其他引用被删除,垃圾回收机制就会释放该对象占用的内存,从而避免内存泄漏。
3、由于WeakMap的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性。
4、没有clear()方法
5、不能遍历
- map和对象的区别
Object 的键只能是字符串或者 Symbols,Map 的键可以是任意值。
添加键值对的时候,Map的键值是有序的(FIFO 原则),而对象的键值是无序的。
Map的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。 - 箭头函数和普通函数的区别
- js异步处理发展史
- async await 原理
- 手写题:Promise.all
1.自我介绍
2.怎么去理解HTML,CSS,JS,交互的话是怎么实现的呢?
3.怎么去获取后端的数据,axios库是什么?跨域要怎样实现?(我说了后端要设置响应头,他说这样的话就不能上线了,如果后端关掉了怎么办,是变相问前端存储?然后我就说了cookie,但cookie也不安全)
4.cookie、localstorage、sessionstorage的区别
5.如果想要将本地存储就是localstorage、sessionstorage发送到后端呢?如何获取以及发送?
6.如何用一个div实现一个圆圈⭕?如何触发事件后让这个⭕弹出来,覆盖在其他元素上面
7.事件冒泡是怎样的?他的好处是什么?jQuery用它实现了什么东西?
8.用jQuery获取Dom节点
9.attr是什么,用法
10.数组的增删查改,concat是什么
11.对ES6中let和const的理解;const变量如果是一个对象的话是可变的,为什么?
12.有用过promise吗?怎么理解它?
12.为什么会选择前端?
-
个人介绍
-
介绍一下在实验室做了些什么
-
除了flex做响应式布局,还有别的方案吗
(教程 https://juejin.cn/post/6844903814332432397 )
-
介绍一下你了解的flex
(教程 http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html)
-
css实现垂直水平居中,尽可能多的方案
-
问我更擅长哪一方面,他就问什么(太好了吧😭 说了css和js)
-
元素隐藏的方法
我:display: none、visibility: hidden、opacity: 0, z-index,
面试官: position: absolute移到可视区域外、缩放 transform:scale(0),
面完后我搜到的:文本缩进属性text-indent: -999px、lip-path: polygon(0 0, 0 0, 0 0, 0 0)、height: 0; overflow: hidden;
-
什么是重绘和重排,怎么减少
(教程 https://www.chenhanpeng.com/reflow-and-repaint/)
-
怎么引起重绘和重排
答:样式改变触发重绘(这个不太对,所以面试官又接着问了) dom结构改变引起重排(应该没答完整所以继续追问了)
- 单纯改变样式会引起重排吗,比如padding和margin
答:会引起
- 哪些样式改变会引起重绘,哪些会引起重排
答:调整字体大小、窗口大小、样式改变(我仿佛脑抽了用问题回答问题,其实我想说颜色这种样式的改变,被抓住了这一点,见12)会引起重排
- 所有样式改变都会引起重排吗
答:改颜色只会引起重绘
- 缩小元素怎么不引起重排,比如10px * 10px 变5px * 5px;
答:一紧张说了个translation??给面试官逗笑了55555
面试官:translate / transform确实不影响真实占位不会重排,但是改位置和改尺寸会引起重排,因为要重新计算div的位置大小,只要不改div在整个布局的位置和尺寸就不引起重排,比如你刚说的改变字体大小和窗口大小就会引起重排。
我主动补充了opacity:0和position: absolute/fixed脱标可以减少重排
面试官:先绝对定位掉,等全部渲染完再(什么什么听不清了)减少重排的次数
-
promise解决了什么问题(回调地狱)
-
promise的方法
说了then, catch, 面试官补充了all race
(教程 https://www.jianshu.com/p/d8a901dd72ac)
- 对算法和数据结构有信心吗(我没有哈哈哈)
面试官是css组的(耶!躲过一劫! )但是说了需要去了解算法思想
考考正则,命名转小驼峰
可能的输入:TestVal,test_val, TEST_VAL ,要求实现驼峰命名的输出
- css选择器考察
-
获取当前页面的所有dom节点
document.querySelectorAll(“*”)
2) 获取当前页面的所有p节点
document.querySelectorAll(“p”)
3) 获取当前页面带有data-click属性的节点,color设为白色
var x = document.querySelectorAll(“[data-click]”);
var i;
for (i = 0; i < x.length; i++) {
x[i].style.color = "white";
}
- 伪类和伪元素,知道哪些,有什么区别
(教程https://blog.csdn.net/qq_27674439/article/details/90608220) - 反问
-
看中面试者的那一方面(只要有一个方面够突出就行,了解的多并有专精能力)
-
公司业务
https 和 http 的区别,对称加密和非对称加密的了解
- http:超文本传输协议,是互联网上应用最为广泛的一种网络协议,建立在 TCP 之上,用于从服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
- https:是以安全为目标的 http 通道,即 http 和 tcp 之间加入 ssl 层。它的主要作用是:建立一个信息安全通道,来确保数据的传输,确保网站的安全性
- 对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key)。对称加密有很多种算法,由于它效率很高,所以被广泛使用在很多加密协议的核心当中
- 非对称加密为数据的加密与解密提供了一个非常安全的方法,它使用了一对密钥,公钥(public key)和私钥(private key)。私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则需要另一个密钥。比如,你向银行请求公钥,银行将公钥发给你,你使用公钥对消息加密,那么只有私钥的持有人–银行才能对你的消息解密。与对称加密不同的是,银行不需要将私钥通过网络发送出去,因此安全性大大提高。
TCP 和 UDP
- 传输控制协议(TCP):TCP(传输控制协议)定义了两台计算机之间进行可靠的传输而交换的数据和确认信息的格式,以及计算机为了确保数据的正确到达而采取的措施。协议规定了 TCP 软件怎样识别给定计算机上的多个目的进程如何对分组重复这类差错进行恢复。协议还规定了两台计算机如何初始化一个 TCP 数据流传输以及如何结束这一传输。TCP 最大的特点就是提供的是面向连接、可靠的字节流服务。
- 用户数据报协议(UDP):UDP(用户数据报协议)是一个简单的面向数据报的传输层协议。提供的是非面向连接的、不可靠的数据流传输。UDP 不提供可靠性,也不提供报文到达确认、排序以及流量控制等功能。它只是把应用程序传给 IP 层的数据报发送出去,但是并不能保证它们能到达目的地。因此报文可能会丢失、重复以及乱序等。但由于 UDP 在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
变量提升和函数提升
- 变量提升 – 是指变量提升到他的作用域最开始的部分。声明之前可以被访问到
- 函数提升-- 是指通过function形式声明的函数才存在函数提升。声明之前可以被访问到
- 函数提升优先级高于变量提升,但是函数提升会被变量赋值覆盖掉。
- 变量提升和函数提升
isNaN 与 Number.isNaN的区别?
- isNaN() 会进行隐式转换,它会将参数看看能不能转换数字,如果可以,返回false,如果不可以返回true
- Number.isNaN 不存在隐式转换,如果参数不是一个数字返回true,其余情况返回false。
JavaScript变量在内存中具体存储形式?
- 基本数据类型:存在栈内存里
- 引用数据类型:指针存栈内存,指向堆内存中一块地址,内容存在堆内存中
讲一讲JavaScript的装箱和拆箱?
- 装箱:把基本数据类型转化为对应的引用数据类型的操作
看以下代码,s1只是一个基本数据类型,他是怎么能调用indexOf的呢?
const s1 = 'Sunshine_Lin'
const index = s1.indexOf('_')
console.log(index) // 8
原来是JavaScript内部进行了装箱操作
1、创建String类型的一个实例;
2、在实例上调用指定的方法;
3、销毁这个实例;
var temp = new String('Sunshine_Lin')
const index = temp.indexOf('_')
temp = null
console.log(index) // 8
- 拆箱:将引用数据类型转化为对应的基本数据类型的操作
通过valueOf或者toString方法实现拆箱操作
var objNum = new Number(123);
var objStr =new String("123");
console.log( typeof objNum ); //object
console.log( typeof objStr ); //object
console.log( typeof objNum.valueOf() ); //number
console.log( typeof objStr.valueOf() ); //string
console.log( typeof objNum.toString() ); // string
console.log( typeof objStr.toString() ); // string
null和undefined的异同点有哪些?
- 都是空变量
- 都是假值,转布尔值都是false
- null == undefined 为 true 不同点
- typeof判断null为object,判断undefined为undefined
- null转数字为0,undefined转数字为NaN
- null是一个对象未初始化,undefined是初始化了,但未定义赋值
- null === undefined 为 false
JavaScript的隐式转换规则
-
1、转成string类型:+(字符串连接符)
-
2、转成number类型:++/–(自增自减运算符) + - * / %(算术运算符) > < >= <= == != === !=== (关系运算符)
-
3、转成boolean类型:!(逻辑非运算符)
双等号左右两边的转换规则?
-
1、null == undefined 为 true
-
1、如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1;
-
2、如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
-
3、如果一个操作数是对象,另一个操作数不是,则调用对象的toString()方法,用得到的基本类型值按照前面的规则进行比较
valueOf 与 toString 的区别
- 1、valueOf偏向于运算,toString偏向于显示
- 2、对象转换时,优先调用toString
- 3、强转字符串优先调用toString,强转数字优先调用valueOf
- 4、正常情况下,优先调用toString
- 5、运算操作符情况下优先调用valueOf
null和undefined的异同点有哪些?
相同点
- 都是空变量
- 都是假值,转布尔值都是false
不同点 - null == undefined 为 true 不同点
- typeof判断null为object,判断undefined为undefined
- null转数字为0,undefined转数字为NaN
- null是一个对象未初始化,undefined是初始化了,但未定义赋值
- null === undefined 为 false
JavaScript的隐式转换规则?
-
1、转成string类型:+(字符串连接符)
-
2、转成number类型:++/–(自增自减运算符) + - * / %(算术运算符) > < >= <= == != === !=== (关系运算符)
-
3、转成boolean类型:!(逻辑非运算符)
undefined >= undefined 为什么是 false ?
按照隐式转换规则,可转换成NaN >= NaN,NaN 不等于 NaN,也不大于,所以是false
null >= null 为什么是 true?
按照隐式转换规则,可转换成0 >= 0,0 等于 0,所以是true
[] == ![] 为什么是 true ?
按照双等号左右两边的转换规则
- 1、! 优先级高于 ==,[]不是假值,所以先转换成 [] == false
- 2、右边为布尔值,false先转数字0,所以可转换为[] == 0
- 3、左边为对象,[]调用toString转为 ‘’,转换为’’ == 0
- 4、左边为字符串,''转换为0,最终为 0 == 0
0.1 + 0.2 === 0.3,对吗?
不对,JavaScript的计算存在精度丢失问题
console.log(0.1 + 0.2 === 0.3) // false
- 原因:JavaScript中小数是浮点数,需转二进制进行运算,有些小数无法用二进制表示,所以只能取近似值,所以造成误差
- 解决方法:
- 先变成整数运算,然后再变回小数
- toFixed() 性能不好,不推荐
什么是匿名函数?
匿名函数:就是没有函数名的函数,如:
(function(x, y){
alert(x + y);
})(2, 3);
这里创建了一个匿名函数(在第一个括号内),第二个括号用于调用该匿名函数,并传入参数。
绑定点击事件有几种方式?
三种
- xxx.onclick = function (){}
<xxx onclick=""></xxx>
- xxx.addEventListence(‘click’, function(){}, false)
addEventListence的第三个参数是干嘛的?
第三个参数是个布尔类型,用于事件在那个阶段执行?,true表示该事件在捕获阶段执行,false表示在冒泡阶段执行。
函数声明和函数表达式的区别?
- 函数声明:享受函数提升
- 函数表达式:归类于变量声明,享受变量提升
- 函数提升优先级 > 变量提升优先级
console.log(fun) // fun () {}
// 函数表达式
var fun = function(name) {}
// 函数声明
function fun () {}
console.log(fun) // fun (name) {}
JavaScript的事件流模型有哪些?
- 事件冒泡:由最具体的元素接收,并往上传播
- 事件捕获:由最不具体的元素接收,并往下传播
- DOM事件流:事件捕获 -> 目标阶段 -> 事件冒泡
Ajax、Axios、Fetch有啥区别?
- Ajax:是对XMLHttpRequest对象(XHR)的封装
- Axios:是基于Promise对XHR对象的封装
- Fetch:是window的一个方法,也是基于Promise,但是与XHR无关,不支持IE
load、$(document).ready、DOMContentLoaded的区别?
DOM文档加载的步骤为:
- 1、解析HTML结构。
- 2、加载外部脚本和样式表文件。
- 3、解析并执行脚本代码。
- 4、DOM树构建完成。// DOMContentLoaded触发、$(document).ready触发
- 5、加载图片等外部文件。
- 6、页面加载完毕。// load触发
如何阻止事件冒泡?
使用 e.stopPropagation() / return false
function stopBubble(e) {
if (e.stopPropagation) {
e.stopPropagation()
} else {
window.event.cancelBubble = true;
}
}
如何阻止事件默认行为?
使用 e.preventDefault() / return false
function stopDefault(e) {
if (e.preventDefault) {
e.preventDefault();
} else {
window.event.returnValue = false;
}
}
什么是事件委托?
当所有子元素都需要绑定相同的事件的时候,可以把事件绑定在父元素上。这就是事件委托,比如: ul元素下有很多li元素,为每个li元素绑定点击事件,这个时候只需要给ul元素绑定点击事件就可以了,事件委托的原理是通过事件冒泡来实现的。
优点: 使用事件委托可以节省大量的内存空间,节省性能,这是因为,事件是绑定到父元素上,而且只需要绑定一次,还可以动态绑定事件,如果后续又有新的子元素添加,会由于事件委托的原因,自动接收到父元素的事件监听。
如何实现数组去重?
- 双重for循环
- indexOf
- sort方法+相邻元素
- object键值对
- set方法
- map方法
// 使用 Map 去重
function quchong1(arr) {
const newArr = []
arr.reduce((pre, next) => {
if (!pre.get(next)) {
pre.set(next, 1)
newArr.push(next)
}
return pre
}, new Map())
return newArr
}
// 使用 Set 去重
function quchong (arr) {
return [...new Set(arr)]
}
NaN是什么?有什么特点?
NaN表示它不是一个数字,是表示本来要返回数值的操作
失败了。比如,用 0 除任意数值在其他语言中通常都会导致错误,而在js中返回NaN,NaN它不等于任何值,即使是它本身,NaN转换布尔值是false。NaN本身得类型是number类型,可以通过typeof进行判断。
处理异步的方法有哪些?
- 回调函数
- promise
- 事件监听
- 发布订阅
- async await
创建一个对象的方式有哪几种?
- new Object创建
const obj = new Object()
obj.name = 'Sunshine_Lin'
- 字面量创建
const obj = { name: 'Sunshin_Lin' }
- 工厂模式创建
function createObj(name) {
const obj = new Object()
obj.name = name
return obj
}
const obj = createObj('Sunshine_Lin')
- 构造函数创建
function Person(name) {
this.name = name
}
const person = new Person('Sunshine_Lin')
数组的常用方法有哪些?
Math的常用方法有哪些?
JS中有哪些不同类型的弹出框?
-
Alert 警告框
-
Confirm 确认框
-
Prompt 提示框
如何将 JS 日期转换为ISO标准
toISOString() 方法用于将js日期转换为ISO标准。它使用ISO标准将js Date对象转换为字符串。如:
var date = new Date();
var n = date.toISOString();
console.log(n);
// YYYY-MM-DDTHH:mm:ss.sssZ
如何在JS中编码和解码 URL
encodeURI()和encodeURIComponent()用于对URL的编码,它们主要的区别是,encodeURI无法编码url中的非标准字符,而encodeURIComponent是可以进行编码的,与它们对应的解码方法是decodeURI() 和decodeURIComponent()
什么是BOM?有哪些api?
BOM 是浏览器对象模型,是把浏览器当中对象来看待,BOM的核心是window对象,它是global对象,也是浏览器的js接口
JS中的substr()和substring()函数有什么区别?
- substr() 函数的形式为substr(startIndex,length)。它从startIndex返回子字符串并返回’length’个字符数。
var s = "hello";
( s.substr(1,4) == "ello" ) // true
- substring() 函数的形式为substring(startIndex,endIndex)。它返回从startIndex到endIndex - 1的子字符串。
var s = "hello";
( s.substring(1,4) == "ell" ) // true
解释一下 “use strict” ?
“use strict”是Es5中引入的js指令。使用“use strict”指令的目的是强制执行严格模式下的代码。在严格模式下,咱们不能在不声明变量的情况下使用变量。早期版本的js忽略了“use strict”。
深拷贝与浅拷贝的区别?
-
深拷贝层层拷贝,浅拷贝只拷贝第一层,深层只是引用
-
在深拷贝中,新对象中的更改不会影响原始对象,而在浅拷贝中,新对象中的更改,原始对象中也会跟着改。
-
在深拷贝中,原始对象不与新对象共享相同的属性,而在浅拷贝中,它们具有相同的属性。
获取对象原型的方式有哪些?
- 对象.proto 通过对象的隐式原型属性
- 构造函数.prototype 通过构造函数的显示原型属性
- Object.getPrototypeOf(对象) 通过Object对象的getPrototypeOf方法
原型链是什么呀?详细点!
掘金讲「原型链」,讲的最好最通俗易懂的
什么是原型对象? 实例对象有个隐式原型属性指向的一个对象就是原型对象。
什么是原型链呢?其实俗话说就是:__proto__的路径就叫原型链
函数的长度length是多少?
length 是函数对象的一个属性值,指该函数有多少个必须要传入的参数,即形参的个数。形参的数量不包括剩余参数个数,仅包括第一个具有默认值之前的参数个数
函数的长度length是多少
面试题:
console.log(123['toString'].length + 123) // 124
:123是数字,数字本质是new Number(),数字本身没有toString方法,则沿着__proto__去function Number()的prototype上找,找到toString方法,toString方法的length是1,1 + 123 = 124,至于为什么length是1,可以看95%的人都回答不上来的
解决遍历对象时,把原型上的属性遍历出来了咋办?
使用hasOwnProperty判断
function Person(name) {
this.name = name
}
Person.prototype.age = 23
const person = new Person('Sunshine_lin')
for (const key in person) { console.log(key) } // name age
// 使用 hasOwnProperty
for (const key in person) {
person.hasOwnProperty(key) && console.log(key)
} // name
Set与Array的区别是什么?
- API不同
- set的成员的惟一的,不重复的
- set是无序结构,操作很快
JavaScript继承方式有几种?
前置工作
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
原型链继承
核心:将父类的实例作为子类的原型
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
var cat = new Cat();
console.log(cat.name); // cat
cat.eat('fish') // cat正在吃:fish
cat.sleep() // cat正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
优点:
- 1、非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 2、父类新增原型方法/属性,子类都能访问到
- 3、简单,易于实现
缺点: - 1、要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放构造器中
- 2、来自原型对象的所有属性被所有实例共享
- 3、创建子实例时,无法向父类构造函数传参
- 4、不支持多继承
构造继承
核心:使用父类的构造器来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(name) {
Animal.call(this);
this.name = name || 'Tom';
}
var cat = new Cat();
console.log(cat.name); // Tom
cat.sleep() // Tom正在睡觉!
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
优点:
- 1、解决了原型链继承中,子类实例共享父类引用属性的问题
-2、创建子类实例时,可以向父类传递参数 - 3、可以实现多继承(call多个父类对象)
缺点: - 1、实例并不是父类的实例,知识子类的实例
- 2、是能继承父类的实例属性和方法,不能继承原型属性/方法
- 3、无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
实例继承
核心:为父类实例添加新特性,作为子类实例返回
function Cat(name){
var instance = new Animal();
instance.name = name || 'Tom';
return instance;
}
var cat = new Cat();
console.log(cat.name) // Tom
cat.sleep() // Tom正在睡觉!
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false
优点:
- 1、不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同效果
缺点: - 1、实例是父类的实例,不是子类的实例
- 2、不支持多继承
拷贝继承
核心:就一个一个拷贝
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
this.name = name || 'Tom';
}
var cat = new Cat();
console.log(cat.name); // Tom
cat.sleep() // Tom正在睡觉!
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
优点:
- 1、支持多继承
缺点: - 1、效率低,内存占用高(因为要拷贝父类的属性)
- 2、无法获取父类不可枚举方法(不可枚举方法,不能使用for in访问到)
5、组合继承
核心:通过父类构造,继承父类的属性并保留传参的有点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat = new Cat();
console.log(cat.name); // Tom
cat.sleep() // Tom正在睡觉!
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
优点:
- 1、弥补了构造继承的缺陷,可以继承实例属性/方法,也可继承原型属性/方法
- 2、既是子类的实例,也是父类的实例
- 3、不存在引用属性共享问题
- 4、可传参
- 5、函数可复用
缺点: - 1、调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
寄生组合继承
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造时,就不会初始化两次实例方法/属性,避免继承组合的缺点
function Cat(name) {
Animal.call(this);
this.name = name || 'Tom';
}
// 创建一个没有实例方法的类
var Super = function () { };
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
// Test Code
var cat = new Cat();
console.log(cat.name); // Tom
cat.sleep() // Tom正在睡觉!
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
优点:
1、堪称完美
缺点:
1、实现复杂
this指向的四种情况?this的指向 哪几种?
默认绑定:全局环境中,this默认绑定到window。
隐式绑定:一般地,被直接对象所包含的函数调用时,也称为方法调用,this隐式绑定到该直接对象。
隐式丢失:隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到window。显式绑定:通过call()、apply()、bind()方法把对象绑定到this上,叫做显式绑定。
new绑定:如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。对于this绑定来说,称为new绑定。
【1】构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值。
【2】如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果。
【3】如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象。
讲讲JavaScript的垃圾回收机制
哪些因素导致内存泄漏?如何解决?
1
获取DOM元素有哪些方法
操作DOM元素有哪些方法
DOM的类型有哪几种?
12种
元素节点 Node.ELEMENT_NODE(1)
属性节点 Node.ATTRIBUTE_NODE(2)
文本节点 Node.TEXT_NODE(3)
CDATA节点 Node.CDATA_SECTION_NODE(4)
实体引用名称节点 Node.ENTRY_REFERENCE_NODE(5)
实体名称节点 Node.ENTITY_NODE(6)
处理指令节点 Node.PROCESSING_INSTRUCTION_NODE(7)
注释节点 Node.COMMENT_NODE(8)
文档节点 Node.DOCUMENT_NODE(9)
文档类型节点 Node.DOCUMENT_TYPE_NODE(10)
文档片段节点 Node.DOCUMENT_FRAGMENT_NODE(11)
DTD声明节点 Node.NOTATION_NODE(12)
数组的splice 与 slice 的区别?
substr 和 substring 的区别?
includes 比 indexOf好在哪?
includes可以检测NaN,indexOf不能检测NaN,includes内部使用了Number.isNaN对NaN进行了匹配
什么是async/await?解决了什么问题?
对于async/await,我总结为一句话async/await是generator + Promise的语法糖,它用同步方式执行异步代码
JS延迟加载的方法有哪些?
- 1、
<script async src="script.js"></script>
:给script标签加async属性,则加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步) - 2、
<script defer src="script.js"></script>
:给script标签加defer属性,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成 - 3、动态创建script标签:等到DOMContentLoaded 事件触发时,生成一个script标签,渲染到页面上上
- 4、setTimeout定时器延迟代码执行
new操作符为什么能创建一个实例对象?
分析一下new的整个过程:
- 1、创建一个空对象
- 2、继承构造函数的原型
- 3、this指向obj,并调用构造函数
- 4、返回对象
简单实现一下new:
function myNew (fn, ...args) {
// 第一步:创建一个空对象
const obj = {}
// 第二步:继承构造函数的原型
obj.__proto__ = fn.prototype
// 第三步:this指向obj,并调用构造函数
fn.apply(obj, args)
// 第四步:返回对象
return obj
}
什么是文档碎片?
- 是什么:一个容器,用于暂时存放创建的dom元素,使用document.createDocumentFragment()创建
- 有什么用:将需要添加的大量元素 先添加到文档碎片 中,再将文档碎片添加到需要插入的位置,大大减少dom操作,提高性能 例子
var oFragmeng = document.createDocumentFragment();
for(var i=0;i<10000;i++)
{
var op = document.createElement("span");
var oText = document.createTextNode(i);
op.appendChild(oText);
//先附加在文档碎片中
oFragmeng.appendChild(op);
}
//最后一次性添加到document中
document.body.appendChild(oFragmeng);
宏任务与微任务有哪些?
宏任务
微任务
JS执行机制
其实不难,JavaScript代码执行机制,我就归结为三句话
- 1、遇到同步代码直接执行
- 2、遇到异步代码先放一边,并且将他回调函数存起来,存的地方叫事件队列
- 3、等所有同步代码都执行完,再从事件队列中把存起来的所有异步回调函数拿出来按顺序执行
请看以下例子
console.log(1) // 同步
setTimeout(() => {
console.log(2) // 异步
}, 2000);
console.log(3) // 同步
setTimeout(() => {
console.log(4) // 异步
}, 0);
console.log(5) // 同步
输出 : 1 3 5 4 2
宏任务与微任务的执行顺序?说说EventLoop?
setTimeout+Promise+Async输出顺序?很简单呀!
Object.defineProperty(target, key, options),options可传什么参数?
- value:给target[key]设置初始值
- get:调用target[key]时触发
- set:设置target[key]时触发
- writable:规定target[key]是否可被重写,默认false
- enumerable:规定了key是否会出现在target的枚举属性中,默认为false
- configurable:规定了能否改变options,以及删除key属性,默认false,具体详细请看Object.defineProperty函数的configurable配置
什么是防抖?什么是节流?
什么是高阶函数?简单实现一个?
高阶函数:英文叫Higher-order function。JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
// 简单的高阶函数
function add(x, y, f) {
return f(x) + f(y);
}
//用代码验证一下:
add(-5, 6, Math.abs); // 11
像数组的map、reduce、filter这些都是高阶函数
什么是函数柯里化?简单实现一个?
柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
// 普通的add函数
function add(x, y) {
return x + y
}
// Currying后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
好处
- 1、参数复用
// 正常正则验证字符串 reg.test(txt)
// 普通情况
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true
// Currying后
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
2、延迟执行 其实Function.prototype.bind就是科里化的实现例子
function sayKey(key) {
console.log(this[key])
}
const person = {
name: 'Sunshine_Lin',
age: 23
}
// call不是科里化
sayKey.call(person, 'name') // 立即输出 Sunshine_Lin
sayKey.call(person, 'age') // 立即输出 23
// bind是科里化
const say = sayKey.bind(person) // 不执行
// 想执行再执行
say('name') // Sunshine_Lin
say('age') // 23
Symbol的应用场景?.
应用场景1:使用Symbol来作为对象属性名
平常我们对象的属性都是字符串
const obj = {
name: 'Sunshine_Lin',
age: 23
}
console.log(obj['name']) // 'Sunshine_Lin'
console.log(obj['age']) // 23
其实也可以用Symbol来当做属性名
const gender = Symbol('gender')
const obj = {
name: 'Sunshine_Lin',
age: 23,
[gender]: '男'
}
console.log(obj['name']) // 'Sunshine_Lin'
console.log(obj['age']) // 23
console.log(obj[gender]) // 男
但是Symbol作为属性的属性不会被枚举出来,这也是JSON.stringfy(obj)时,Symbol属性会被排除在外的原因
console.log(Object.keys(obj)) // [ 'name', 'age' ]
for(const key in obj) {
console.log(key) // name age
}
console.log(JSON.stringify(obj)) // {"name":"Sunshine_Lin","age":23}
其实想获取Symbol属性也不是没办法。
// 方法一
console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol(gender) ]
// 方法二
console.log(Reflect.ownKeys(obj)) // [ 'name', 'age', Symbol(gender) ]
应用场景2:使用Symbol来替代常量
有以下场景
// 赋值
const one = 'oneXin'
const two = 'twoXin'
function fun(key) {
switch (key) {
case one:
return 'one'
break;
case two:
return 'two'
break;
}
}
如果变量少的话还好,但是变量多的时候,赋值命名很烦,可以利用Symbol的唯一性
const one = Symbol()
const two = Symbol()
应用场景3:使用Symbol定义类的私有属性
以下例子,PASSWORD属性无法在实例里获取到
class Login {
constructor(username, password) {
const PASSWORD = Symbol()
this.username = username
this[PASSWORD] = password
}
checkPassword(pwd) { return this[PASSWORD] === pwd }
}
const login = new Login('123456', 'hahah')
console.log(login.PASSWORD) // 报错
console.log(login[PASSWORD]) // 报错
console.log(login[PASSWORD]) // 报错
AMD 和 CMD 的区别?
为什么Vue3用Proxy?
当我们使用push/unshift方法对数组增加元素的时候,Object.defineProperty是无法监听这个新增元素的,当我们对对象增加新属性的时候,Object.defineProperty 也是无法监听这个新增属性的。所以要vue3.0使用proxy
Object.defineProperty 和proxy的区别
- Object.defineProperty劫持的对象的属性,而proxy代理的是整个对象
- Object.defineProperty需要通过遍历来对对象的每个属性进行劫持,而proxy是不用的
- Object.defineProperty对新增的属性需要手动进行observe(劫持),例如:
$set``$delete
方法
为什么object.defineProperty不对数组进行劫持?
- 对象的属性通常比较少,对每一个属性都劫持set和get,并不会消耗很多性能
- 数组有可能有成千上万个元素,如果每一个元素都劫持set和get,无疑消耗太多性能了
- 所以对象通过defineProperty进行正常的劫持set和get
- 数组则通过修改数组原型上的部分方法,来实现修改数组触发响应式
Object.defineProperty 缺点?
- 对象新增或者删除的属性无法被 set 监听到 只有对象本身存在的属性修改才会被劫持
拦截器用法:
-
1、动态设置header,身份认证信息;日志记录;显示loading;
-
2、统一处理请求异常;权限校验;重连登录态;网络状态管理;