# 面试前准备-3

汇总已知的前端场景题

# 1. CSS以及Html场景题

# 1.1. offsetWidth

  • 判断以下div1的offsetWidth多大
<style>
  #div1 {
    width: 100px;
    padding: 10px;
    border: solid 1px #ccc;
    margin: 10px;
  }
</style>

<div id="div1"></div>
1
2
3
4
5
6
7
8
9
10
  • 122px
    • 左边和右边内边距102,左右边框12,宽度100
  • 如何使offsetWidth为100
    • box-sizing: border-box
    • 意思是加了这个之后with就不仅仅是内容宽度了,是border-box宽度,即到边框的box的宽度,即加上左右内边距加内容宽度加上边框,为100px

# 1.2. margin纵向重叠的问题

  • AAA和BBB之间的距离是多少
<style>
  p {
    font-size: 16px;
    line-height: 1;
    margin-top: 10px;
    margin-bottom: 15px;
  }
</style>

<p>AAA</p>
<p></p>
<p></p>
<p></p>
<p>BBB</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 15px
<p>AAA</p>  下15  
<p></p>  空内容被重叠即被忽略 margin值为0
<p></p>  0
<p></p>  0
<p>BBB</p> 上10 重叠15
1
2
3
4
5
  • 相领元素的margin-top和margin-bottom会发生重叠
  • 空白内容的<p></p>也会重叠

# 1.3. BFC的理解和应用(常考)

  • 什么是BFC?如何使用
    • Block format context => 块级格式化上下文
    • 一块独立渲染区域,内部元素的渲染不会影响边界以外的元素
  • 形成BFC的常见条件 => 即能实现BFC的方式
    • float不是none
    • position是absolute或fixed
    • overflow不是visible
    • display是flex或inline-block等
  • BFC的常见应用
    • 清除浮动
  • 示例
  • 一个容器,里面有文字和图片,给图片设置浮动
  • 此时图片无法撑开容器,即脱离文档流
  • 我们可以使用BFC来解决这个问题
<style type="text/css">
  .container {
      background-color: #f1f1f1;
  }
  .left {
      float: left;
  }
  .bfc {
      overflow: hidden; /* 触发元素 BFC */
  }
</style>

<div class="container bfc">
    <img src="https://www.imooc.com/static/img/index/logo.png" class="left" style="magin-right: 10px;"/>
    <p class="bfc">某一段文字……</p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 1.4. 图文样式

  • line-height如何继承 => 有个坑
  • p标签行高是多少
<style>
  body {
    font-size: 20px;
    line-height: 200%;
  }

  p {
    font-size: 16px;
  }
</style>

<body>
  <p>AAA</p>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 答:40px
    • 20*200% => p标签直接继承了body
  • 解析
    • line-height相关
      • 写具体数值,如30px,则继承该值
      • 写比例,如2/1.5,继承该比例
        • 子元素font-size*比例
      • 写百分比,如200%,则继承计算出来的值(考点) => 也是个坑
        • 先算完自身的行高,再进行继承
        • 即子元素的行高为,父元素的font-size*百分比

# 2. Javascript场景题

# 2.1. 创建10个<a>标签,点击的时候弹出对应的序号

// dom加载很快,但事件只有在点击的时候才会触发,i如果是全局变量,他会很快变成10,即每次点击弹出的都是10
let a
// 如果在for里面定义,就是块级作用域
// 每次循环的时候,都会形成一个新的作用域块,这里的i就会不一样
for(let i=0; i<10; i++>) {
  a = document.createElement('a');
  a.innerHTML = i + '<br>'
  a.addEventListener('click', function(e){
    // 这里的i是自由变量,他会在被执行的环境里面一层层往上找,如果i在全局,就会找到全局,全局作用域是针对所有的块
    // 如果i在for里面被定义,那么每次就会在块级作用域里面去找
    e.preventDefault();  // 阻止冒泡
    alert(i);
  });
  document.body.appendChild(a)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.2. js执行顺序场景题

// setTimeout 笔试题
console.log(1);
setTimeout(()=> {
  console.log(2);
}, 1000)
console.log(3)
// 不管是几秒,他是个异步,在任务队列中,等主线程执行完之后再执行
setTimeout(()=> {
  console.log(4);
}, 0)
console.log(5)

// 1 3 5 4 2
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2.3. promise场景题

// 第一题
// Promise.resolve属于resolved状态,会触发then
Promise.resolve().then(() => {  
    console.log(1)  // 1 resolve状态的promise不会执行catch
}).catch(() => {
    console.log(2)  // 不执行
}).then(() => {
    console.log(3)
})
// 整个promise执行完之后,返回的也还是resolved状态的promise
// 1、3

// 第二题
Promise.resolve().then(() => {  
    console.log(1)  // 1 
    throw new Error('erro1')  // 报错,那么整个then方法中的promise状态就是rejected,会执行catch回调
}).catch(() => {
    console.log(2)  // 2 无论catch还是then,只要不报错,返回的promise状态就是resolved,执行then回调
}).then(() => {
    console.log(3)  // 3
})  // 整个返回的也是resolved状态的promise
// 1、2、3

// 第三题
Promise.resolve().then(() => {  // rejected 触发catch回调
    console.log(1)  // 1
    throw new Error('erro1')
}).catch(() => {
    console.log(2)  // 2 resolve 触发then回调
}).catch(() => {
    console.log(3)
})
// 1、2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 2.4. async/await场景题

(async function() {
  console.log('start');
  const a = await 100  // 直接返回
  console.log('a', a)
  // await相当于then,resolved后面加then可以获取值,即直接得到Promise里面的返回值(200)
  const b = await Promise.resolve(200)  
  console.log('b', b)
  // await相当于then,rejected后面只能用catch
  const c = await Promise.reject(300)
  // 后面的方法属于回调,上面返回的是rejected,下面没有try catch
  console.log('c', c)
  console.log('end')
})()
// start 100 200 
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.5. async/await,promise,js综合场景题

// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout

async function async1() {
  console.log('async1 start')  // 2  同步
  await async2()  // 先执行async2,再执行await

  // 执行await,await后面的都是回调 - 微任务
  // 同步代码执行过程中,微任务会被放到micro task queue里面等待时机
  console.log('async1 end')  // 6  第一个微任务
}
async function async2 () {
  console.log('async2')  // 3  同步
}

console.log('script start')  // 1  同步

// 宏任务
// 同步代码执行过程中,宏任务会被放到Callback Queue里面等待时机
setTimeout(function() {
  console.log('setTimeout');  // 8  最后执行宏任务
}, 0)

async1()

// 初始化promise时,传入的函数会立刻被执行
new Promise(function(resolve) {
  console.log('promise1')  // 4  同步
  resolve()  // 变成了一个resolved状态下的promise
  // 即then会被触发
  // 这里的then是异步,是一个微任务
}).then(function() {
  console.log('promise2')  // 7  第二个微任务
})

console.log('script end');  // 5  同步  ->  同步代码执行完毕 => 调用者(call stack)被清空
// 同步代码执行完毕之后 => 微任务队列执行 => 尝试触发渲染DOM => 触发事件循环机制 => 宏任务队列执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 2.6. 如何一个一个数字进行打印

// 异步循环
!(async ()=> {
  for(let i of nums) {
    // 先执行第一个
    // 第一个有了结果之后执行第二个
    // 以此类推
    let res = await muti(i);
    console.log(res)
  }
})()
1
2
3
4
5
6
7
8
9
10

# 2.7. [10, 20, 30].map(parseInt)返回结果是什么

  • [10, NaN, NaN]
    // 他实际上算是简写
    // console.log([10, 20, 30].map(parseInt))  // [10, NaN, NaN]
    
    // 拆解 - 两个写法是一样的
    let res = [10, 20, 30].map((num, index)=> {
      // 第一个参数是数字,第二个参数是进制
      // 进制0和10是一样的
      // parseInt(10, 0)  // 10
      // parseInt(20, 1)  // NaN
      // parseInt(30, 2)  // NaN
      return parseInt(num, index)
    })
    console.log(res)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# 2.2. 关于作用域和自由变量的场景题

  • 场景题1

    // setTimeout(宏任务)属于异步队列中的任务,主线程执行完之后才会执行
    // 所以会先进行遍历,每次遍历会把setTimeout里面的方法放到异步队列中,但不会执行setTimeout
    
    // 遍历过程中,i在当前作用域(代码块)中没有定义,但是使用了,符合自由变量的条件,这个i是一个自由变量
    // 自由变量会在被执行的环境里面一层层往上找哪里定义了(这就是作用域链)
    // 此时i在全局定义,就会找到全局,即每次遍历完就会把值赋在全局
    // 全局作用域是针对所有的块
    
    // 当for循环遍历完之后,此时同步代码执行完,此时的i是全局变量是4,异步队列里面有4个setTimeout待执行
    // 开始执行异步队列里面的宏任务setTimeout,第一个setTimeout里面有个console.log(i)
    // 此时这个自由变量i同样也会在作用域链上一层层往上找,直到找到全局i为4
    
    // 于是此时打印的就是4 => 第一次打印
    // 执行下一个宏任务setTimeout
    // 以同样的方法找i
    // 于是此时打印的就是4 => 第二次打印
    // 后面同上
    let i
    for(i=1; i<=3; i++) {
      // debugger
      setTimeout(function(){
        console.log(i)  // 4 4 4
      }, 0)
    }
    
    // 先进行遍历,每次遍历会把setTimeout里面的方法放到异步队列中,但不会执行setTimeout,setTimeout中的i是一个自由变量
    // 当同步代码执行完,开始执行异步队列中的任务的时候,执行console.log(i),这个自由变量i开始往上找值,找到for里面
    // i在for里面被定义,即就是块级作用域,所以每次循环的时候,都会形成一个新的作用域块,这里的i就会不一样
    // 因为i在for里面被定义,那么每次遍历后的操作就会在块级作用域里面去找
    // 由于每次循环,都会形成一个新的作用域块,所以遍历中每次的宏任务setTimeout所在的作用域是不一样的,即里面的i也是不一样的
    
    // 第一次打印,在i=1的块级作用域中,此时打印的是1 => 第一次打印
    // 第二次打印,在i=2的块级作用域中,此时打印的是1 => 第二次打印
    // 后面同上
    for(let i=1; i<=3; i++) {
      // debugger
      setTimeout(function(){
        console.log(i)  // 1 2 3
      }, 0)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
  • 场景题2

    let a = 100
    function test() {
      alert(a)  // a是自由变量,找到100
      // 由于a是全局变量,
      // 这里其实是把全局变量的a给修改了
      a = 10
      alert(a)  // 10
    }
    test()
    // test方法执行完成之后,a这个全局变量实际已经被修改了
    alert(a)  // 10
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11


~End~