是的,还是没有合适的工作,焯,我就不信了。
1、rem和em单位的区别
本人回答:
rem是相对于根元素html上的font-size的大小,em是相对于当前元素的直接父元素的大小。
搜索一手:
- rem:
rem
是相对于根元素(通常是<html>
元素)的字体大小的单位。这意味着,无论页面上的元素嵌套有多深,1rem
始终等于根元素<html>
的font-size
属性值。浏览器默认的字体大小通常是16px
,因此如果未对<html>
的font-size
进行更改,1rem
就等于16px
。 - em:
em
是相对于其直接父元素的字体大小的单位。如果父元素的字体大小是16px
,那么子元素中1em
就等于16px
。在嵌套结构中,em
单位会继承并受到父元素字体大小的影响,因此在嵌套较深的元素中,字体大小可能会逐级累加。
2、如何实现将"I am a developer" 转换成 "developer a am I"
const a = "I am a developer"
console.log(a.split(" ").reverse().join(" "))
3、 如何实现如下布局,在PC端和IPad端显示一行三列,在移动端显示三行一列,请给出html以及主要的css代码
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
@media (min-width: 769px) and (max-width: 1024px){
.container {
display:flex;
justify-content: space-between;
}
}
@media(max-width: 768px){
.container {
display:flex;
flex-direction:column;
}
}
4、怎么判断一个js变量是否是对象,并且是否是空对象
本人回答:
function judge(target) {
return target instanceof Object && !Object.keys(target).length
}
上述回答没得分,因为如果target是个数组,target instanceof Object 返回的结果也会为true。
同时如果该变量是基础数据类型并且是通过构造函数显示创建,那么instanceof也会返回为true。在之前的文章记录过这个问题,但是紧张没考虑全面,我的我的。
// 例如:
const a = new Number(123)
console.log(a instanceof Object) // true
搜索一手:
function isPlainObjectAndEmpty(target) {
// 首先检查target是否不为null且为对象
if (target === null || typeof target !== 'object') {
return false;
}
// 排除数组、日期等特殊对象类型
// 这里使用了Object.prototype.toString.call来准确判断类型
// 注意:这里可以根据需要排除更多特殊对象类型
const type = Object.prototype.toString.call(target);
if (type !== '[object Object]') {
return false;
}
// 检查对象是否为空(即没有自身属性)
// 或者可以使用Reflect.ownKeys(target),在Symbol类型数据存在的情况下
return Object.keys(target).length === 0;
}
// 示例
console.log(isPlainObjectAndEmpty({})); // true
console.log(isPlainObjectAndEmpty(new Object())); // true
console.log(isPlainObjectAndEmpty([])); // false,因为数组不是普通对象
console.log(isPlainObjectAndEmpty(null)); // false
console.log(isPlainObjectAndEmpty(undefined)); // false
console.log(isPlainObjectAndEmpty(new Date())); // false,因为Date是特殊对象类型
console.log(isPlainObjectAndEmpty(function() {})); // false,因为函数不是普通对象
console.log(isPlainObjectAndEmpty({ a: 1 })); // false,因为对象包含属性
5、如下的代码输出什么
var a = 8
var func = (function (){
var a = 10
return function (){
a++
alert(a)
}
})()
func()
func()
func对应的是一个立即执行函数,因此内部的代码会执行一次,同时内部因为返回的是一个函数。所以该函数会赋值给func, 当第一次调用func 的时候,a++, 此时a = 11 ,弹框输出11,第二次调用的时候,由于闭包作用域,经过a++,a变为12,因此弹框输出12。(注意:a++和++a独自调用的时候,就相当于a+=1,没有先后顺序)
6、实现如下once函数
const onlyFireOnce = once(() => {
console.log("running")
})
onlyFireOnce() // 无输出
onlyFireOnce() // 无输出
onlyFireOnce() // 输出: running
其实题目的主旨是实现一个once函数,即无论调用多少次,最终都只执行一次。但是题目给出调用三次,最后一次才有输出,要实现最后一次才输出,因为无法确实哪一次才是最后一次,所以应该是题目有点问题。题目的主要主题还是实现执行一次,而不是只执行最后一次。
本人回答:
(虽然满足提议了,但是很丑陋,之前遇到过once函数的实现,但是没仔细看,忘记了,活该)
function once(Fn){
let a = 1
return function(){
return a == 3 ? Fn() : a++
}
}
// 超过三次调用就老实了
搜索一手:
function once(Fn){
let called = false
return function(...arg){
if(!called){
called = true
Fn.apply(this,arg)
}
}
}
7、如何实现1.235四舍五入为1.24
const a = 1.235
console.log(Number(a.toFixed(2))) // '1.24'
8、去除数组最大最小值,求剩余数据均值,同时格式化输出
有如下数据:
let data = [
{ number:"p1",score:[80,75,30,90,89,50,78,56,68,88] },
......
]
去除score中的最大值、最小值,求剩余成绩的平均值,最后格式化输出
p1得到的平均成绩为xx分
p2得到的平均城市为xx分
......
os:第一眼,简单,用sort排序,去除头尾,求均值,简单。格式化输出,更加简单。
写的时候,焯,sort方法忘记咋用进行排序了,所以有了如下代码
function getAvg(arr){
let max = min = arr[0]
let maxIndex = 0
let minIndex = 0
arr.forEach((item,index) => {
if(item > max) {
max = item
maxIndex = index
}
if(item < min){
min = item
minIndex = index
}
})
return arr.splice(max,1).aplice(min,1).reduce((total,item) => total+=item, 0) / 8
}
function formatData(data){
data.forEach(item => {
console.log(`${item.number}得到的平均成绩为${getAvg(item.score)}分`)
})
}
牛逼,我到底在写的什么鬼,splice会改变元素组,但是返回的结果是被去除的元素数组,所以上面的链式调用是错误的,真就是基础不牢地动山摇了。(借此也回顾一下slice,返回的结果也是被去除的元素数组,但是不会改变原数组)
实际只要
function getAvg(arr){
arr.sort((a,b) => a - b) // sort会改变元素组
arr.shift() // 删除开头
arr.pop() // 删除尾部
return arr.reduce((total,item) => total += item,0) / 8
}
function formatData(data){
data.forEach(item => {
console.log(`${item.number}得到的平均成绩为${getAvg(item.score)}分`)
})
}
9、给出css代码实现文字截取样式,限制长度为200px
其实就是文本溢出隐藏
.text {
width:200px;
overflow:hidden;
white-space:nowrap;
text-overflow: ellipsis;
}
10、如何查看当前的函数是谁在调用的
1. 使用浏览器的断点调试,查看对应的调用栈
2. 或者使用console.trace(),当对应函数被调用时就会显示当前执行的代码在堆栈中的调用路径
11、(场景题)当用户连续进行货物扫码的时候,如何根据用户扫码的顺序发起请求,即确保用户扫码之后发起的请求的有序性
// 扫码结果的队列
let dataQueue = []
let isRequestInProgress = false
// 用户扫码
function scanCode(){
uni.scanCode({
successL:(res) => {
// 扫码结果
dataQueue.push({data:res})
// 如果队列不为空且当前没有正在进行的请求,则开始处理队列
if(dataQueue.length && !isRequestInProgress){
sendRequestFromQueue()
}
}
})
}
// 将扫码的结果作为参数发起请求
function sendRequest(data,callback){
uni.request({
url:"/testScan",
data,
success:(res) => {
// 请求成功的逻辑
},
fail:(err) => {
// 请求失败的逻辑
},
complete:() => {
callback()
isRequestInProgress = false
}
})
}
// 发送队列中第一个请求的函数
function sendRequestFromQueue(){
if(dataQueue.length > 0){
isRequestInProgress = true
let currentRequestData = dataQueue.shift()
sendRequest(currentRequestData.data, () => {
if(dataQueue.length > 0){
sendRequestFromQueue()
}
})
}
}
12、防抖和节流
防抖:多次事件只执行最后一次
节流:单位时间内事件只执行一次
封装防抖函数:
function debance(fn,limit){
let timer;
return function(...args){
clearTimeout(timer)
const that = this
timer = setTimeout(() => {
fn.apply(that,args)
}, limit)
}
}
// 使用实例
const Search = debance((query) => {
console.log("Searching for:", query)
}, 500)
document.querySelector(".searchInput").addEvenlistener("input",function(e){
Search(e.target.value)
})
封装节流函数:
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function() {
const context = this;
const args = arguments;
if (!lastRan) {
// 如果这是第一次运行,立即执行函数
func.apply(context, args);
lastRan = Date.now(); // 记录上次执行的时间
} else {
// 如果不是第一次,则设置定时器
clearTimeout(lastFunc); // 清除之前的定时器(如果有的话)
lastFunc = setTimeout(function() {
// 检查当前时间与上次执行时间之间的差是否大于等于限制时间
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args); // 如果足够长,则执行函数
lastRan = Date.now(); // 更新上次执行时间
}
}, limit - (Date.now() - lastRan)); // 计算剩余时间以设置定时器
}
};
}
13、节流阀
节流阀防止事件重复执行,例如发送请求的时候,为了防止请求重复发起,可以使用节流阀,其实就是定义一个boolean类型的数据,默认设置为false,表示当前没有请求在执行。在请求的函数内部,初始判断对应的节流阀是否为true,如果为true直接退出当前函数的执行,否则执行对应的请求,请求结束后设置节流阀为false,表示当前没有请求在执行。
14、typeof和instanceof判断变量类型的区别
1. typeof 适合用来检测普通数据类型。
typeof检测引用数据类型返回的都是"object"
注意:特殊情况,使用typeof 检测null 返回的结果也是"object",这是js设计之初遗留的一个问题。
2. instanceof 适合用来检测引用数据类型。
instanceof 用于检测对象是否是另一个对象(构造函数)的实例。
如果想准确的判断对应的数据类型,可以使用Object.prototype.toString.call(对应变量),来检测。
例如:
Object.prototype.toString.call(123) // "[object Number]"
Object.prototype.toString.call("sss") // "[object String]"
Object.prototype.toString.call({}) // "[object Object]"
....
15、浏览器的工作原理
浏览器的工作原理是一个复杂而精细的过程,它涉及从用户输入URL到最终在屏幕上呈现网页的多个步骤。在前端面试中,当被问及浏览器的工作原理时,可以按照以下结构进行回答:
一、浏览器的基本组成
首先,简要介绍浏览器的基本组成,包括但不限于用户界面(如地址栏、标签页等)、浏览器引擎、渲染引擎(也称为浏览器内核)、网络模块、JavaScript解释器、UI后端、数据存储等部分。这些部分共同协作,完成浏览器的各项功能。
二、浏览器处理URL请求的过程
- 解析URL:
- 当用户在地址栏输入URL并按下回车键时,浏览器首先会解析这个URL,包括协议(如http、https)、域名(如www.example.com)和路径(如/index.html)等部分。
- DNS解析:
- 如果URL中包含域名,浏览器需要将其解析为对应的IP地址。这个过程可能会涉及浏览器缓存、系统缓存、路由器缓存以及向DNS服务器查询等步骤。
- 建立TCP连接:
- 浏览器使用解析得到的IP地址和端口号(默认为80或443),通过TCP协议与服务器建立连接。TCP连接是可靠的连接服务,通过三次握手建立。
- 发送HTTP请求:
- 连接建立后,浏览器会向服务器发送HTTP请求,请求中包含了请求方法(如GET、POST)、请求URL、HTTP版本协议等信息。
- 服务器处理请求:
- 服务器接收到请求后,会根据请求的内容进行相应的处理,并返回响应结果。响应结果通常包括响应行、响应头和响应体三部分。
- 接收响应并渲染页面:
- 浏览器接收到响应后,会开始解析HTML文档,构建DOM树。同时,如果遇到CSS文件和JavaScript文件,浏览器会并行下载这些资源,并构建CSSOM树。
- DOM树和CSSOM树构建完成后,浏览器会合并它们生成渲染树。渲染树包含了所有需要显示的内容以及它们的样式信息。
- 接下来,浏览器会进行布局和绘制操作,将渲染树的内容绘制到屏幕上,最终呈现给用户。
三、浏览器的渲染过程
浏览器的渲染过程可以细分为以下几个步骤:
- 解析HTML构建DOM树:
- 浏览器将HTML文档解析成一系列的DOM节点,并按照层级关系构建成DOM树。
- 构建CSSOM树:
- 浏览器将CSS文件解析成CSSOM(CSS Object Model)树,这个树包含了所有的样式信息。
- 合并DOM树和CSSOM树生成渲染树:
- 浏览器将DOM树和CSSOM树合并成渲染树,渲染树中包含了所有需要显示的内容以及它们的样式信息。
- 布局(Layout):
- 浏览器根据渲染树中的信息,计算每个节点的位置和大小,这个过程称为布局。
- 绘制(Paint):
- 浏览器将布局后的渲染树绘制到屏幕上,用户就可以看到最终的页面了。
四、浏览器的优化机制
在回答中,还可以提到浏览器的优化机制,如缓存机制、并行下载、异步加载等,这些机制可以提高网页的加载速度和性能。
五、总结
最后,总结浏览器的工作原理,强调它是一个复杂但高效的过程,涉及多个组件和步骤的协同工作。同时,可以根据面试的具体要求,进一步展开或深入讨论某个方面的内容。
通过以上结构化的回答,可以清晰地展示你对浏览器工作原理的理解和掌握程度,有助于在前端面试中脱颖而出。