哈喽哈喽,这里是小菜不拖延博主,博主最近开始面试了,刚刚面完小红书,还没出结果先出一篇凉经,总的来说问的不难,但是也不妨碍我不会(sos,平时不注重理论就是这个结果),大抵是凉了的
- 自我介绍
- 项目难点
- 为什么选择做前端
- 其他:问了实验室的一些情况、做的小程序是什么、做的是什么项目(政府的还是企业的)
- 代码题数组去重,还有什么别的实现方法吗越多越好
- src和herf的区别
- script标签执行有几种形式(当时都没听懂这个是干嘛)
- 说一下事件循环
- data为什么是一个函数
- 还有什么其他的优化技术
- call、bind、apply函数的区别
- 强制缓存和协商缓存
ps:由于一个问题补充芝士太多了,我们分成几部分讲叭
代码题
代码题部分没有让我运行通过,直接写完之后问的思路,并且询问我除了当前方法还有哪些方法可以实现的
数组去重
bash
输入:
var arr=[{name:'a',id:1},{name:'b',id:2},{name:'c',id:3},{name:'a',id:4},{name:'c',id:5}]
输出:
[{name:'a',id=1},{name:'b',id=2},{name:'c',id=3}]
解题:
方法一:双重for循环
外层遍历输入数组,进行逐个判断,内层循环判断输出的数组当中是否存在该值,如果存在就break,那么j没有遍历完成输出数组,自然就不等于输出数组长度,所以只有j和输出数组长度相等的时候才输出
javascript
let arr1=[]
let j
for(let i=0;i<arr.length;i++){
for(j=0;j<arr1.length;j++){
if(arr[i]['name']==arr1[j]['name']){
console.log(arr[i]['name'],i);
break
}
}
if(j==arr1.length){
arr1.push(arr[i])
}
}
方法二:利用对象
forEach遍历数组,用name作为键,对象作为值,由于一个键只会对应一个值,所以可以去重
javascript
var obj={}
arr.forEach(item=>{
obj[item.name]=item
})
let arr1=Object.values(obj)
forEach
array.forEach(callback(currentValue, index, array), thisArg)
array
:要遍历的数组。callback
:回调函数,用于对每个元素执行的操作。currentValue
:当前元素的值。index
:当前元素的索引。array
:被遍历的数组。thisArg
(可选):在执行回调函数时,用作this
的值。
js
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(function(number, index) {
console.log(`Element at index ${index} is ${number}`);
});
//输出:
Element at index 0 is 1
Element at index 1 is 2
Element at index 2 is 3
Element at index 3 is 4
Element at index 4 is 5
Obejct
- Obejct.keys:获取属性,返回的值是对象
- Object.values:获取属性的值,返回的是对象
方法三:利用map
- 首先arr.map遍历数组,用item.name作为键,item整一个作为值,newMap生成Map的一个集合
- 获取到Map的值,此时是一个集合,是{{},{}}的形式
- 利用Array.from转换为数组
javascript
let uni=Array.from(new Map(arr.map(item=>[item.name,item])).values())
Array.from
Array.from 是一个数组方法,在 JavaScript 中用于从类数组对象或可迭代对象创建一个新的数组。比如我可以传入一个Set对象,他就可以帮我转成数组 栗子:
js
// 从字符串创建数组
const str = 'Hello';
const arr = Array.from(str);
console.log(arr);
// 输出:["H", "e", "l", "l", "o"]
// 从 Set 创建数组
const set = new Set([1, 2, 3]);
const arr = Array.from(set);
console.log(arr);
// 输出:[1, 2, 3]
// 从 Map 创建数组
const map = new Map([ ['key1', 'value1'], ['key2', 'value2'] ]);
const arr = Array.from(map);
console.log(arr);
// 输出:[["key1", "value1"], ["key2", "value2"]]
map
用于存储键值对的有序集合
- 创建
js
const map = new Map(); // 创建一个空的 Map
const map = new Map([[key1, value1], [key2, value2]]); // 创建并初始化一个 Map
- 添加和删除
js
const map = new Map();
map.set(key1, value1); // 添加一个键值对
map.set(key2, value2).set(key3, value3); // 连续添加多个键值对
map.delete(key2); // 删除一个键值对
map.clear(); // 清空 Map 中的所有键值对
- 获取键值对
js
console.log(map.get(key1)); // 获取指定键对应的值
- 判断是否存在
js
console.log(map.has(key1)); // 判断 Map 中是否存在指定键,输出: true
Set
它是一种无序且唯一的集合。Set 中的元素不允许重复,并且可以存储任何类型的值,包括原始类型和对象引用。
- 创建
js
// 创建一个空的
const set = new Set();
// 创建并初始化一个 Set
Set const set = new Set([1, 2, 3]);
- 获取大小
new Set().size
- 判断set中是否存在:
set.has(2)
,返回的是true/false
注意!!!!!!!!
Set 中的元素是无序的,不像数组有固定的索引,因此不能通过索引进行访问。访问出来是undefined
方法四:利用filter过滤器
注意!!!这个方法和上面的双重for循环的办法都可以保证只保留重复出现的第一个数据,但是map和对象的那两个是不可以的,需要更多的操作
js
var uniqueArr = [];
var uniqueObj = {};
arr.filter(function(item) {
//如果不存在item.name的值
if (!uniqueObj[item.name]) {
//那么我就存入这个键值对,a:true,如果下一次再出现a的话,那么就不会被push进我的输出数组
uniqueObj[item.name] = true;
uniqueArr.push(item);
}
});
src和href的区别
src通常是指定需要嵌入到我文档中的外部资源,图片视频之类的,他是需要下载的,浏览器解析到src属性的时候,会暂停解析,直到下载解析完成当前的资源,才会继续解析。而href是超文本引用,像一些a标签link会用到,它是建立文档与外部资源之间的关联,一个是嵌入一个是关联,我用a标签只是会跳转,只是指向了一个资源地址,所以不会影响页面的解析。
script标签
由于这部分我没弄懂是问什么,那就直接把script标签复习了吧!
引入js
内联式
直接在script标签当中写js代码
markdown
如果在script标签在head当中,那么浏览器执行顺序就是:
1. 暂停 DOM 解析过程
2. 执行 JS 代码
3. 执行完成,继续 DOM 解析过程
如果在body当中写就是
外联式
markdown
script标签在head标签当中
1. 暂停 DOM 解析过程
2. 加载 a.js
3. 加载完成后,开始执行该脚本
4. 执行完成,继续 DOM 解析过程
在上面所提到的方式当中,我们会发现dom的解析过后曾都会被打断,也就是同步阻塞 ,如果我们的js脚本过大的话,那我们的解析打断时间就会很长,也就是说用户会看到页面空白,也就是白屏现象,所以解决办法是将script标签插入在body底部,就可以让浏览器先解析script标签之前的内容,再来执行脚本,就能很好的解决白屏问题。
但是要是能解析dom和加载js过程并行,那么就能省下部分时间,也就出现了下面的
defer/async
这个是为了解析dom和加载js同步并行
defer延迟加载
这个属性可以让浏览器在解析dom的同时加载js,但是只有在解析dom完成之后才会执行js脚本
html
<html>
<head>
<script defer src="./a.js"></script>
</head>
</html>
async异步加载
浏览器解析dom的同时加载js,加载完成并立即执行js(也就是会打断dom解析)
async和defer同时出现的话:async
的优先级会比defer
高,因此defer
将不会生效
动态引入
通过 innerHTML 方式其实也能添加 script 标签,只是该标签下的 JS 不会运行
js
var myScript = document.createElement("script");
myScript.textContent = 'alert("✋")';
document.head.appendChild(myScript);
es modules模块化脚本
通过指定
type="module"
来声明一个外部脚本是一个es
模块,默认是defer的
js
<script type="module" src="./a.mjs"></script>
// a.mjs
import b from "./b.mjs"
import c from "./c.mjs"
浏览器加载
a.mjs
之后,还需要继续加载b.mjs
和c.mjs
。只有所有这些依赖模块都分析加载完毕,才能开始执行其中的js
脚本,而且根据根据es modules
的规则,浏览器会先执行b.mjs
中的代码,然后是c.mjs
,最后才是a.mjs
的代码。
nomodule
es modules
的浏览器,第一个脚本不会被执行,第二个脚本会被执行。
js
<script type="module" src="main.js"></script>
<script nomodule src="fallback.js"></script>
阅读参考: