3 Star 4 Fork 1

SamuelSue / 前端面试

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
基础面试题.md 20.46 KB
一键复制 编辑 原始数据 按行查看 历史
SamuelSue 提交于 2021-06-19 02:02 . 防抖与节流

HTML

1. 如何理解HTML语义化

_20210519004938.png

对开发者而言,代码结构的可读性更强,对于标题,段落,列表等结构一目了然,方便快速定位和维护

对于搜索引擎而言,搜索引擎通过爬虫获取到html结果,对于使用语义化标签的结果能更加方便的分清内容结构和关系,比如标题和段落,方便搜索引擎读懂结果。

2. 默认情况下,哪些标签是块级(内联)元素

inline: span img input button

block/table: div h1 h2... table ul ol p

CSS

布局

1. 盒模型宽度如何计算

#div1 {
    width: 100px;
    padding: 10px;
    border: 1px solid #ccc;
    margin: 10px;
}

请问div1offsetWidth是多大? 答案100+20+2=122 $$ offsetWidth = width+padding+border $$

  • offsetWidth不包含margin
  • clientWidth不包含border和margin

如果要让offsetWidth变成100,只需要box-sizing:border-box

2. margin纵向重叠

相邻元素的margin-top和margin-bottom会重叠,最终距离是取两者较大者

3. margin为负值

  • margin-top和margin-right设置为负值,元素向上/左移动
  • margin-right为负值,右侧元素向左移动,自身不受影响
  • margin-bottom为负值,下方元素上移,自身不受影响

4. BFC的理解和应用

Block Format Context 块级格式化上下文

一块独立渲染区域,内部元素的渲染不会影响边界以外的元素

形成BFC的条件:

  • float不是none
  • position为absolute或者fixed
  • overflow不是visible
  • display是flex或者inline-block等

常见应用: 清除浮动

5. float布局以及clearfix

圣杯布局和双飞翼布局的目的:

  • 三栏布局,中间一栏最先加载和渲染(内容最重要)
  • 两侧内容固定,中间内容宽度自适应
  • 一般用于PC网页

圣杯布局: header + 中间三栏 + footer

双飞翼布局: 三栏布局,中间自适应

圣杯布局的要点

<div class="header">header</div>
<div class="container">
    <!--要先写中间,因为要让中间的元素先渲染 -->
    <div class="center column">center</div>
    <div class="left column">left</div>
    <div class="right column">right</div>
</div>
<div class="footer">footer</div>

header和footer没有什么难点,主要是中间container如果不BFC,可以让footer用BFC清除浮动

body {
  min-width: 600px; /*防止太窄使得三列换行  min-width = 2*left + right */
}
.column {
  float: left;
  height: 200px;
  text-align: center;
  line-height: 200px;
}
.container {
  padding-left: 200px; /* 左右使用padding预留空间 */
  padding-right: 150px;
}
.center {
  width: 100%;
  background-color: green;
}
.left {
  background-color: red;
  width: 200px;
  margin-left: -100%; /*把left挤到center前面去*/
  position: relative;
  left: -200px; /*相对位置,把自己推到父容器的padding中*/
}
.right {
  width:150px;
  background-color: blue;
  margin-right: -150px;/*将右边元素挤到左边来,因为它右边已经没有元素了,达成目的是从外部来看,它自身的宽度width+marigin=0了,可以放到上一行中*/
}
.footer {
  clear: both;
}

双飞翼布局

<div class="center column">this is center</div>
<div class="left column">this is left</div>
<div class="right column">this is right</div>
.column {
    height: 200px;
    float: left;
    line-height:200px;
    text-align: center;
}
.center {
    background-color: grey;
    width: 100%;
}
/* 这里得有个wrap通过margin来预留空间,但是父级center没有预留空间 */
.center-wrap {
    margin: 0 190px 0 190px;
}
.left {
    background-color: aqua;
    width: 190px;
    margin-left:-100%;
}
.right {
    width: 190px;
    background-color: bisque;
    margin-left: -190px; /*这里是因为center没有往右边预留空间,如果.right宽度为0还是会被放到center的后面,
    现在是要right盖在center-wrap的margin-right的区域上,因此直接让right往左边移动挤进center上就行了*/
}

6. flex画骰子

<div class="container">
    <div class="circle"></div>
    <div class="circle"></div>
    <div class="circle"></div>
</div>
.container {
    width: 300px;
    height: 300px;
    border-radius: 20px;
    border: 1px solid #ccc;
    padding: 50px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.circle{
    width: 50px;
    height: 50px;
    border: 1px solid #ccc;
    background-color: blue;
    border-radius: 50%;
}
.circle:first-child {
    align-self: flex-start;
}
.circle:last-child {
    align-self: flex-end;
}

flex常用的属性要记住:

  • justify-content: space-between(等比例分部排列,两头没有空隙), space-around(等比例缝补,两头空隙加起来等于各个元素的空隙)

定位

1. absolute和relative分别根据什么定位

  • relative:依据自身定位
  • absolute: 根据最近一层的定位元素(absolute relative fixed) 直到body

2. 居中对齐的实现方式

  • 水平居中
  1. inline元素: text-align:center
  2. block元素: margin:auto
  3. absolute元素: left: 50% + margin-left(负值) 需要知道子元素的尺寸
  • 垂直居中
  1. inline元素: line-height等于height值
  2. absolute元素
    • top: 50% + margin-top负值 需要知道子元素的尺寸
    • top50%+left:50%+transform:translate(-50%,-50%);
    • top,left,bottom,right=0 + margin:auto

图文样式

1. line-height继承问题

body{
    font-size:20px;
    line-height: 200%;
}
p {
    font-size: 16px;
}
<body>
    <p>
        p标签
    </p>
</body>

p标签的line-height是多少? 40px;

line-height继承规则:

  • 写具体数值比如30px, 就继承该值
  • 写比例2/1.5,就继承该比例
  • 写百分比200%,则继承计算之后的值(父值计算完之后给子)

响应式

1. rem

2. 如何实现响应式

媒体查询+rem

3. vw/vh

rem的弊端

“阶梯”性: 媒体查询的弊端,在一个范围内rem代表的尺寸是固定的,不会随着屏幕的变化而变化,无法实现一种真正意义上的完全的响应式

网页视口尺寸

  • window.screen.height 屏幕高度
  • window.innerHeight 网页视口高度(除了浏览器工具栏之类的,显示网页的高度)
  • document.body.clientHeight Body高度

vh:网页视口高度的1/100

vw:网页视口宽度的1/100

vmax: 取两者(vh,vw)最大值

vmin: 同理

CSS3

动画

JS

1. typeof能判断哪些类型

识别所有值类型(undefined,string,number,boolean,symbol) 和 function, object

2. ===和==

3. 值类型和引用类型

值类型

imagec7392830944801dc.png

值类型一般存在栈中,如果a,b都是值类型,b=a实际上是将a的数据复制了一份给b

引用类型

imagedeb7b24fdf2b3632.png

4. 手写深拷贝

需要考虑很多情况,这里有个比较好写的版本

// 可继续遍历类型
const mapTag = "[object Map]";
const setTag = "[object Set]";
const objectTag = "[object Object]";
const arrTag = "[object Array]";
const deepType = [mapTag, setTag, objectTag, arrTag];

function getType(obj) {
  return Object.prototype.toString.call(obj);
}

function getCloneObj(target, typeTag) {
  if(typeTag === objectTag) {
    const allDesc = Object.getOwnPropertyDescriptors(target); // 拷贝属性描述符
    return Object.create(Object.getPrototypeOf(target),allDesc);  // 拷贝原型
  }else{
    return new target.constructor(target);
  }
}

function deepClone(target, hash = new WeakMap()) {
  if (typeof target !== 'object') {
    return target;
  }
  if (hash.has(target)) {
    return hash.get(target)
  }
  const typeTag = getType(target);
  const cloneObj = getCloneObj(target, typeTag);
  hash.set(target, cloneObj);
  if (deepType.includes(typeTag)) {
    if (typeTag === mapTag) {
      target.forEach((value, key) => {
        cloneObj.set(key, deepClone(value, hash));
      })
    }
    if (typeTag === setTag) {
      target.forEach(value => {
        cloneObj.add(deepClone(value, hash))
      })
    }
    if ([arrTag, objectTag].includes(typeTag)) {
      for (const key in target) {
        // 只拷贝自身属性,父类型继承的Property不拷贝
        if (target.hasOwnProperty(key)) {
          cloneObj[key] = deepClone(target[key], hash);
        }
      }
    }
  }
  return cloneObj;
}

5. 原型和原型链

1. 如何准确判断变量的类型(比如数组)

引入的知识点是instanceof的使用规则,比如判断a instanceof Array,就是看Array.prototype是否存在于a.__proto__的原型链上。

2. 手写一个简易的jQuery,考虑插件和扩展性

  • 原型拓展
  • 重写(继承)

3. class的原型本质,怎么理解?

class Person {
    constructor(){}
}

typeof Person; // function 类本质上就是一个(构造)函数
const p = new Person();
p.__proto__ === Person.prototype;  // true

6. 作用域和闭包

1. 手写bind

Function.prototype.bind = function() {
    var args = Array.prototype.slice.call(arguments);
    var context = args.splice(0,1)[0];
    var fn = this;
    var res =  function() {
        let rest = Array.prototype.slice.call(arguments);
        return fn.apply(context, args.concat(rest));
    }
    // 保留原型
    if(this.prototype) {
        res.prototype = this.prototype;
    }
    return res;
}

2. 闭包以及其使用场景

注意一点:自由变量的查找规则

var a = 100;
function logA() {
  console.log(a);
}
function t(fn) {
  var a = 200;
  fn();
}
t(logA); // 打印的是100

自由变量:当前函数作用域内不存在的变量

自由变量的查找规则:会在声明函数的作用域链上(或者叫上一级)查找,和调用函数的作用域没有关系

7. 异步

1. 描述event loop机制

**event loop是一个执行模型,**在不同的地方有不同的实现。浏览器中的Event Loop是html5规范中明确定义的。

event loop

上图的将浏览器的Even loop完整的描述出来了

  1. 执行全局Script同步代码,这些代码有一些是同步语句,有一些是异步语句(如setTimeout)
  2. 全局Script代码执行完毕后,调用栈Stack会清空
  3. 微任务队列依次取出任务,放入调用栈Stack中执行,直到microtask queue清空(注意:在执行microtask的过程中,又产生了microtask,会继续加入到队列的末尾,同样会在这个周期内被执行)
  4. microtask queue为空,调用栈stack也为空
  5. 尝试进行浏览器UI渲染
  6. 开始执行宏任务队列队首的任务(也就是执行一个)
  7. 重新检查微任务队列,如果有任务,执行完所有微任务,然后UI渲染,然后执行一个宏任务

2. 宏任务和微任务

宏任务macrotask,也叫task。一些异步任务的回调会依次进入macrotask queue,等待后续被调用

  • setTimeout
  • setInterval
  • setImmediate(Node独有)
  • requestAnimationFrame(浏览器独有)
  • I/O

微任务microtask,也叫jobs。另一些异步任务的回调会依次进入microtask queue,等待后续被调用

  • process.nextTick(Node独有)
  • Promise
  • Object.observe
  • MutationObserver

8. DOM相关

DOM常用的API

attr和property的区别

const p = document.querySelector('p');
p.style.width = '100px'; // property的形式

p.setAttribute('data-name','test'); // attr形式

property修改的是js变量,不会对DOM结构产生影响,attr修改的是标签结点的属性,会显示在DOM结构中

两者都有可能引起DOM的重新渲染

一次性插入多个DOM结点,提升性能

const listNode = document.querySelector('ul');
// 创建一个文档片段,此时还没有插入到DOM树中
const fragment = document.createDocumentFragment();

// 执行插入
for(let x=0;x<10;x++){
    const li = document.createElement('li');
    li.innerHTML = x + 'tag';
    fragment.appendChild(li);
}
// 将片段插入到DOM树中
listNode.appendChild(fragment);

9. BOM相关

识别浏览器类型

// navigator
navigator.userAgent

//
screen.height
screen.width

拆解URL各部分

// 以 https://www.baidu.com/s?word=abc#123 为例
location.href; // "https://www.baidu.com/s?word=abc#123"
location.protocal;  // https
location.host;  // www.baidu.com
location.pathname;  //  /s
location.search;  // ?word=abc
location.hash;   // #123

10. 事件

编写一个通用的事件监听函数

function bindEvent(dom, event, selector, cb) {
  if (cb == null) {
    cb = selector
    selector = null
  }
  if (dom) {
    dom.addEventListener(event, (e) => {
      let target
      if (selector) {
        // 代理事件
        target = e.target
        if (target.matches(selector)) {
          cb.call(target, e)
        }
      } else {
        // 不需要代理
        cb(e);
      }
    })
  }

描述事件冒泡的流程

基于DOM树结构,指事件最开始由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播至最不具体的那个节点(文档)

无限下拉的图片列表,如何监听每个图片的点击

事件代理

11. AJAX

手写AJAX

var xhr = new XMLHttpRequest();
xhr.open('GET','http://127.0.0.1:5500/src/%E9%9D%A2%E8%AF%95%E9%A2%98/');
xhr.onreadystatechange = function() {
  if(xhr.readyState === 4) {
    if(xhr.status === 200) {
      console.log(xhr.responseText)
    }
  }
}
xhr.send()

跨域

12. 存储

cookie

document.cookie='a=100;b=2';

cookie的劣势:

  • 存储大小,最大4KB
  • http请求时,会携带cookie发送到服务端,增加请求数据量

localStorage和sessionStorage

HTML5专为存储而设计,最大可存5M

API使用简易:setItem / getItem

不会随http请求发送

区别:

  • localStorage数据会永久存储,除非手动删除
  • sessionStorage只存在于当前会话,浏览器关闭则清空
  • 一般localStorage会更常用一些

HTTP相关

常见的状态码

1xx: 服务端已经收到请求,还未响应

2xx: 服务端成功响应

3xx: 重定向相关(配合response header-Location使用,浏览器自动解析跳转), 304和缓存相关

4xx: 客户端请求错误

5xx: 服务端错误

常见的headers

Request

Accept:可接收的数据格式

Accept-Encoding:浏览器可接收的压缩算法,如gzip

Accept-Language:语言

Connection:keep-alive 一次TCP连接重复使用

cookie

Host: 请求的域名

User-Agent

Content-type:发送数据的格式 如application/json

Response

Content-type:返回的数据格式

Content-length:返回数据的大小(字节)

Content-Encoding:返回数据的压缩算法

缓存相关

整个http的缓存可以从两个角度来看待,缓存控制缓存校验

cache-control

用于设置缓存策略(缓存控制

  • max-age=[seconds], 缓存有效时间,过期后再去服务器请求新的资源
  • no-cache 不缓存(本地),走服务器请求(服务端可能有缓存)
  • no-store 不缓存,服务端也没有缓存,强制获取最新的资源
  • private 最终用户缓存(中间的路由器,网关之类的不可以缓存)
  • public 中间的路由器,网关之类的不可以缓存

Expires也是类似功能,不过是老版本的浏览器会有的。

协商缓存

是一种服务器端缓存策略,即服务端来判断能不能使用本地缓存的内容。

服务端判断客户端资源,是否和服务端资源一样

一致则返回304,否则返回200和最新的资源

image.png

资源标识,在response header中,有两种

  • Last-Modified 资源的最后修改时间
  • Etag 资源的唯一标识

Last-Modified / If-Modified-Since

  1. 初次请求,服务端返回Last-Modified
  2. 再次请求,header带着If-Modified-Since(时间,精确到秒)
  3. 返回304 或者 新的资源和Last-Modified

Etag / If-None-Match

  1. 初次请求,返回Etag(字符串)
  2. 再次请求,header带着If-None-Match(也就是资源的唯一标识字符串)
  3. 返回304 或新的资源和Etag

总结

image23eb33c949978114.png

运行环境

从输入url到渲染出页面的整个过程

  • 加载过程
      1. DNS解析:域名 to IP地址
      2. 浏览器根据IP地址向服务器发起HTTP请求
      3. 服务器处理http请求,并返回给浏览器
  • 渲染过程
      1. 根据HTML代码生成DOM Tree
      2. 根据CSS代码生成CSSOM
      3. 将DOM Tree和CSSOM整合形成Render Tree
      4. 根据Render Tree渲染页面
      5. 遇到<script>则暂停渲染,优先加载并执行js代码,完成再继续渲染
      6. 直到渲染完成

window.onload和DOMContentLoaded的区别

window.onload=function(){
    /*
    页面的全部资源加载完才会执行,包括图片和视频
    */
}

document.addEventListener('DOMContentLoaded',function(){
    /*
    DOM渲染完即可执行,此时图片等资源可能还没有加载完
    */
})

性能优化

节流

节流是表示同一事件频繁触发,在一段时间内只执行一次回调(比如鼠标移动,wait时间为500ms,则每500ms会触发一次事件回调)

function throttle(fn,wait){
  var timer;
  return function() {
    var context = this;
    var args = arguments;
    if(!timer){
      // 如果计时器不存在,就设置一个计时器,计时结束后会自己置空计时器
      timer = setTimeout(function() {
        fn.apply(context,args);
        clearTimeout(timer);
        timer = null;
      },wait);
    }
  }
}

防抖

防抖是表示同一事件频繁触发,在触发完之后等待一段时间才会执行一次回调,如果一段时间内又触发了,那就以新的为准,再等一段时间(比如鼠标移动,wait时间为500ms,500ms内一直在移动,那么回调一直等,直到鼠标停止后500ms后,回调执行)

function debounce(fn, wait) {
  var timer
  return function () {
    var context = this
    var args = arguments
    if (timer) clearTimeout(timer)
    timer = setTimeout(function () {
      fn.apply(context, args);
    }, wait)
  }
}

安全

XSS攻击

场景:

在一个博客网站中,我发表一篇博客,其中嵌入script脚本,脚本内容是获取cookie并且发送到指定的服务器上(服务器配合跨域)

博客发布后,有人查看,会执行script脚本,我轻松收割访问者的cookie

防范:

  1. 替换(转义)特殊字符,如<变为&ltl
  2. 前后端都要做替换

XSRF(CSRF)攻击

场景:

Bob通过给银行网站发送请求http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 将钱转给bob2,请求发送给后端服务器后,服务器会验证session,检查合法且该session的用户Bob已经成功登陆

黑客做了一个钓鱼网站,在网站中放入img src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory“,诱导Bob访问网站,然后执行了该请求,服务端验证Bob的session未过期且已经登陆,则转账成功

防范:

主要思路是在请求中加入无法伪造的验证信息,CSRF之所以能成功,是因为关键的验证信息都存在于cookie中,而黑客可以在不知道这个验证信息的情况下伪造用户自己发出的请求。

可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 <input type="hidden" name="csrftoken" value="tokenvalue"/>,这样就把 token 以参数的形式加入请求了

JavaScript
1
https://gitee.com/samuelsue/frontend_interview.git
git@gitee.com:samuelsue/frontend_interview.git
samuelsue
frontend_interview
前端面试
master

搜索帮助