1.怎么理解H5语义化

  1. 用正确的标签做正确的事,方便我们开发时阅读以及文档式的阅读。
  2. 有利于搜索引擎的解析和网站的SEO。
  3. 归根结底还是让程序或者机器去理解网页,也能更好的让人去理解网页。

2. H5与H4的区别

  1. 文件类型声明不同,H5是
  2. 标准不同,H4基于SGML,H5有自己的标准
  3. 新增语义化标签,废除一些其他标签
  4. 新增audio和video两个音频视频标签支持
  5. 新增表单属性
  6. script新增async属性
  7. webstorage与indexDB
  8. application cache,为PWA提供底层支持

3. src与href的区别

href是超文本引用,建立当前文档与引用链接之间的联系,如a标签中的href会跳转界面,Link中的href也会并行下载,而不会影响当前文档的处理;src则会暂停处理,将资源下载到当前文档,等待下载执行完毕。

4. 从load和DOMContentLoaded到defer与async

load和DOMContentLoaded。前者代表所有的同步代码加载完毕;后者代表了最符合我们直觉的页面打开时间。

ios 嵌套h5 js报错 h5嵌入和h5跳转区别_父类

再加上defer和async,两者都是异步下载,前者是指我们HTML文档加载完毕后在执行,执行完之后再触发DOMContentLoaded;后者是指异步下载完成后就立刻执行。

因此,async完全即可能在DCL之前执行,也可能在DCL之后执行,因为DCL执行是看HTML解析完毕,是不会看async的;但是一定会在defer之后执行,因为不论defer下载完成在DCL之前或之后,都会等defer下载执行结束再加载DCL。

5. CSS 如何阻塞文档解析

如果浏览器尚未完成CSS加载解析,这时却需要执行JS脚本,那么浏览器会暂停JS脚本和文档解析,转而先进行CSS加载解析,再去执行JS,再去执行文档解析。因为JS可能会处理样式信息,因此CSSOM的构建要在JS加载前。

6. 水平居中

行内元素:text-align: center
块级元素:确定宽度的话,margin: 0 auto
绝对定位+transform/margin;
flex,justify-content;
grid,justify-self;

7. 垂直居中

文字:line-height === 容器高
绝对定位+transform/margin
flex,align-items
grid,align-self
flex,margin: auto 0

8. 清除浮动的方法与原理

在浮动元素后面加个空div/伪元素,设置clear: left/right/both,告诉浏览器,我的左边/右边/两边不能存在浮动元素,不能改变浮动的位置,伪元素只能顺位下沉,撑起父元素高度。

为父元素设置新的BFC,计算BFC高度需要考虑浮动元素的高度

BFC:
浮动元素和绝对定位元素,非块级盒子的块级容器(例如 inline-blocks, table-cells, 和 table-captions),以及overflow值不为"visiable"的块级盒子,都会为他们的内容创建新的BFC(Block Fromatting Context, 即块级格式上下文)

9. display: inline-block

空白问题,两个同级行内块中间会计算空白元素的宽度,解决办法:

父元素设置字体大小0,子元素再还回来;

左边设置float: left

10. position

static
relative
absolute
fixed
sticky,粘性布局,当元素在屏幕内,表现为relative,就要滚出显示器屏幕的时候,表现为fixed。

11. 0.1+0.2为什么不等于0.3

0.1和0.2在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出现了精度的损失,一般只要之差小于Number.EPSILON即可。

12. instanceof判断基本类型

class Instance {
  static [Symbol.hasInstance](x) {
    return typeof x === 'number' 
  }
}
12 instanceof Instance

13. 类型转换

==判断规则:

  1. 首先看类型想不想同,不同下一步。
  2. 接着看是否是null和undefined,是则true
  3. 接着看是否是number和string,是则转化number
  4. 接着看看是否有一方为bool,是则转化为number
  5. 最后看一边是否为对象,是的话转化为基本类型

14. 几种继承

  1. 类式继承,在字类构造函数中调用父类构造函数,使得父类构造函数属性绑定到字类实例。缺点是无法绑定原型方法。
  2. 原型继承,字类构造函数的prototype指向父类实例,字类构造函数的prototype的constructor指向构造函数。父类引用类型属性被字类实例共享。
  3. 组合继承,缺点,父类构造函数调用了两次,原型继承不需要继承父类实例属性。
  4. 组合寄生,通过object.create创建一个没有属性却有着父类原型方法的实例,赋值给字类构造函数prototype。
  5. es6继承与es5继承的区别
  1. 首先,es5的继承实质是先创造子类的实例对象,然后将父类的方法添加到this上面。es6则不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后用子类的构造函数修改this。
  2. 字类实例的dander proto指向不同,ES5由于组合寄生,实例的dander proto指向Function.prototype,ES6指向父类。

15. 闭包

通俗:在某个函数作用域外面,却能够拿到这个函数作用域中的变量。
举例:a 函数套 b 函数,然后 a 函数返回 b 函数,这样 b 函数在 a 函数以外的地方执行时,依然能访问 a 函数的作用域
专业:当前函数作用域存在指向父级作用域的引用
特点:即使函数执行完毕,变量的值始终保存在内存中不会释放

16. BigInt

JS 中所有数字都以双精度64位浮点格式表示,无法精确表示的非常大的整数将自动四舍五入,因此:

9007199254740991+2 = 9007199254740992

因此我们可以加n转化成BigInt类型,来进行大数操作。

当然对于不大于:Number.MAX_SAFE_INTEGER的值,我们也可以使用BigInt,这时隐式转换会讲BigInt转换成number:

> 22n==22
true

17. 数组的高阶函数

  1. map和forEach,后者return是没有作用的,一般用于遍历而不修改,也可以修改,但是会修改原数组;前者会开辟新的内存空间,不会对原数组进行修改。函数第二个参数都是this绑定的对象,可选;回调都接受三个参数。
  2. reduce,回调接收四个参数,用reduce实现一个map:
Array.prototype.mymap = function(fn, thisA){
  let res = []
  self = thisA || null
  this.reduce((t,v,i,a) =>{
    res.push(fn.call(self, v, i, a))
  }, null)
  return res
}
  1. filter, 接受一个函数参数,选true的留下
  2. some和every接收两个参数,一个回调,一个this对象,注意箭头函数是有问题的。

18. 类数组

有arguments;getElementByTagName,getElementByName,getElementByClassName获取的HTTP collection;querySlector获得的nodeList

转化为数组:

  1. Array.prototype.slice.call()
  2. Array.from()

19. 判断数组

  1. n instanceof === Array
  2. Array.isArray(n) === true
  3. Object.prototype.toString.call(n) === ‘[object Array]’

20. 判断数组/字符串中是否有某个值

// 数组
let b = arr.find(a=>a==3) // findIndex
arr.includes(a)
arr.indexOf(a)
//字符串
indexOf, includes
startsWith, endsWith //开头结尾

21. 数组扁平化方法

  1. ary = arr.flat(Infinity);
  2. stringify + replace
let a = [1,2,3,[3,[4,5]]]
a.replace(/[\[\]]/g, '').split(',')
  1. 递归
function flat(arr){
  return arr.reduce((t,v) => t.concat(Array.isArray(v) ? flat(v) : v ), [])
}

22. 手写new, call, apply, bind

看这里

23. 深拷贝

序列化存在的问题,undefined,Symbol等会被忽略,无法拷贝函数,不认识Map,Set等。还有循环引用的问题。

function dcopy(obj){
  if(typeof obj!=='object' && typeof obj!=='function') return obj
  const tar = Array.isArray(obj)?[]:{}
  for(let i in obj){
    if(obj.hasOwnProperty(i)){
      tar[i] = dcopy(obj[i])
    }
  }
  return tar 
}
  1. 循环引用的问题,使用WeakMap:
function dcopy(obj){
  let map = new WeakMap()
  let help = function(obj){
    if(typeof obj!=='object' && typeof obj!=='function') return obj
    if(map.get(obj)) return obj
    map.set(obj, true)
    const tar = Array.isArray(obj)?[]:{}
    for(let i in obj){
      if(obj.hasOwnProperty(i)){
        tar[i] = help(obj[i])
      }
    }
    return tar 
  }
  return help(obj)
}
  1. 特殊对象,要分类型考虑,首先要拿到构造函数来创建实例,而不仅仅是判断数组和对象,如对于Set:
let proto = Object.getPrototypeOf(obj)
let tar = new proto.constructor()
if(Object.prototype.toString.call(tar)=='[object Set]') {
  obj.forEach(v=>{
    tar.add(dcopy(v))
  })
}
  1. 函数处理,分为箭头函数和普通函数,前者直接返回,后者要用正则转化字符串再新建函数,这里不写了。
  2. 不可遍历对象,对于不可遍历对象还要做单独处理,这里直接看这篇文章