面试刷题--002

146 阅读13分钟

一、js

1. setTimeout 的运行机制

1.1 js的运行机制如下:

    1. 所有同步任务都在主线程上执行,形成一个执行栈(Call Stack)
    1. 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件
    1. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
    1. 主线程不断重复上面的第三步。

1.2 setTimeout运行机制

setTimeout 和 setInterval的运行机制,其实就是将指定的代码移出本次执行,等到下一轮 Event Loop 时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮 Event Loop 时重新判断。

这意味着,setTimeout指定的代码,必须等到本次执行的所有同步代码都执行完,才会执行。

2. String.prototype.padStart

把指定字符串填充到字符串头部,返回新字符串。

str.padStart(targetLength [, padString])
  • targetLength

当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。

  • padString 可选

填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的默认值为 " "

示例:

'abc'.padStart(10);         // "       abc"
'abc'.padStart(10, "foo");  // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0");     // "00000abc"
'abc'.padStart(1);          // "abc"

应用场景

日期格式化:yyyy-mm-dd的格式:

const now = new Date()
const year = now.getFullYear()
// 月份和日期 如果是一位前面给它填充一个0
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const day = (now.getDate()).toString().padStart(2, '0')
console.log(year, month, day)
console.log( `${year}-${month}-${day}` ) //输入今天的日期 2023-02-01

数字替换(手机号,银行卡号等)

const tel = '118378395882'
//银行卡
const newTel = tel.slice(-4).padStart(tel.length, '*')
console.log(newTel) // *******5882
//手机号隐藏中间 4位
const newTel1 = tel.slice(0,3)+tel.slice(-4).padStart(tel.length-3, '*')
console.log(newTel1) // 183****5882

3. String.prototype.padEnd

把指定字符串填充到字符串尾部,返回新字符串。 语法同上String.prototype.padStart

3.1 应用场景

在JS前端我们处理时间戳的时候单位是ms毫秒,但是,后端同学返回的时间戳则不一样是毫秒,可能只有10位,以s秒为单位。所以,我们在前端处理这个时间戳的时候,保险起见,要先做一个13位的补全,保证单位是毫秒。

timestamp = +String(timestamp).padEnd(13, '0')

4. for await of

异步迭代器(for-await-of):循环等待每个Promise对象变为resolved状态才进入下一步。

4.1 for...of 是同步运行的

function TimeOut(time){
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(time)
        }, time)
    })
}

async function test() {
    let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)]
    for (let item of arr) {  
     console.log(Date.now(),item.then(console.log))
    }
}

test()

上述代码证实了 for of 方法不能遍历异步迭代器,得到的结果并不是我们所期待的,于是 for await of 就粉墨登场啦!

4.2 for...await...of

ES9 中可以用 for...await...of 的语法来操作

async function test() {
    let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)]
    for await (let item of arr) {
        console.log(Date.now(), item)
    }
}
test()
// 1675240005623 2000
// 1675240005624 1000
// 1675240006617 3000

5. 空值合并运算符(Nullish coalescing Operator)

空值合并操作符( ?? )是一个逻辑操作符,当左侧的操作数为 null或者undefined时,返回其右侧操作数,否则返回左侧操作数。

const foo = undefined ?? "foo"
const bar = null ?? "bar"
console.log(foo) // foo
console.log(bar) // bar

与逻辑或操作符(||)不同,逻辑或操作符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 || 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如'',0,NaN,false)时。见下面的例子。

const foo = "" ?? 'default string';
const foo2 = "" || 'default string';
console.log(foo); // ""
console.log(foo2); // "default string"

const baz = 0 ?? 42;
const baz2 = 0 || 42;
console.log(baz); // 0
console.log(baz2); // 42

5.1 注意点

将 ?? 直接与 AND(&&)和 OR(||)操作符组合使用是不可取。

6. 可选链 Optional chaining

6.1 介绍

可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为 null 或者 undefined 的情况下不会引起错误,该表达式短路返回值是 undefined。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined

const user = {
    address: {
        street: 'xx街道',
        getNum() {
            return '80号'
        }
    }
}

const street = user && user.address && user.address.street
const num = user && user.address && user.address.getNum && user.address.getNum()
console.log(street, num)

用了 Optional Chaining ,上面代码会变成

const user = {
    address: {
        street: 'xx街道',
        getNum() {
            return '80号'
        }
    }
}

const street2 = user?.address?.street
const num2 = user?.address?.getNum?.()
console.log(street2, num2)

可选链中的 ? 表示如果问号左边表达式有值, 就会继续查询问号后面的字段。根据上面可以看出,用可选链可以大量简化类似繁琐的前置校验操作,而且更安全。

6.2 与空值合并操作符一起使用

let customer = {
  name: "jimmy",
  details: { age: 18 }
};
let customerCity = customer?.city ?? "广州";
console.log(customerCity); // "广州"

6.3 注意点

可选链不能用于赋值

7.1 globalThis

在以前,从不同的 JavaScript 环境中获取全局对象需要不同的语句。在 Web 中,可以通过 windowself 取到全局对象,在 Node.js 中,它们都无法获取,必须使用 global

在松散模式下,可以在函数中返回 this 来获取全局对象,但是在严格模式和模块环境下,this 会返回 undefined

以前想要获取全局对象,可通过一个全局函数

const getGlobal = () => {
    if (typeof self !== 'undefined') {
        return self
    }
    if (typeof window !== 'undefined') {
        return window
    }
    if (typeof global !== 'undefined') {
        return global
    }
    throw new Error('无法找到全局对象')
}

const globals = getGlobal()
console.log(globals)

现在globalThis 提供了一个标准的方式来获取不同环境下的全局 this  对象(也就是全局对象自身)。不必担心它的运行环境。全局作用域中的 this 就是globalThis

8. 数字分隔符

欧美语言中,较长的数值允许每三位添加一个分隔符(通常是一个逗号),增加数值的可读性。比如,1000可以写作1,000

ES2021中允许 JavaScript 的数值使用下划线(_)作为分隔符。

  • 值分隔符没有指定间隔的位数
  • 小数和科学计数法也可以使用数值分隔符

数值分隔符有几个使用注意点。

  • 不能放在数值的最前面(leading)或最后面(trailing)。
  • 不能两个或两个以上的分隔符连在一起。
  • 小数点的前后不能有分隔符。
  • 科学计数法里面,表示指数的eE前后不能有分隔符。

9. 请求的 Content-Type

HTTP中,提交数据的方式,最常用的就是GET和POST。

GET方式,是把参数按键值对通过QueryString的方式放在URL尾部,比如: http://www.example.com/test.html?a=1&b=2

POST方法,通常是把要提交的表单放在一个Form中,指明action后就可以提交数据。

提交数据时需要通过表单enctype属性(规定在发送到服务器之前应该如何对表单数据进行编码)根据content type进行编码。

并且,如果是GET,用”?”连接,编码方式为“application/x-www-form-urlencoded”;如果是POST,则根据enctype属性确定content type,默认也为”application/x-www-form-urlencoded”。

image.png

9.1 关于Content-Type

在响应中,Content-Type标头告诉客户端实际返回的内容的内容类型。浏览器会在某些情况下进行MIME嗅探,并不一定遵循此标题的值; 为了防止这种行为,可以将标题 X-Content-Type-Options 设置为 nosniff。

在请求中 (如POST 或 PUT),客户端告诉服务器实际发送的数据类型。

语法

Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=something[1 to 70 characters]

media-type 资源或数据的 media type

charset 字符编码标准

boundary 对于多部分(multipart)实体,boundary 是必需的,它用于封装消息的多个部分的边界。其由1到70个字符组成,浏览器自动生成,该字符集对于通过网关鲁棒性良好,不以空白结尾。

10. 遍历数组的方式

这张图涵盖了数组大部分的方法 image.png

10.1 for

标准的for循环语句,也是最传统的循环语句

var arr = [1,2,3,4,5]
for(var i=0;i<arr.length;i++){
  console.log(arr[i])
}

最简单的一种遍历方式,也是使用频率最高的,性能较好,但还能优化

优化版for循环语句

var arr = [1,2,3,4,5]
for(var i=0,len=arr.length;i<len;i++){
  console.log(arr[i])
}

使用临时变量,将长度缓存起来,避免重复获取数组长度,尤其是当数组长度较大时优化效果才会更加明显。

这种方法基本上是所有循环遍历方法中性能最高的一种

10.2 for...in

任意顺序遍历一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性。

一般常用来遍历对象,包括非整数类型的名称和继承的那些原型链上面的属性也能被遍历。像 Array和 Object使用内置构造函数所创建的对象都会继承自Object.prototype和String.prototype的不可枚举属性就不能遍历了.

var arr = [1,2,3,4,5]
for(var i in arr){
  console.log(i,arr[i])
}  //这里的i是对象属性,也就是数组的下标
/**
0 1
1 2
2 3
3 4
4 5 **/

10.3 for...of(不能遍历对象)

在可迭代对象(具有 iterator 接口)(Array,Map,Set,String,arguments)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句,不能遍历对象

let arr=["前端","面试题宝典","真好用"];
    for (let item of arr){
        console.log(item)
    }

//遍历对象
let person={name:"alex",age:18,city:"上海"}
for (let item of person){
  console.log(item)
}
// 我们发现它是不可以的 我们可以搭配Object.keys使用
for(let item of Object.keys(person)){
    console.log(person[item])
}
// alex 18 上海

这种方式是es6里面用到的,性能要好于forin,但仍然比不上普通for循环

10.4 reduce

reduce()方法对数组中的每个元素执行一个由你提供的reducer函数(升序执行),将其结果汇总为单个返回值

const arr = [1,2,3,4]
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4 = 10
console.log('arr',arr1.reduce(reducer));

10.5 数组遍历各个方法的速度:传统的for循环最快,for-in最慢

for-len > for > for-of > forEach > map > for-in

javascript原生遍历方法的建议用法:

  • for循环遍历数组
  • for...in遍历对象
  • for...of遍历类数组对象(ES6)
  • Object.keys()获取对象属性名的集合

为何for… in会慢?

因为for … in语法是第一个能够迭代对象键的JavaScript语句,循环对象键({})与在数组([])上进行循环不同,引擎会执行一些额外的工作来跟踪已经迭代的属性。因此不建议使用for...in来遍历数组

二、css

2. css来实现禁止移动端页面的左右划动手势

CSS属性 touch-action 用于设置触摸屏用户如何操纵元素的区域(例如,浏览器内置的缩放功能)。

html{
 touch-action: none;
 touch-action: pan-y;
}

2.2 opacity: 0、visibility: hidden、display: none 优劣和适用场景

2.21 结构

display:none: 会让元素完全从渲染树中消失,渲染的时候不占据任何空间, 不能点击, visibility: hidden:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,不能点击 opacity: 0: 不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击

2.22 继承

display: none和opacity: 0:是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示。 visibility: hidden:是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式。

2.23 性能

display: none : 修改元素会造成文档回流,读屏器不会读取display: none元素内容,性能消耗较大 visibility:hidden: 修改元素只会造成本元素的重绘,性能消耗较少读屏器读取visibility: hidden元素内容 opacity: 0 :修改元素会造成重绘,性能消耗较少

3. 使用css完成视差滚动效果

3.1 视差滚动(Parallax Scrolling)是指多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验

3.2 实现方式

使用css形式实现视觉差滚动效果的方式有:

  • background-attachment
  • transform:translate3D

3.21 background-attachment

作用是设置背景图像是否固定或者随着页面的其余部分滚动

值分别有如下:

  • scroll:默认值,背景图像会随着页面其余部分的滚动而移动
  • fixed:当页面的其余部分滚动时,背景图像不会移动
  • inherit:继承父元素background-attachment属性的值

完成滚动视觉差就需要将background-attachment属性设置为fixed,让背景相对于视口固定。即使一个元素有滚动机制,背景也不会随着元素的内容而滚动

也就是说,背景一开始就已经被固定在初始的位置

section {
    height: 100vh;
}

.g-img {
    background-image: url(...);
    background-attachment: fixed;
    background-size: cover;
    background-position: center center;
}

3.22 transform:translate3D

同样,让我们先来看一下两个概念transformperspective

  • transform: css3 属性,可以对元素进行变换(2d/3d),包括平移 translate,旋转 rotate,缩放 scale,等等
  • perspective: css3 属性,当元素涉及 3d 变换时,perspective 可以定义我们眼睛看到的 3d 立体效果,即空间感
<style>
    html {
        overflow: hidden;
        height: 100%
    }

    body {
        /* 视差元素的父级需要3D视角 */
        perspective: 1px;
        transform-style: preserve-3d; 
        height: 100%;
        overflow-y: scroll;
        overflow-x: hidden;
    }
    #app{
        width: 100vw;
        height:200vh;
        background:skyblue;
        padding-top:100px;
    }
    .one{
        width:500px;
        height:200px;
        background:#409eff;
        transform: translateZ(0px);
        margin-bottom: 50px;
    }
    .two{
        width:500px;
        height:200px;
        background:#67c23a;
        transform: translateZ(-1px);
        margin-bottom: 150px;
    }
    .three{
        width:500px;
        height:200px;
        background:#e6a23c;
        transform: translateZ(-2px);
        margin-bottom: 150px;
    }
</style>
<div id="app">
    <div class="one">one</div>
    <div class="two">two</div>
    <div class="three">three</div>
</div>

transform实现视觉差动的原理如下:

  • 容器设置上 transform-style: preserve-3d 和 perspective: xpx,那么处于这个容器的子元素就将位于3D空间中,
  • 子元素设置不同的 transform: translateZ(),这个时候,不同元素在 3D Z轴方向距离屏幕(我们的眼睛)的距离也就不一样
  • 滚动滚动条,由于子元素设置了不同的 transform: translateZ(),那么他们滚动的上下距离 translateY 相对屏幕(我们的眼睛),也是不一样的,这就达到了滚动视差的效果

4. 让Chrome支持小于12px 的文字

解决方案有:

  • zoom
  • -webkit-transform:scale()
  • -webkit-text-size-adjust:none

4.1 Zoom

zoom 的字面意思是“变焦”,可以改变页面上元素的尺寸,属于真实尺寸

其支持的值类型有:

  • zoom:50%,表示缩小到原来的一半
  • zoom:0.5,表示缩小到原来的一半

使用 zoom 来”支持“ 12px 以下的字体

.div{
    font-size: 12px;
    display: inline-block;
    zoom: 0.8;
}

4.2 -webkit-transform:scale()

针对chrome浏览器,加webkit前缀,用transform:scale()这个属性进行放缩

注意的是,使用scale属性只对可以定义宽高的元素生效,所以,下面代码中将span元素转为行内块元素:

.span{
    font-size: 12px;
    display: inline-block;
    -webkit-transform:scale(0.8);
}

4.3 -webkit-text-size-adjust:none

该属性用来设定文字大小是否根据设备(浏览器)来自动调整显示大小

属性值:

  • percentage:字体显示的大小;
  • auto:默认,字体大小会根据设备/浏览器来自动调整;
  • none:字体大小不会自动调整

4.4 总结

Zoom 非标属性,有兼容问题,缩放会改变了元素占据的空间大小,触发重排

-webkit-transform:scale() 大部分现代浏览器支持,并且对英文、数字、中文也能够生效,缩放不会改变了元素占据的空间大小,页面布局不会发生变化