HTML和CSS 原型、作用域、异步 DOM事件和Ajax 性能优化 手写各种代码 HTTP协议 ​

HTML 和 CSS

如何理解HTML语义化

  • 让人更容易读懂(增加代码的可读性)
  • 让搜索引擎更容易读懂(SEO)

块状元素 & 内联元素

  • display:block/table; 有div h1 h2 table ul ol p 等
  • display:inline/inline-block 有 span img input button等

盒模型宽度的计算

image.png offsetWidth = (内容宽度 + 内边距 + 边框),无外边距 box-sizing: border-box

margin 纵向重叠

image.png 相邻元素的margin-top和margin-bottom会发生重叠 空内容的p标签也会发生重叠

margin 负值的问题

margin-top 和 margin-left 负值,元素向上、向左移动 margin-right 负值,右侧元素左移,自身不受影响 margin-bottom 负值,下方元素上移,自身不受影响 ​

BFC 理解与应用

block format context, 块级格式化上下文 一个独立渲染的区域,内部元素的渲染不会影响边界以外的元素 ​

形成bfc的常见条件

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

常见应用:

  • 清除浮动

实现圣杯布局和双飞翼布局

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

  • 圣杯布局和技术总结

使用float布局 两侧使用margin负值,以便和中间内容横向重叠 防止中间内容被两侧覆盖,圣杯布局使用padding,双飞翼布局使用margin ​

手写clearfix

/* 手写clearfix */
.clearfix:after {
	content: '';
	display: table;
  clear: both;
}
.clearfix {
	*zoom: 1; /* 兼容IE低版本 */
}

flex 布局

.box {
  width: 200px;
  height: 200px;
  border: 2px solid #ccc;
  border-radius: 10px;
  padding: 20px;

  display: flex;
  justify-content: space-between;
}
.item {
  display: block;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background-color: #666;
}
.item:nth-child(2) {
  align-self: center;
}
.item:nth-child(3) {
  align-self: flex-end;
}

absolute 和 relative 定位

relative 依据自身定位 absolute依据最近一层的元素定位 ​

定位元素: absolute,relative,fixed body ​

居中对齐的实现方式

  • 水平居中

inline 元素:text-align :center block元素:margin:auto absolute元素:left:50%;+margin-left:负值 ​

  • 垂直居中

inline元素:line-height的值等于height的值 absolute元素:top50% + margin-top:负值 absolute元素: transform:translate(-50%,-50%) absolute元素:top,left,bottom,right = 0 + margin:auto ​

line-height 如何继承

如果写具体数值,如30px,则继承该值(比较容易理解) 如果写比例,如2/1.5,则继承该比例 如果写百分比,如200%,则继承计算出来的值 ​

rem 是什么

  • rem是一个长度单位,
    • px是绝对长度单位,最常用
    • em相对长度单位,相对于父元素,不常用
    • rem,相对长度单位,相对于根元素,常用语响应式布局

响应式布局的常用方案 media-query根据不同的屏幕宽度设置根元素的font-size rem基于根元素的相对单位 ​

  • rem 弊端,“阶梯”性

网页的视口尺寸 window.screen.height // 屏幕高度 window.innerHeight // 网页视口高度 document.body.clientHeight // body 高度 ​

vh 网页视口高度的 1/100 vw 网页视口高度的 1/100 vmax 取两者的最大值,vmin取两者的最小值 ​

CSS3 动画

JS基础,变量类型和计算

typeof 能判断那些类型

值类型 image.png 引用类型 image.png 值类型有那些 undefined,string,number,boolean,symbol ​

引用类型有哪些 object array null function ​

typeof运算符

  • 识别所有的值类型,
  • 识别函数
  • 判断是不是引用类型(不可再细分)

手写js深拷贝

const obj1 = {
	age: 20,
  name: '',
  addresss: {
  	city: 'beijing'
  }
}

const obj2 = obj1;

function deepClone(obj = {}){
  if(typeof obj !== 'object' || obj == null){
    return obj
  }
  
  let result
  if(obj instanceof Array){
    result = []
  } else {
    result = {}
  }
  
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      result[key] = deepClone(obj[key])
    }
  }
  
  
  return result
}

类型转换

  • 字符串拼接
  • ==
  • if语句和逻辑运算

除了==null,其他地方都用=== ​

0,NaN,"",null,undefined,false,其他均为truly ​

原型和原型链

原型关系

每个 class 都有显示原型prototype 每个实例都有隐式原型__proto__ 实例的隐式原型都指向对应class 的显示原型 ​

image.png

获取属性name或者执行方法的时候,现在自身的属性或者方法中寻找,找不到就自动从隐式原型中寻找 ​

原型链

image.png image.png

如何判断一个变量是数组

a instanceof Array ​

作用域和闭包

闭包

  • 函数作为参数,
  • 函数作为返回值

this是在函数执行的地方决定的

手写bind函数

Function.prototype.bind1 = function () {
  const args = Array.prototype.slice.call(arguments)
  const t = args.shift()
  
  const self = this
  
  // 返回一个函数
  return function () {
    return self.apply(t, args)
  }
}

闭包的应用

  • 隐藏数据

异步

单线程和异步

js是单线程语言,只能同时做一件事儿 浏览器和nodejs已经支持js启动进程,如Web Worker js和DOM渲染共同使用一个线程,因为JS可修改DOM的结构 ​

遇到等待,(网络请求,定时任务)不能卡住 需要异步 回调callback函数形式 ​

异步的应用场景

网络请求,ajax图片加载 定时任务setTimeout ​

异步进阶

event loop

js 是单线程运行的 异步要基于回调来实现 event loop 就是异步回调的实现原理 ​

js如何执行

  • 从前到后,一行一行执行
  • 如果某一行执行报错,则停止下面代码的执行
  • 先把同步代码执行完,再执行异步

image.png

  • 同步代码,一行一行放在call stack 执行
  • 遇到异步,先记录下,等待时机(定时、网络请求等)
  • 时机到了,就移动到Callback Queue中
  • 如果Call Stack 为空,(即同步代码执行完)Event Loop 开始工作
  • 轮询查找CallBack Queue ,如有则移动到Call Stack执行
  • 然后继续轮询查找

DOM事件和event loop

  • JS 是单线程的
  • 异步(setTimeout,ajax等)使用回调,基于event loop
  • DOM事件也使用回调,基于event loop

Promise 的三种状态

pending fulfilled rejected image.png

async/await 和 Promise 的关系

  • 执行 async 函数,返回的是Promise对象
  • await 相当于 Promise的then
  • try catch 可不会异常,代替了Promise的catch

什么是宏任务,什么是微任务

宏任务:setTimeout,setInterval,Ajax,DOM事件 微任务:Promise,async await 微任务执行时机比宏任务要早 ​

event loop 和 DOM 渲染

  • 再次回归一遍event loop 的过程
  • JS 是单线程的,而且和DOM渲染公用一个线程
  • JS执行的时候,需要留一些事件给DOM渲染

  1. call stack 空闲
  2. 尝试DOM渲染
  3. 触发event loop

微任务和宏任务的区别

宏任务: DOM渲染后触发,如 setTimeout 微任务: DOM渲染前触发,如 Promise

微任务和宏任务的根本区别

微任务是ES6语法规定, 宏任务是由浏览器规定 ​

宏任务放在webapi的空间中 微任务放在 micro task queue中 ​

  1. call stack 清空
  2. 执行当前的微任务
  3. 尝试DOM渲染
  4. 触发Event Loop

Promise的问题

async function async1 () {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}
async function async2 () {
  console.log('async2')
}
console.log('script start')

setTimeout(function () {
	console.log('setTimeout')
}, 0)

async1()

new Promise(function (resolve) {
	console.log('promise1')
  resolve()
}).then(function () {
	console.log('promise2')
})

console.log('script end')

手写Promise

const PENDING = 'pending' // pending
const FULFILLED = 'fulfilled' // fulfilled
const REJECTED = 'rejected' // rejected

export default class MyPromise {
  constructor (executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }

  // promsie 状态
  status = PENDING
  // 成功之后的值
  value = undefined
  // 失败后的原因
  reason = undefined
  // 成功回调
  successCallback = []
  // 失败回调
  failCallback = []

  resolve = value => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return
    // 将状态更改为成功
    this.status = FULFILLED
    // 保存成功之后的值
    this.value = value
    // 判断成功回调是否存在 如果存在 调用
    while (this.successCallback.length) this.successCallback.shift()()
  }
  reject = reason => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return
    // 将状态更改为失败
    this.status = REJECTED
    // 保存失败后的原因
    this.reason = reason
    // 判断失败回调是否存在 如果存在 调用
    // this.failCallback && this.failCallback(this.reason);
    while (this.failCallback.length) this.failCallback.shift()()
  }

  then (successCallback, failCallback) {
    // 参数可选
    successCallback = successCallback ? successCallback : value => value
    // 参数可选
    failCallback = failCallback ? failCallback : reason => { throw reason }
    let promsie2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value)
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promsie对象返回的结果
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason)
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promsie对象返回的结果
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else {
        // 等待
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value)
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve
              // 如果是promise对象 查看promsie对象返回的结果
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason)
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve
              // 如果是promise对象 查看promsie对象返回的结果
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
    })
    return promsie2
  }

  finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }

  catch (failCallback) {
    return this.then(undefined, failCallback)
  }

  static all (array) {
    let result = []
    let index = 0
    return new MyPromise((resolve, reject) => {
      function addData (key, value) {
        result[key] = value
        index++
        if (index === array.length) {
          resolve(result)
        }
      }

      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        if (current instanceof MyPromise) {
          // promise 对象
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          // 普通值
          addData(i, array[i])
        }
      }
    })
  }

  static resolve (value) {
    if (value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }
}

function resolvePromise (promsie2, x, resolve, reject) {
  if (promsie2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    x.then(resolve, reject)
  } else {
    // 普通值
    resolve(x)
  }
}

JS基础到JS Web API

js基础知识,规定语法(ECMA262标准) js web api ,网页操作的API(W3C标准) 前者是后者的基础,两者结合才能真正实际应用 ​

DOM的本质是什么

DOM 本质是一棵存在内存中的树 ​

获取DOM节点

getElementById getElementsByTagName getElementsByClassName querySelectorAll

节点操作

className nodeName nodeType style.width ​

getAttribute setAttribute 可以通过setAttribute设置style ​

property和attribute两种形式的区别 ​

DOM的结构操作

appendChild 新增移动 parentNode 获取父元素 childNodes Array.prototype.slice.call removeChild ​

DOM 性能

  • DOM操作非常“昂贵”,避免频繁的DOM操作
  • 对DOM的查询做缓存
  • 将频繁的操作改为一次性操作 createDocumentFragment

BOM 操作

  • navigator
  • screen
  • location
  • history

userAgent

navigator.userAgent ​

location.href 整个网址 location.protocol 协议 location.search location.hash location.pathname ​

history.back() history.forward() ​

事件

div.addEventListener(‘click’, ()=>{}) ​

e.target e.preventDefault() e.stopPropagation() ​

ele.matches ​

Ajax 的核心API

XMLHttpRequest ​

readyState 状态

  • 0 未初始化,还没有调用send方法
  • 1 载入,已调用send方法,正在发送请求
  • 2 载入完成,send方法执行完成,已经接收到全部的响应内容
  • 3 交互,正在解析响应内容
  • 4 完成,响应内容解析完成,可以在客户端调用
const xhr = new XMLHttpRequest()
xhr.open("GET", "/api", true) // true 是异步的
xhr.onreadystatechange = function () {
	// 这里函数异步执行
  if (xhr.readyState === 4) {
  	if(xhr.status === 200) {
    	alert(xhr.responseText)
    }
  }
}
xhr.send(null)

什么是浏览器的同源策略

  • ajax请求时,浏览器要求当前网页和server必须同源(安全)
  • 同源:协议,域名,端口 三者必须一致

加载图片 css js 可无视同源策略

  • img 可以统计大点,可以使用第三方统计服务
  • link script 可以使用CDN,CDN一般都是外域
  • script 可实现JSONP

  • 所有的跨域都必须经过server端的允许和配合
  • 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号

实现跨域的常见方案,JSONP 和 CORS

script 可绕过跨域限制 服务器可以任意动态拼接数据返回 所以,script就可以后去跨域数据,只要服务端愿意返回 ​

服务端设置 http header image.png

JS-Web-API 存储

描述 cookie localstorage sessionStorage ​

本身用于浏览器和server通讯 被借用到本地存储 可以使用document.cookie = '' 开修改 ​

cookie的缺点

  • 存储大小,最大4KB
  • http请求的时候需要发送的服务端,增加请求的数量
  • 只能使用document.cookie = '' 来修改,太过于简陋

cookie 不设置失效时间的话,关闭当前页面之后就会消失。 ​

localstorage 和 sessionstorage

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

  • API简单易用setItem,getItem

  • 不会随着http请求被发送出去

  • localstorage 数据会永久存储,除非代码或手动删除

  • sessionStorage 数据只存在于当前会话,浏览器关闭则清空

  • 一般localstorage会更多一些

Http

http 常见状态码有哪些

分类

  • 1XX 服务器收到请求
  • 2XX 请求成功
  • 3XX 重定向
  • 4XX 客户端错误
  • 5XX 服务端错误

常见状态码

  • 200 请求成功
  • 301 永久重定向(配合location,浏览器自动处理)
  • 302 临时重定向(配合location,浏览器自动处理)
  • 304 资源请求未修改
  • 404 资源未找到
  • 403 没有权限
  • 500 服务器错误
  • 504 网关超时

什么是Restful API

传统的methods

  • GET获取服务器数据
  • POST 向服务器提交数据

现在的 methods

get post patch/put delete

Restful API 设计,把每个URL当做一个唯一的资源 ​

http 常见有哪些header

  • 常见的 request headers
    • Accept 浏览器可接收的数据格式
    • Accept-Encoding 浏览器可接收的压缩算法,如 gzip
    • Accept-Language 浏览器可以接收的语言,如zh-CN
    • Connection: keep-alive 一次TCP连接可以重复使用
    • cookie 同域每次请求都会带上cookie
    • Host
    • User-Agent 简称UA,浏览器信息
    • Content-Type 发送数据的格式,如application/json
  • 常见的 response headers
    • Content-Type: 返回数据的格式
    • Content-length: 返回数据的大小,多少字节
    • Content-Encoding 返回数据的压缩算法 如gzip
    • Set-Cookie

缓存相关的headers

  • Cache-Control Expires
  • Last-Modified If-Modified-Since
  • Etag If-None-Match

http 缓存

强制缓存 image.png Cache-Control ​

  • Response Headers中
  • 控制强制缓存的逻辑

image.png

  • max-age
  • no-cache 不用强制缓存
  • no-store 不用强制缓存,也不用服务端的缓存
  • private
  • public

Expires 过时的缓存机制,已经被cache-control代替 ​

http 协商缓存

  • 服务端缓存策略
  • 服务端判断客户端资源,是否和服务端资源一样
  • 一致则返回304,否则返回200和最新资源

image.png

资源标识,

  • 在response Headers 中,有两种
  • Last-Modified 资源最后的修改时间
  • Etag 资源的唯一标识(一个字符串,类似人类的指纹)

Last-Modified(If-Modified-Since) image.png

Etag(If-None-Match) image.png

Last-Modified 和 Etag

  • 会有限使用Etag
  • Last-Modified 只能精确到秒级
  • 如果资源被重复生成,而内容不变,则Etag更精准

image.png

  • 正常操作:地址栏输入url,跳转连接,前进后退等
  • 手动刷新:F5,点击刷新按钮,点击菜单刷新
  • 强制刷新:ctrl + F5

正常操作:强制缓存有效,协商缓存有效 手动刷新:强制缓存失效,协商缓存有效 强制刷新:强制缓存失效,协商缓存失效 ​

https

http和https http是明文传输,敏感信息容易被中间劫持 https = http + 加密,劫持之后也无法解密 现代浏览器一开始强制https协议 ​

对称加密: 一个key可以负责加密,解密 非对称加密: 一个key,A加密之后,只能用B来解密 https同时用到了这两种加密方式 ​

https 过程

https 证书

  • 中间人攻击,
  • 使用第三方证书
  • 浏览器校验证书

image.png

开发环境

  • git
  • 调试工具
  • 抓包
  • webpack babel
  • linux 常用命令

运行环境

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

加载资源的形式

  • html代码
  • 媒体文件,如图片、视频
  • javascript css

加载资源的过程

  • DNS解析: 域名->IP地址
  • 浏览器根据IP地址向服务器发起http请求
  • 服务器处理Http请求,并返回给浏览器

渲染过程

  • 根据HTML代码生成DOM Tree
  • 根据CSS代码生成CSSOM
  • 将DOM Tree 和 CSSOM 整合出 Render Tree
  • 根据RenderTree 渲染页面
  • 遇到 script标签,则暂停渲染,有限加载并执行JS代码,完成再继续
  • 直至把 Render Tree 渲染完成

为何把link放在Head中

在DOM生成完成之前加载,渲染的时候就可以直接按照样式渲染页面 ​

为何把JS放在body最后

渲染初始化框架之后在解析js,然后执行js逻辑 ​

window.onload 和 DOMContentLoaded 有什么区别

window.onload 页面中的全部资源加载完才会执行,包括图片、视频等 DOMContentLoaded DOM渲染完即可执行,此时图片视频可能还没有加载完 document ​

前端性能优化有哪些方式

原则

  • 多使用内存、缓存或其他方法
  • 减少CPU计算量,减少网络加载耗时
  • 空间换时间

从何入手

  • 让加载更快
    • 减少资源体积: 压缩代码 webpack gzip
    • 减少访问次数:合并代码,SSR服务器端缓存渲染,缓存
    • 使用更快的网络:CDN
  • 让渲染更快
    • CSS放在head中,JS放在body最下面
    • 今早开始执行JS,用DOMContentLoaded触发
    • 懒加载(图片懒加载,上滑加载更多)
    • 对DOM查询进行缓存
    • 频繁DOM操作,合并到一起插入DOM
    • 节流,防抖

资源合并 a.js b.js c.js 合并成一个abc.js 缓存:

  • 静态资源加hash后缀,根据文件内容计算hash
  • 文件内容不变,则has不变,则url不变
  • url和文件不变,则会自动触发http缓存机制,返回304

手写防抖

function debounce (fn, delay = 100) {
	let timer = null 
  
  return function () {
  	if (timer) {
    	clearTimeout(timer)
    }
    
    timer = setTimeout(() => {
    	fm.apply(this, arguments)
      timer = null
    }, delay)
  }
}

节流 throttle

function throttle (fn , delay = 100) {
	let timer = null
  
  return function () {
  	if (timer){
      return
    }
    
    timer = setTimeout(() => {
    	fn.apply(this, arguments)
      timer = null
    }, delay)
  }
}

XSS 跨站请求攻击

  • 替换特殊字符,如< 变为 < > 变为>
  • 前端需要替换,后端也需要替换

XSRF 攻击

image.png

  • 使用post接口
  • 增加验证,例如密码,短信验证码,指纹

面试真题

var let const 区别

  • var 是es5语法,let const 是es6 语法,var有变量提升
  • var 和 let 是变量,可修改;const 是常量不可修改
  • let const有块级作用域,var 没有

typeof 能判断那些类型

  • undefined,string,number,boolean,symbol
  • object (typeof null === ‘object’)
  • function

列举强制类型转化和隐式类型转换

  • 强制:parseInt parseFloat,toString
  • 隐式:if,逻辑运算,==,+字符串拼接

手写深度比较

function isObject () {
	return typeof obj === 'object' && obj !== null
}

function isEqual (obj1, obj2) {
  if (!isObject(obj1) || !isObject(obj2)) {
  	return obj1 === obj2
  }
  
  if (obj1 === obj2) {
  	return true
  }
  
  const obj1Keys = Object.keys(obj1)
  const obj2Keys = Object.keys(obj2)
  
  if (obj1Keys.length === obj2Keys.length) {
  	return false
  }
  
  for ( let key in obj1) {
  	const res = isEqual(obj1[key], obj2[key])
    if (!res) {
    	return false
    }
  }
  
  return true
}

哪些数组方法没有副作用

concat map slice filter ​

非纯函数 push pop shift unshift forEach some every reduce ​

函数声明和函数表达式的区别

  • 函数声明: function fn() {...}
  • 函数表达式:const fn = function () {...}
  • 函数声明会在代码执行前进行预加载,而函数表达式不会

new Object 和 Object.create的区别

  • {} 等同于 new Object() 原型 Object.prototype
  • Object.create(null) 没有原型
  • Object.create({…}) 可以指定原型

数组打平

function flat (arr) {
	const isDeep = arr.some(item => item instanceof Array)
  if (!isDeep) {
  	return arr
  }
  
  const res = [].concat(arr)
  return flat(res)
}

介绍 RAF requestAnimateFrame

image.png

Map 和 Set

有序和无序

js中, Object 是无序的,Array是有序的 ​

有序:操作慢 无序:操作快,但无序 如何结合两者优点呢:二叉树及其变种 ​

Map 和 Object 区别

  • API不同,Map可以以任意类型作为key
  • Map 是有序结构(重要)
  • Map 操作同样很快

image.png image.png

Set 和 Array 区别

  • API不同
  • Set元素不能重复
  • Set 是无序结构操作很快

image.png

WeakMap 和 WeakSet

  • 弱引用,方式内存泄漏
  • weakSMap 只能使用对象作为key,WeakSet 只能用对象作为value
  • 没有forEach 和 size,只能用 add delete has

DONE