前端面试、笔试题总结


HTML部分

1. HTML5新特性,移除了哪些元素
1. 语义化标签
header、nav、footer、section、article(页面独立的内容区域)
2. 增强型表单

输入类型描述
color主要用于选取颜色
date从一个日期选择器选择一个日期
datetime选择一个日期(UTC 时间)
datetime-local选择一个日期和时间 (无时区)
email包含 e-mail 地址的输入域
month输入月份
number数值的输入域
range一定范围内数字值的输入域
search用于搜索域
tel定义输入电话号码字段
time选择一个时间
urlurl输入域
week选择周和年
  1. 视频和音频
  2. canvas绘图
  3. 地理位置
  4. web Storage(local、session)
  5. web socket
  6. 拖拽API dragstart、drag、dragend
  7. 标签添加conteneditable可编辑

移除的元素:
纯表现的元素:big、center、basefont、font、s、strike、tt、u
对可用性产生负面影响的元素:frame、frameset、noframes
2. iframe的缺点
1.对于SEO不友好
2.会增加更多的请求,阻塞页面
3.产生很多的页面,不易管理
3 常用行内元素和块级元素
块级元素:address , div , dl , form , h1 , h2 , h3 , h4 , h5 , h6 , menu , ol , p , table , ul , li
内联元素:a , b , em , img , input , label , select , small , span , textarea
可变元素:button(根据上下文关系确定)
空元素:br、hr、input、img
4 webWorker
在HTML页面中执行脚本时,页面的状态是不可响应的.webWorker是运行在后台的JavaScript,不会影响HTML页面的性能.可以继续做想做的事,点击、选取内容等.

API:

  • 主线程
    Worker.onerror:指定 error 事件的监听函数。
    Worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
    Worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
    Worker.postMessage():向 Worker 线程发送消息。
    Worker.terminate():立即终止 Worker 线程。
// 开启一个web worker线程(加载js文件,创建worker对象)
var worker =new Worker("./index.js"); 
		
		// 传递数据给work
		worker.postMessage(5000);     
		
		console.log("continue ...");
		
		// 接收work返回的数据
		worker.onmessage =function(evt){    
			console.log("main now is: " + new Date());            
			console.log(evt.data);              
		}
		Worker.onerror=function(err){
 		 console.log(err);
		}
	//关闭worker,由于worker是新开辟一个空间,在不使用时记得关闭
	Worker.terminate()

  • work
    Web Worker 有自己的全局对象,不是主线程的window,而是一个专门为 Worker 定制的全局对象。因此定义在window上面的对象和方法不是全部都可以使用。

self.name: Worker 的名字。该属性只读,由构造函数指定。
self.onmessage:指定message事件的监听函数。
self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
self.close():关闭 Worker 线程。
self.postMessage():向产生这个 Worker 线程发送消息。
self.importScripts():加载 JS 脚本。


JavaScript部分

1. new 的原理

1.	创建一个新对象
2.	将构造函数的作用域赋给新对象  newObj.__proto__=obj.prototype
3.	将属性,方法赋给新对象(调用构造函数将obj的this指向new)
4.	返回新对象

2. 自加

	var a=5
	var b=6 => a+++b=11 , a=6

3.比较大小

  • ’132’-13 ‘132’-‘13’ 转化为数字进行计算 只要是数字 如果是数组一个值也可以 其他情况就位NaN
  • 字符串之间比较比较第一个字符的ascii码(如果相同依次比较)
  • 数字与数字字符串之间比较将字符串转化为数字比较
  • 数字与非数字字符串之间比较返回false
console.log(5<'6')true
console.log('a'>'b')false
console.log('a'>4)false
  • 字符之间比较相等会将字符转化为数字比较(排除严格相等的情况)
console.log(1=='1')true

4. 数组去重的方法

//1.

Array.prototype.distanct=function(){
    var arr=this
    var len=arr.length
    for(let i=0;i<len;i++)
    {
        for (let j=i+1;j<len;j++){
            if(arr[i]===arr[j]){
                arr.splice(j,1)//去除
                len--;//去除过后 减1
                j--;//多判断一次 有可能有连续相等的
            }
        }
    }
    return arr
}

var arr=[123,undefined,null,'a',"aa",undefined]
arr.distanct()

//2
new Array(...new Set(arr));
Array.from(new Set(arr))

4. 对象解构
var obj={a:1,b:{x:1}} ,将x 取出

var obj={a:1,b:{x:1}}
var {b:{x:c}}=obj
console.log(c)

5. parseInt

parseInt(item,index) 第一个参数为需要转化的数,第二个参数为表示其进制数,如果不能表示则返回NaN

6. 闭包
定义:
1.函数的嵌套构成闭包,内部函数能够访问外部函数的变量

2.内部函数存放对外部函数变量引用的对象[[scope]]

作用:
1.封装,将数据和相关操作隐藏在内部,是外部通过暴露的接口来操作内部数据,不能任意操作数据,只能通过指定的接口操作
2.延长变量声明周期.

对象中的匿名函数不能访问函数对象本身 该案例中第一个fun和第三个fun 相等,第二个fun 为新创建的匿名函数.

function fun(n,o) {
    console.log(o)
    return {
      fun:function(m){
        return fun(m,n);
      }
    };
  }
  var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);//undefined,0,0,0
  var b = fun(0).fun(1).fun(2).fun(3);//undefined,0,1,2
  var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,0,1,1

模块化开发:

(function(){
    var a=0;
    
    function add(){
        a++;
        console.log(a);
    }

    function minus(){
        a--;
        console.log(a);
    }
    window.Moudle={add,minus};
})()

使用闭包需要注意的:变量会一直保存在内存中,造成页面性能差,还可能造成内存泄漏,解决方法:在退出函数时删除全部变量

7. 事件冒泡和事件委托

<ul id="ul">
<li class="food"></li>
<li></li>
<li></li>
</ul>

document.getElementsByClassName('food')[0].onclick=function(e){
  e.stopPropagation()
  console.log('香蕉打印了')
}
document.getElementById('ul').onclick=function(e){
  console.log("ul打印了")
}

在点击 某个li 的时候ul也会相应的触发事件(事件的冒泡,都含有事件),这个时候我们可能就需要阻止事件的冒泡 e.stopPropagation()
如果我们想为每一个li添加点击事件,如果遍历一次添加会非常麻烦,这个时候我们可以为ul添加一个点击事件即可(事件的委托)
8. 原型继承
JS中的继承
9. 深拷贝
基本数据类型存放在栈中,引用类型存放在堆中,地址存放在栈中
基本数据类型:Boolean、String、null、undefined、number
引用类型:object function array
typeof 能判断的类型:undefined、number、Boolean、function、object、string

//检测类型
function getType(obj) {
  let map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object'

  };
  return map[Object.prototype.toString.call(obj)];
}

let deepclone = function (data) {
    let type=getType(data);
    let obj;
    if(type=='object'){
      obj={};
      for(let item in data){
        if(data.hasOwnProperty(item)){
          obj[item]=deepclone(data[item]);
        }
      }
    }
    else if(type=='array'){
      obj=[];
      for(let item of data){
        obj.push(deepclone(item));
      }
    }
    else return data;
    return obj;
}


let obj=[1,2,3]
let newobj=deepclone(obj)
console.log(obj,newobj)

10. 作用域与作用域链
作用域看做一种规则,规定了在何处以及如何查找表示符
各个作用域的嵌套关系组成作用域链,标识符解析就是沿着作用域链一层一层查找的过程,而作用域链保证了对变量与函数的有序访问
每次执行函数都会生成执行上下文(环境)推入环境栈中,执行完毕就从执行栈中弹出,定义了变量对象,里边保存了有权访问的变量和函数

var a = 1;
var b = 2;
function fn(x){ //fn 中作用域链为 fn->全局
    var a = 10;
    function bar(x){ //bar 中作用域链为 bar->fn->全局
        var a = 100;
        b = x + a;
        return b;
    }
    bar(20);
    bar(200);
}
fn(0);

11. 防抖和节流
处理事件时如果无限次的让其调用会加重浏览器负担,降低用户体验,这个时候就要用到防抖和节流
防抖:持续触发事件,只有在一定时间内没有再次触发时事件处理函数才会执行

 function debounce(fn, wait) {
    let timeout = null;
    return function () {
      clearTimeout(timeout);
      timeout = setTimeout(fn, wait);
    }

  }
  function handle() {
    console.log(123)
  }
  document.addEventListener('scroll', jitter(handle, 1000))

节流:持续触发事件,一段时间内只触发一次

  function throttle(fn,wait){
   let prev=Date.now()
   return function(){
     let now=Date.now()
     if(now-prev>=wait){
       fn();
       prev=Date.now()
     }
   }
 }
  function handle() {
    console.log(123)
  }
  document.addEventListener('scroll', throttle(handle, 1000))

12. Math方法
Math.round() 四舍五入 ,如果为负数就是变为正数四舍五入,然后加负号

Math.round(-1.5)= - 1
Math.round(-1.6)= - 2

13. 字符串方法
substr(index,length) 返回子串
substring(from,to) 取两个范围内的,也是左闭右开
slice()
concat()
indexof() 第二个参数可以指定开始位置
lastIndexof()
search() 可以使用正则
replace()
14. 数组方法
arr.splice(index,length,item) 删除数组值,第三个参数往后为添加内容(添加到index处),返回值为删除的数组,如果没有删除返回空数组
arr.unshift(item,item) 在数组最前边添加内容,返回添加后的长度
arr.shift(item,item) 删除数组第一个值并返回删除的值
arr.reduce(function(total,currentValue,currentIndex,arr){ return total+currentValue},1)
arr.filter(function(item,index,arr){}) 筛选
arr.map(function(item,index,arr){})遍历,有返回值
15. this是什么
this是包含它的函数被调用时的所属对象(别人总结的,很精简)
16. 事件流模型
1.事件冒泡
2.事件捕捉
3.“DOM事件流”:三个阶段:事件捕捉,目标阶段,事件冒泡
16. 常见DOM操作
常见DOM操作
17. 常用content-type
上传文件所需 enctype =‘multipart/form-data’
application/json
表单上传默认 application/x-www-form-urlencoded
18. 讲一讲原型和原型链
原型:每个函数都有一个prototype属性(显式原型),指向一个Object的空实例对象(原型对象),原型对象里边有个constructor属性,指向函数对象 Fun.prototype.constructor===Fun,每个实例对象有一个__proto__属性(隐式原型),指向与函数对象相同的原型对象Fun.prototype===new Fun().__proto__,空实例对象的原型对象为Object函数对象的原型对象,如果在往上查找Object.prototype.__proto__===null.
原型链:查找对象属性时,现在自身属性中查找,找到返回,如果没有找到,则沿着__proto__链查找,找到返回,没有找到,返回undefined
在这里插入图片描述
关键点:
1.所有函数都是Function的实例
2.函数的显式原型都是Object实例对象(除Object外)
3.函数既有显式原型,又有隐式原型
在这里插入图片描述
19. JavaScript的垃圾回收机制
JS会定期的清除那些不在使用的变量,释放其内存空间
一般有两种方法:
1.标记清除
垃圾回收器在运行时会为所有变量添加标记,会清除哪些引用了的变量,最后剩下的标记变量予以清除
2.引用计数
变量被引用一次,引用数+1,其引用变量的值变为另外一个值则引用计数-1 最后清除引用计数为0的变量,释放其内存空间

但是引用计数的方法会造成一些问题:循环调用、相互指向.造成引用计数永远不可能为0,造成内存的泄漏,所以现在基本常用的是标记清除的方法
在变量不使用是使用null将变量置为空地址
20. JavaScript去除字符串两端空格的兼容性写法

if(!String.prototype.trim){
    String.prototype.trim=function(){
        return this.replace(/^[\s\ufeff\xA0]+[\s\ufeff]\xA0]+$/g,'');
    }
}

\ufeff:字节次序标记字符,为空白字符
\xA0:不间断空白字符&nbsp;
21. 上下文
上下文对象为一个容器,里边存放了当前执行环境的属性方法等,分为全局上下文和函数上下文,使用栈结构进行管理,执行函数时将其放入栈中(压栈),执行完毕出栈,全局上下文一直在栈底
21. JS执行机制
JS执行机制
需要注意的是:JavaScript虽然是单线程的,但是执行环境并不是单线程的(比如浏览器),消息队列在消息线程执行。
22. iterator遍历器对象
为不同数据接口提供访问机制,数据结构部署iterator接口,就可以完成遍历操作
对象不存在默认的iterator接口是因为对象是无序的,遍历顺序是不固定的,不知道哪个先遍历,哪个后遍历

原生具备iterator接口的数据结构有:
TypeArray
Array
Set
Map
String
函数的arguments对象(类数组)

for...of操作主要调用的就是iterator接口

数组原生遍历器实现

//数组的遍历器实现
function makeIterator(data) {
    var index = 0;
    return {
        next: function () {
            return index < data.length ?
                { 'value': data[index++], 'done': false } :
                { 'value': undefined, 'done': true }
        }
    }
}

var it = makeIterator([1, 2, 3]);

为object实现iterator接口

Object.prototype[Symbol.iterator] = function (obj) {
    var obj = this;
    var keys = Object.keys(obj);
    var index=0;
    function next() {
    
        return index<keys.length?{'value':obj[keys[index++]],'done':false}:
        {'value':undefined,'done':true}
    }

    return { next };
}

var obj = { a: 1, b: 2 }
//调用遍历器生成迭代对象 
var it = obj[Symbol.iterator]();

22. 函数的arguments对象
是一个类数组,存放函数调用时的实参,可以使用类似与数组的方式调用,里边有个callee属性指向函数本身
23. 函数的柯里化
函数的柯里化是将多参数的函数转化为单一参数的函数,返回接收余下参数而且返回结果的新函数
作用:
1.参数的复用

function check(reg){
    return function(txt){
        return reg.test(txt);
    }
}
var checkNumber=check(/\d+/g);

2.提前确认
3.延迟运行

实现柯里化

function curring(fn){
    var args=[];
    return function(){
        if(arguments.length===0){
            return fn.apply(this,args);
        }
        else{
            [].push.apply(args,arguments);
        }
    }
}
function add(){
    var total=0;
    for(let i=0;i<arguments.length;i++){
        total+=arguments[i];
    }
    return total;
}

var it=curring(add);
it(1)
it(2)
it(3)
console.log(it())

柯里化实现func(1,2)(3) 6的形式:

function add(){
    var args=[].slice.call(arguments);
    function cb(){
        var newArgs=args.concat([].slice.call(arguments));
        return add.apply(null,newArgs)
    }
    cb.toString=function(){
       return  args.reduce(function(a,b){
            return a+b
        })
    }
    return cb
}

console.log(add(1,2)(3))

CSS部分

  1. 优雅降级和渐进增强
    渐进增强(Progressive Enhancement):一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。
    优雅降级(Graceful Degradation):一开始就构建站点的完整功能,然后针对浏览器测试和修复。比如一开始使用 CSS3 的特性构建了一个应用,然后逐步针对各大浏览器进行 hack 使其可以在低版本浏览器上正常浏览。
  2. CSS选择器优先级
    在这里插入图片描述
  3. CSS清除浮动的方法
    如果布局时父元素高度设置为auto,内部元素有使用float的情况可能会造成高度崩坍,这个时候就要用到清除浮动
    1. 为父元素设置overflow:auto/overflow:hidden
    2. 在最后添加空元素设置clear:both
    3. 为父元素添加伪元素设置不可见(visibility:hidden)和clear:both
    4. 为父元素设置双伪元素均设置clear:both
  4. CSS3新特性
    动画、渐变、阴影(text-shadow、box-shadow)、圆角、字体(@font-face)、transform、过渡(transition)、弹性布局(flex)

动画示例:

#btn{
    width: 100px;
    height: 100px;
    /*   名字、持续时间、速度曲线、延迟、无穷次、反向播放*/
   animation: trans 10s linear 1s infinite alternate;
}
@keyframes trans{
    from {
        background-color: red;
    }
    to{
        background-color: blue;
    }
}
  1. CSShack
    由于不同浏览器、浏览器版本对css的支持和解析不同,可能会造成在不同浏览器或浏览器版本上有不同的页面效果,这个时候就要针对不同的浏览器写特定版本的样式,这个过程就叫做csshack

方式1:条件注释法
IE浏览器专有的hack方式,微软官方推荐

  <!--[if IE]>
    只在IE显示
  <![endif]-->

方式2:类内属性前缀法
方式3:选择器前缀法
6. margin\padding的百分比值是相对于什么而定的
margin、padding的百分比都是相对于父元素的宽度而定的
解释:现代浏览器一般都为横向排版,宽度一定,按照宽度取值不会随着内容的变化而变化,而高度则是无限延伸的,如果根据高度取值,那么就会造成无限循环,高度增加适应margin、padding的增加,margin、padding增加造成高度的拓展
7. 如何开启硬件加速
使用3D效果开启加速

 div{
    transform: translate3d(100px,100px,100px) rotate3d() scale3d();
  }

如果不需要使用3d效果,可以使用

 div{
   -moz-transform: translateZ(0);
   -webkit-transform: translateZ(0);
   -o-transform: translateZ(0);
   -ms-transform: translateZ(0);
   transform:translateZ(0);
  }

通过-webkit-transform:transition3d/translateZ开启GPU硬件加速之后,有些时候可能会导致浏览器频繁闪烁或抖动,可以尝试以下办法解决之:

backface-visibility:hidden;
-webkit-backface-visibility:hidden;
-webkit-perspective:1000;
perspective:1000;
  1. 无缝滚动的原理,以及实现
    原理:创建一个与滚动内容相同的替换内容拼接到滚动内容后,当滚动内容到达最后内容时将最开始的内容放到起点,外边假装盒子限制可查看范围,在视觉上就实现了无缝滚动
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="./index.js"></script>
  <style>
#box ol li {height: 20px;}
#ol1{position: absolute;}
  </style>
</head>

<body>
  <div id="box" style="width: 100px;height: 100px;position: relative;overflow: hidden;">
    <ol  id="ol1">
      <li>第一行</li>
      <li>第二行</li>
      <li>第三行</li>
      <li>....</li>
    </ol>
   
  </div>
</body>

</html>

<script>
  //原理:创建一个与原滚动内容相同的内容拼接到原来后边,最后内容到达后再将最开始的内容放到起点,则构成了无缝滚动的效果
  var box = document.getElementById('box')
  var ol1 = document.getElementById('ol1')
  ol1.innerHTML += ol1.innerHTML
  var timer = null;
  timer = setInterval(myscroll, 1);
  var num = 0;
  function myscroll() {
  
    num -= 0.25;
    num <=  -ol1.offsetHeight/2 ? num = 0 : num;
    ol1.style.top = num + 'px'

  }
  setInterval(myscroll, 50);
  box.onmouseover = function () {
    clearInterval(timer);
  }
  box.onmouseout = function () {
    timer = setInterval(myscroll, 1);
  }
</script>
  1. 居中方式比较

水平居中:

  • 行内元素,父元素是块级元素—>text-align:center
  • 块级元素且定宽设置margin:0 auto,如果不定宽将子元素设置为行内,与1相同
  • 定位,块级元素可以使用transform
  • flex-box

垂直居中:

  • 行内元素:单行设置行高等于父元素高度,多行为父元素设置:display:table-cell;vertical:middle
  • flex-box
  • 定位和transform

上下左右居中:

  • postion:absolute;top:0;left:0;right:0;bottom:0;margin:auto
  • flex-box
  • transform
  1. margin的坍塌和合并问题

高度坍塌:
父元素的高度一般由子元素撑开,但是当子元素存在浮动的情况下,则会出现父元素高度塌陷的问题
解决方法(将父元素设置为BFC:块级格式上下文):

  • 父元素脱离文档流
  • 父元素设置overflow:hidden\overflow:auto\overflow:scroll
  • 父元素设置display 为inline-blocks/table-cell

margin合并:

  • 处于上下位置关系的两个div容器,在通过margin-top、margin-bottom改变间距时,如果两个属性的值相同时,则两容器间的距离就是这个值;如果两个属性的值不同,则取较大值作为两容器间的距离;
  • 子元素与父元素不存在间距分离时则父元素与子元素共用一个margin
  1. BFC和IFC

BFC:块级格式上下文,创建一个隔离的容器(一些规则),内部元素的布局不会影响到外部元素,反之如此.规则:

  1. 自动包含内部突出的浮动元素
  2. 包含块的左边界与块元素的左边界对齐,当紧挨浮动元素时,该规则失效,并且会收缩自身适应浮动元素剩余区域
    如何产生BFC:
  • 父元素设置脱离文档流
  • 父元素设置overflow:hidden/auto/scroll
  • display的值为table-cell, inline-block中的任何一个。

作用:清除浮动,解除高度坍塌,解决margin合并问题,上文中也有提到

IFC:内联格式上下文,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响),会因内部有浮动元素而出现高度崩塌的问题,解决方法在上面.

GFC:网格格式上下文
FFC:自适应格式上下文

  1. 圣杯布局和双飞翼布局

圣杯布局和双飞翼布局都是两边固定,中间自适应的布局方式,效果如下:
在这里插入图片描述
圣杯布局:

 <style>
  *{margin: 0;padding: 0;}
  #box{padding: 0 150px;}
  #middle{width: 100%;background-color: red;height: 100px;}
  #middle,#left,#right{float: left;}
  #left{width: 150px;height: 100px;background-color: coral;margin-left: -100%;position: relative;left: -150px;}
  #right{width: 150px;height: 100px;background-color: cyan;margin-left: -150px;position: relative;right: -150px;}
  </style>
</head>
<body>
  <div id="box">
    <div id="middle">middle</div>
    <div id="left">left</div>
    <div id="right">right</div>
  </div>
</body>

双飞翼布局:

 <style>
  *{margin: 0;padding: 0;}

  #middle{width: 100%;background-color: red;height: 100px;}
  #middle,#left,#right{float: left;}
  #left{width: 150px;height: 100px;background-color: coral;margin-left: -100%;}
  #right{width: 150px;height: 100px;background-color: cyan;margin-left: -150px;}
  #inner{margin:  0 150px;}
  </style>
</head>
<body>
  <div id="box">
    <div id="middle">
      <div id="inner">Main</div>
    </div>
    <div id="left">left</div>
    <div id="right">right</div>
  </div>
</body>
  1. 媒体查询

为对应的设备设置样式组
CSS媒体查询
,相当于or
only关键字防止老旧的浏览器不支持带媒体属性的查询而应用到给定的样式
and
all
orientation:landscape/portrait 设置横屏还是竖屏
常用的响应式宽度

@screen-sm-min:768px;
@screen-md-min:992px;
@screen-lg-min:1200px;
//手机
@media screen and (max-width:@screen-sm-min){font-size: 10px;}
//小屏幕、平板
@media screen and (min-width:@screen-sm-min){font-size: 12px;}
//中等屏幕
@media screen and (min-width:@screen-md-min){font-size: 14px;}
//大屏幕
@media screen and (min-width:@screen-lg-min){font-size: 16px;}
  1. haslayout

haslayout是IE的专有属性,拥有该属性的元素有自己的布局方式以及与其他元素的排布方式
默认开启的有
form、table、tr、td、input、img等

不带有layout属性的也可以通过控制属性开启
设置BFC的都可以开启layout、zoom:1等等


React部分

  1. 为什么虚拟dom 能提高性能

    虚拟dom 相当于一个对象,渲染是只需遍历该对象与原对象的区别进行局部渲染,不需要大量的dom操作(如果比较dom差距非常消耗性能),所以能较好的提高性能

不一定虚拟dom性能就一定比真实dom性能好,比如全部页面整个进行更换时,使用innerHTML性能更好

  1. diff算法

diff算法:结合虚拟dom使最少的操作进行页面重绘,比较新数据与旧数据的差距,然后更改虚拟dom,从而更改真实dom,达到局部更新的目的
传统diff算法,进行逐次循环递归比较找出差异,复杂度O(n³),react提出三大策略:

1.tree diff Web UI 中DOM节点跨层级的移动操作特别少,可以忽略不计
2.component diff 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。如果碰到不同直接判断不同,不继续向下判断
3.element diff 对于同一层级的一组子节点,它们可以通过唯一id进行区分。(key) 如果不同直接更改,减少diff算法的开销,如果相同可以复用

diff算法复杂度为O(n)
3. 生命周期
在这里插入图片描述
4. Flux
flux更多的是一种思想,是React的架构模式,
主要有四个部分

Action:是一个对象,保存动作类型,数据等
dispatch:派发器,接收Action,将Action派发到Store保存
store:数据层,用于保存数据,数据一旦改变触发change事件,更新页面
view:视图层

在这里插入图片描述
流程:view触发Actiondispatch接收Action,将其派发到store保存,store更新触发change事件,更新页面

  1. PureComponent和shouldComponentUpdate

PureComponent:纯组件,与Component区别不大,只是在更新时,内部实现了一个shouldComponentUpdate对state和props进行了浅比较,省去了多余的render操作,在效率上是高于Component的,但是其也有缺点,由于浅比较的原因,一些深层次的数据变化,是检测不到的,可能会出现错误,并且会影响子组件,推荐使用在只供展示的底端组件.(这也是react的一种性能优化方案)

  1. redux中间件的原理

改装dispatch、flux架构中正常的数据传递方式为Action为一个对象触发dispatch派发到reducer,但是如果在action到dispatch之间添加一个中间件,改装dispatch,使其可以传递function等参数(redux-thunk源码)

  1. 把数据统一放在redux管理,还是共享数据放在redux中管理

把数据统一管理,如果只是单纯的将共享数据放在redux中管理,在代码量上肯定会小的多,但是组中的数据管理就会存在state、props、redux,非常多,对于后期维护、差错等非常不友好

  1. ComponentWillReceiveProps的调用时机

props更新的时候(不是父组件向子组件传值的时候)

  1. 调用setState过后,发生了什么

调用setState过后会进行一个调和过程,会新生成一个React对象树,与之前的对象比较差异(diff),渲染页面.setState最后使用函数式调用,避免异步带来的影响,如果需要马上获取state,需要在setState回调中获取

this.setState(()=>{
	return{
		age:++ this.state.age
	}
})
  1. refs的作用是什么,在什么业务场景下使用过refs

作用:操作真实的dom结点
场景:

  • 图片渲染后,获取其宽高
  1. ref是一个函数,有什么好处

ref有两种写法,第一种是字符串的形式、第二种是函数的形式,推荐使用函数的形式
方便在销毁组件是更好的将其销毁,防止内存泄漏的问题

  1. 高阶组件怎么理解的,本质上是什么东西

高阶组件接收函数,返回其实也是一个函数,包装组件,达到共享数据的目的

  1. 受控组件和非受控组件

通过数据控制,驱动的组件为受控组件,不通过数据控制的组件为非受控组件

<input value={this.state.value} />//受控组件
<input ref={input=>this.input=input} />//非受控组件

受控组件更好,react本身就是一个数据驱动的框架,通过数据改变页面的方式更好

  1. 函数组件和Hooks函数组件与Hook
  2. 函数组件如何做性能优化

函数式组件性能可能高也可能低,高是因为是一个函数,不是类,没有各种生命周期,所以性能比较高,低是因为每次props改变都会重新执行函数,如果能够实现像类一样的shouldCompomentUpdate那么就能提高性能

可以使用React.memo(()=>{}) 带有shouldComponentUpdate 并且不带有各种生命周期,性能更好

  1. 哪个生命周期发送ajax

componentDidMount

  • componentWillMount在新版本已经废除
  • 在做SSR时,componentWillMount要做服务端数据的获取,不能被占用
  1. 如何避免ajax数据重复获取

使用react-redux进行统一管理,当跳转页面重新回来的时候判断store中是否有数据,有则不更新(再一次体现了redux的重要性)

  1. hashHistory和browserHistory

browserHistory需要服务器的配置支持,例如重定向的,告诉客户端使用哪个路由,而hashHistory不需要.browserHistory为真实的路由,访问URL时是访问服务器,服务器返回内容,正常页面请求时进入主页,加载JS文件,获得路由信息,这是路由是没有问题,但是如果刷新页面,就会存在404的情况.而hashHistory不是真正的路由,所以不会存在这个问题

  1. 什么时候使用异步组件

Reloadable 库
按需加载,不同的路由使用不同包,而不是在首页一起引入

  1. XSS攻击在React中如何防范

React其实已经做了,但是慎用 dangerouslySetInnerHTML ={{_html:html}}

  1. immutable.js和redux的最佳实践

ES6

  1. let、var、const的区别

let const 有块级作用域,var只有函数作用域
let const 没有变量提升
let const 不能重复定义
const 只读,不能修改
const定义与赋值必须一起

  1. 暂时性死区

在代码块内,使用let命令声明变量之前,该变量都是不可用的,。这在语法上,称为“暂时性死区"(TDZ),进入作用域变量就已经存在了,但是不可获取

  1. Symbol、Set、Map

Symbol:定义唯一的标识符,在用于对象属性时可以避免重名的情况,有Symbol.forSymbol.keyFor两个方法

var a=Symbol('a')
var b=Symbol('a')
var obj={}
obj[a]=1;
obj[b]=2
console.log(obj)  

Set:定义集合,允许存储数据的唯一值

NaN和undefined都可以被存储在Set 中, NaN之间被视为相同的值(尽管 NaN !== NaN)

add、has、delete方法

Map:保存键值对,任何值都可以作为一个键或者一个值

可以set、get、has、delete的方法获取,也可以通过对象设置、获取的方式进行(使用对象键值对获取的严格意义上不算Map,不能使用get方法获取)

Map与object的区别:

  • 一个Object的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。
  • Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。
  • 你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。
    Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。
  • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。虽然 ES5 开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。
  • Map 在涉及频繁增删键值对的场景下会有些性能优势。
  1. Proxy

改变操作对象的方式

//改变操作对象的方式
var handler={
    //接收两个参数,对象本身和对象的属性
    get:function(obj,prop){
        console.log('这是自定义的操作!');
        return obj[prop];
    },
    set: function(obj, prop, value) {
    	obj[prop]=value;
        console.log(`${prop} is being set to ${value}`);
    }
}
var obj={a:1,b:2}
var pro=new Proxy(obj,handler)
pro.c=3
console.log(pro.a)
/*
c is being set to 3
这是自定义的操作!
1

*/

可以用来将对象属性设置为私有属性,当访问该属性时抛出错误,也可以进行属性添加时的验证等…
4. Reflect
一个原生对象,不是构造函数,里边包含了对对象操作的方法,大多数方法已经被原生Object实现了
使用Reflect的原因:

  • 更加有用的返回值 比如Reflect.defineProperty(obj,name,desc) 返回是否成功设置的布尔值
  • 函数操作 Reflect.deleteProperty(obj,name) Reflect.has(obj,name)
  • 更加可靠的执行方式 ,调用函数并指定this fn.apply(obj,args) 但是apply方法可能被重写,可以使用
    Function.prototype.apply.call(obj,args) 但是太长且不易理解,更加安全的调用是Reflect.apply(fn,obj,args)
  • 避免直接访问 __proto__ (已经从web 标准中废除,尽量不要使用)
    可以通过Object.getPrototypeOf(obj) 也可以使用新方法 Reflect.getPrototypeOf()
  1. Promise

是一种异步操作解决方案,可以对异步操作进行封装,结果使用then的方式进行链式调用,在一定程度上避免了回调地狱的问题,但是如果then内嵌套也会存在回调地狱的问题,当前最好的异步解决方案是async/await

Promise.all(iteratror) :多个promise同时操作的方法,传入可迭代对象

var promise1=Promise.all([1,Promise.resolve(2),3]).then(e=>console.log(e))
//Array(3) [1, 2, 3]
  1. async、await

async、await类似与generator,async函数就是generator函数的语法糖,返回一个Promise对象
async相当于generator+自动执行器
相对于generator有一些优点:
1.内置自执行函数
不需要像generator那样调用,而只需要像普通函数一样,一行代码即可
2.更好的语义表达,async、await比起星号和yield在理解上更加容易,async异步操作,而await则等待await后面的表达式结果

  1. CommonJS和module
  • ComonJS输出的是值的拷贝(也就是说,一旦输出,内部变化就不能影响该值),ES6模块输出的是值的引用.ES6模块输出内容只读,不能改变其值,即不能改变其指向,ES6模块的运行机制与CommonJS不一样(支持异步加载),它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值

  • 当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。

//demo.js
var a=1;
module.exports.a=a;

//index.js
var ts=require('./demo.js')
ts.a=3;
var ts=require('./demo.js')
console.log(ts.a)//3
  • 可以随意在何处require,但是除了第一次,之后都是在模块缓存中取得,而ES6只能在顶层引入
  • commonJS加载时执行,即碰见require就会全部执行

CommonJS原理

var module={
	id:'module1',
    exports:{}
}

function load(module){
    function greeting(){};
    module.exports=greeting;
    return module.exports;
}
//将module和exports保存下来,放进node执行环境,node将其放在某个地方,当require时找到相应module和exports返回
save(module,load(module))

commonJS循环加载返回已经被执行的部分

  1. thunk函数ES6实现Generator自执行(thunk函数)

  2. 原生JS实现Promise

function PromiseM(callback) {
    this.status = "pendding";
    this.msg = "";
    callback && callback(data => {
        this.status = "resolve";
        this.msg = data;
    }, data => {
        this.status = "reject";
        this.msg = data
    });
    // 用于链式调用
    return this;
}
PromiseM.prototype.then = function () {
    if (typeof arguments[0]==="function"&&arguments[0]() instanceof PromiseM) {
        return arguments[0]()
    }
    if (typeof arguments[1]==="function"&&arguments[1]() instanceof PromiseM) {
        return arguments[1]()
    }
    // 添加setTimeout 的原因在于 then 调用必须后于resolve,reject执行,利用事件队列机制,实现
    setTimeout(() => {
        if (this.status === "resolve") {
            arguments[0](this.msg);
        }
        if (this.status === "reject") {
            arguments[1](this.msg);
        }
    }, 0);

}
PromiseM.prototype.resolve = function (data) {
    this.status = "resolve";
    this.msg = data;
    return this;
};
PromiseM.prototype.reject = function (data) {
    this.status = "reject";
    this.msg = data;
    return this;
};

HTTP与计算机网络

1.TCP/IP协议分层管理

在这里插入图片描述
应用层决定了向用户提供应用服务时通信的活动。
TCP/IP协议族内预存了各类通用的应用服务。比如,FTP ( FileTransfer Protocol,文件传输协议)和DNS ( Domain Name System,域名系统)服务就是其中两类。
HTTP协议也处于该层。

传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传输。
在传输层有两个性质不同的协议: TCP ( Transmission ControlProtocol,传输控制协议)和UDP ( User Data Protocol,用户数据报协议)。

网络层用来处理在网络上流动的数据包。

用来处理连接网络的硬件部分。包括控制操作系统、硬件的设备驱动、NIC ( Network Interface Card,网络适配器,即网卡),及光纤
等物理可见部分(还包括连接器等一切传输媒介 )。硬件上的范畴均在链路层的作用范围之内。

2.TCP和UDP区别

都是传输层协议

  • TCP是面向连接的,UDP是无连接的,发送数据前不需要确认连接
  • TCP是可靠的,UDP不可靠,不能保证数据的不丢失
  • TCP连接只能是点到点的操作,UDP支持点到点、一到多、多到一、多到多连接
  • TCP是面向字节流的,UDP是面向报文的

3.TCP连接的三次握手和四次挥手

  • TCP三次握手
    客户端向服务端发起连接请求
    服务端向客户端确认连接
    客户端再向服务端发起确认连接请求,建立连接发送报文

  • 为什么还要发一次确认报文

防止第一次发送的报文又重新发回到服务端
客户端发起请求,假设请求在中间停留迟迟未到达,服务端就不会发起确认连接,客户端认为服务端没有收到请求,然后又会重新发起连接请求,这时上一次的链接请求又到达服务端

  • 四次挥手

客户端向服务端发送释放报文,服务端接收会向客户端发送收到,但是socket并不会立即关闭,等到socket关闭,服务端会向客户端发送可以关闭报文,客户端发送确认报文,然后释放连接

4.GET和POST的区别

get发送一个TCP数据包,post发送两个TCP数据包
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

5.HTTP报文结构

报文结构分为 报文首部、空行、报文体

GET请求:
在这里插入图片描述

GET / HTTP/1.1
Host: www.enjoytoday.cn
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://www.enjoytoday.cn/posts/326
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1466032270994; UM_distinctid=15c4ef2ac4e2e4-0d13269271b947-1b2a120b-1fa400-15c4ef2ac4f7b5; un=aGZjYWk=; comment_author=aGZjYWk=; comment_author_email=1710600212@qq.com; comment_author_url=http://www.enjoytoday.cn; c_id=dUhIaTlndmc4MVVYbjRQTGxMRTotMTpFODg3QjgzQjg1NjgxQjQxRUYxNjg2QzJFRkMyQjI2QQ==; JSESSIONID=ADBC8C3DADF6C815D778450C193C6637.ajp13_worker; Hm_lvt_ce55bfda158556585a8b7b246346c8ba=1498560244,1498739070,1498833193,1498917432; Hm_lpvt_ce55bfda158556585a8b7b246346c8ba=1498917597; CNZZDATA1262047894=1598545996-1495973145-%7C1498917578
 
username=hfcai&sex=man

POST请求:
在这里插入图片描述

  HTTP/1.1 200 OK
  Date: Sat, 01 Jul 2017 14:51:26 GMT
  Server: Apache/2.4.7 (Ubuntu)
  Set-Cookie: JSESSIONID=84C993F5E433C4DE9BFBA57150FFC065.ajp13_worker;path=/;HttpOnly
  Content-Language: zh-CN
  Vary: Accept-Encoding
  Content-Encoding: gzip
  Content-Length: 7333
  Keep-Alive: timeout=5, max=100
  Connection: Keep-Alive
  Content-Type: text/html;charset=UTF-8

HTTP一共有四种类型的首部字段通用首部字段,请求首部字段,响应首部字段,实体首部字段。

通用首部字段:请求报文和响应报文两方都会使用的首部。Cache-ControlConnection(标记不在转发字段或者保持连接)、DateVia(中转经过的代理)、Upgrade(是否支持其他协议)
请求首部字段:从客户端向服务器发送请求报文时使用的首部。if-none-matchLast-Modified-Sinceuser-agent
响应首部字段:从服务器向客户端返回响应报文时使用的首部。EtagServer
实体首部字段:针对请求报文和响应报文的实体部分使用的首部。expiresLast-Modified

6.cookie相关字段

Set-Cookie:name=forcehack;expires=Sat, 26 Oct 2019 03:12:11 GMT;path=/;domain=www.forcehack.fun;secure;HttpOnly

expires:设置失效期,为GMT格式
path:设置cookie适用的文件目录
domain:设置cookie对象适用域名
secure:仅在HTTPS安全通信时才发送cookie
HttpOnly:禁止JS操作cookie

6.HTTP与HTTPS区别以及实现

HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
  HTTPS和HTTP的区别主要如下:
  1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
  2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
实现:
更改路由协议为https即可,为了考虑兼容性可以不携带协议,比如//www.forcehack.fun

7.session和cookie(区别于cookie和sessionStorage)

  • session运行在服务端,cookie运行在客户端
  • cookie不安全,session保存在内存中
  • session存储大小为5MB(localStorage也是),cookie存储大小为4KB,且个数也有限制
  • session个数多会造成服务器压力

8.web攻击分类

  • DOS攻击
    发送大量的合法请求到服务器,服务器无法识别是攻击请求还是正常请求,全部都响应,大量的请求造成服务器停止工作或者拒绝服务
  • CSRF(跨站请求伪造)
    伪造客户端身份重新发起请求,例子:用户在已经打开携带cookie的网站基础上,打开恶意网站,则网站会携带身份信息向之前网站发起请求盗取信息

防御:

  1. 通过 referer、token 或者 验证码 来检测用户提交。
  2. 尽量不要在页面的链接中暴露用户隐私信息。
  3. 对于用户修改删除等操作最好都使用post 操作 。
  4. 避免全站通用的cookie,严格设置cookie的域。
  • SQL注入
    对web连接的数据库发送恶意的SQL语句进行攻击,SQL注入的原理是通过在对SQL语句调用方式上的疏漏,恶意注入SQL语句。
  • XSS (跨站脚本攻击)
    将代码植入页面,达到攻击的目的
  • 反射型
    构造包含XSS代码的URL,诱导用户点击
  • 持久型
    通过提交数据的形式提交到数据库,然后渲染到页面中,所有访问过该页面的用户都会收到攻击
  • DOM型
    将XSS代码嵌入到DOM结点中,可能是反射型也可能是持久型

防御:对输入进行检查、过滤,对输出进行替换等,设置HttpOnly避免cookie劫持
9.内容安全策略
CSP:内容安全策略,设置客户端白名单,指定客户端访问内容,可以通过设置meta,也可以设置HTTP请求头设置

 	<!-- 同源可访问 -->
  <meta http-equiv="Content-Secure-Policy" content="default-src: 'self'">

算法

在这里插入图片描述

/**
 * @name: 插入排序 时间复杂度:O(n) 最坏:O(n²) 稳定
 * @test: test font
 * @msg: 
 * @param [Array] 需要排序的数组
 * @return: 排序后的数组
 */
function insert(arr){
    let handle=[arr[0]];
    for(let i=1;i<arr.length;i++){
        let fake=arr[i];
        for(let j=handle.length-1;j>=0;j--){
            if(fake>handle[j]){
                handle.splice(j+1,0,fake);
                break;
            }
            if(j===0){
                handle.unshift(fake);
            }
        }
    }
    return handle;
}
/**
 * @name: 冒泡排序 时间复杂度:O(n) 最坏:O(n²) 稳定
 * @param {Array} 需要排序的数组
 * @return: undefined
 */


 function bubble(arr){
     for(let i=0;i<arr.length-1;i++){
         for(let j=0;j<arr.length-1-i;j++){
             if(arr[j]>arr[j+1]){
                 [arr[j],arr[j+1]]=[arr[j+1],arr[j]];
             }
         }
     }
 }

/**
 * @name: 快速排序 时间复杂度:O(nlogn) 最坏:O(n²) 不稳定
 * @param {Array} 需要排序的数组
 * @return: 排序好的数组
 */


 function quick(arr){
    // 4.如果只剩一个则不需要做下边的排序
    if(arr.length<=1){
        return arr;
    }
    // 1.先取出中间值,并将其从其中排除
    let middleIndex=Math.floor(arr.length/2);
    let middleValue=arr.splice(middleIndex,1)[0];
    let left=[],right=[];
    // 2.遍历剩下数组,大于中间值的放右边,小于中间值的放左边
    for(let i=0;i<arr.length;i++){
            arr[i]>middleValue?right.push(arr[i]):left.push(arr[i]);
    }
    // 3.左边和右边继续排序
    return quick(left).concat(middleValue,quick(right));
    
 }

/**
 * @name:选择排序 时间复杂度:O(n²) 不稳定
 * @params: 需要排序的数组
 * @return: undefined
 */
function select(arr){
    // 1.遍历数组
    for(let i=0;i<arr.length-1;i++){
        // 2.初始值作为最小的 
        let    minIndex=i;
        // 3.遍历查找最小值,然后交换
           for(let j=i+1;j<arr.length;j++){
                if(arr[j]<arr[minIndex]){
                    minIndex=j;
                }
           } 
        //    交换
        [arr[minIndex],arr[i]]=[arr[i],arr[minIndex]];
    }
}
 /*
  * @name: 归并排序算法 时间复杂度:O(nlogn) 稳定排序
  * @params: 需要排序的数组 [Array]
  * @return: 排序后的数组  [Array]
  
 */
function mergeSort(arr){
    if(arr.length<2) return arr;
    let middleIndex=Math.floor(arr.length/2);
    let left=arr.slice(0,middleIndex);
    let right=arr.slice(middleIndex);
    
    return merge(mergeSort(left),mergeSort(right))
}

function merge(left,right){
    let res=[];
    while(left.length>0&&right.length>0){
        if(left[0]<right[0]){
            res.push(left.shift())
        }else {
            res.push(right.shift())
        }
    }
    if(left.length>0){
        res=res.concat(left)
    }
    if(right.length>0){
        res=res.concat(right)
    }
    return res;
}

/**
 * @name:希尔排序 :对插入排序的改进,使用增量的方式将大的尽量放后边,小的尽量放前边,减少插入的开销 时间复杂度 最好:O(n) 最差:O(n²) 平均:O(n^1.3)
 * @params: 需要排序的数组 [Array]
 * @return: 排序好的数组
 */
function shellSort(arr) {
    let len = arr.length;
    let gap = 1;
    while (gap < len / 3) {
        gap = gap * 3 + 1;
    }
    // 动态生成增量
    for (gap; gap > 0; gap = Math.floor(gap / 3)) {
        for (var i = gap; i < len; i++) {
            let temp = arr[i];
            // 向后查找交换,小的在前,大的在后
            for (var j = i - gap; j >= 0 && arr[j] > temp; j -= gap) {
                arr[j + gap] = arr[j];
            }
            arr[j + gap] = temp;
        }
        console.log(arr);
    }

    return arr;
}
/**
 * @name:堆排序
 * @description:利用完全二叉树的性质进行排序,分为小根堆  和大根堆 根节点小于叶子的为小根堆  大于的为大根堆 ,时间复杂度O(nlogn)  不稳定
 * @params: 需要排序的数组
 * @return: 排序后的数组
 */
function heapSort(arr){
    let res=[];
    while(arr.length>0){
        heapAdjust(arr);
        res.push(arr.shift());
    }
   
     return res;
}
// 建立小根堆
function heapAdjust(arr){
    let len=arr.length;
   //  从完全二叉树的最大不是叶子结点的堆开始调整
    for(let i=Math.floor(len/2);i>=0;i--){
       let min=i; 
       let left=2*i+1;
       let right=2*i+2;

       if(arr[left]<arr[min]) min=left;
       if(arr[right]<min) min=right;
    
    // 交换
    if(min!==i){
        [arr[min],arr[i]]=[arr[i],arr[min]];
        heapAdjust(arr);
    }
    
       
    }
}

let arr=[8,1,3,4,7,45,31,9];

console.log(heapSort(arr))

其他

  1. 网页所用图片格式区别以及应用场景
    jpg:有损压缩,占用内存小,加载快,色彩表现好,不支持透明效果,缺点是有损压缩会造成图片品质下降,不适用于文字较多的图片,颜色层次丰富的可以使用,不建议在颜色单调的图片使用
    png:无损压缩,占用内存大,加载慢,支持透明效果,在尽可能不失真的情况下不压缩图片,对于需要高保真图片时,因为png占用内存大,所以不适合用在web页面上
    gif:与png类似,主要用于动图,(其实png也支持动图,但是浏览器支持不够)
    svg:矢量位图,同时也是一种标记语言。可以直接嵌入网页中,也可以作为资源引用

  2. HTTP协议是无状态的
    HTTP 协议是无状态的,每次的请求都是独立的内容(相互没有关联),cookie在其中起到连接的目的,使得HTTP协议看起来是有状态的
    看的一个很好地比喻:

有状态
A:你今天中午吃的啥?
B:吃的大盘鸡。
A:味道怎么样呀?
B:还不错,挺好吃的。
无状态
A:你今天中午吃的啥?
B:吃的大盘鸡。
A:味道怎么样呀?
B:???啊?啥?啥味道怎么样?
所以需要cookie这种东西
A:你今天中午吃的啥?
B:吃的大盘鸡。
A:你今天中午吃的大盘鸡味道怎么样呀?
B:还不错,挺好吃的

  1. 浏览器内核
    Chrome 、Opera:Blink(基于webkit开发)
    Safari:webkit
    Firefox:Gecko
    IE:trident
  2. 浏览器渲染详细过程
    1.DNS解析
    2.TCP链接
    3.HTTP请求
    4.响应请求
    5.渲染页面

首先会发送请求给服务器,得到响应后,加载完毕html,之后开始解析html,生成dom tree解析过程中遇到了 css文件或者js文件,接着发请求,得到响应后,加载完毕css文件,然后解析css文件,继续解析html,然后计算样式,形成render tree ,然后layout,然后绘制
5. IE浏览器和其他浏览器的区别(兼容性问题)

  1. 获取属性
    IE:IE11以下没有dataset自定义data属性集
    chrome、FF:有

常规API:

1.alpha透明的设置
IE9以下设置透明

#myelement{filter:alpha(opacity=50);}
document.getElementById("myelement").style.filter="alpha(opacity=50)"

在chrome、FF中

#myelement{opacity:0.5}
document.getElmentById("myelement").style.opacity="0.5"

2.不同浏览器的默认内边距(padding)、外边距(margin)大小不同
解决方案:*{margin:0;padding:0}
事件API:
1、表示发生事件:
非IE:变量e接收
IE:window.event表示发生事件
解决方案:e||window.event
2、触发事件对象
(1)IE下,window.event对象有srcElement属性,但没有target属性。

(2)Firefox下,e对象有target属性,但没有srcElement属性。

(3)Chrome下,e对象同时具有target和srcElement属性。

解决方案:event.srcElement ? event.srcElement : event.target来兼容。
3、按键码
(1)IE下,window.event对象只有keyCode属性。

(2)FireFox下,e对象有which和charCode属性。

(3)Opera下,e对象有keyCode和which属性。

(4)Chrome下,e对象有keyCode、which和charCode属性。

解决方案:用e.keyCode || e.which || e.charCode来兼容。

4、阻止事件的默认行为:
(1)IE 中阻止事件的默认行为需要将window.event.returnValue属性设置为false。

(2)非IE阻止事件的默认行为需要调用 e.preventDefault() 方法。

解决方案:条件判断浏览器是否具有event.preventDefault再做相应处理。

5、阻止事件冒泡:

(1)IE阻止事件冒泡需要设置window.event.cancelBubble = true。

(2)非IE阻止事件冒泡需要调用e.stopPropagation()。

解决方案:条件判断浏览器是否具有event.stopPropagation再做相应处理。

6、触发事件:

IE9以下不能通过addEventListener注册事件
解决方案:

function bindEvent(element,type,handle){
    if(element.addEventListener){
        element.addEventListener(type,handle);
    }
    if(element.attachEvent){
        element.attachEvent("on"+type,handle);
    }
}

7.页面加载慢的解决方案

  1. 减少HTTP请求
  2. 使用CDN(内容发布网络),分布在不同地理位置的web服务器
  3. 使用缓存
  4. 对HTTP传输进行压缩
  5. 样式表放头部,脚本放底部
  6. 懒加载
  7. 按需加载
  8. 预加载

7.图片比较多

  1. 懒加载
    通过判断scroll 然后将自定义属性data-src赋值给src
  2. 将图片放到图片服务器,例如CDN
  3. 压缩图片
  4. 将图片压缩成base64格式来节约请求
    将图片压缩成base64,随html或者css一起下载到浏览器,不需要额外的请求,这样就节约了请求.
    我们知道图片在传输过程中是流传输,如果将图片转换成base64,实际上是变大了,并且浏览器在decode base64编码的图片时需要耗费很多时间的,所以如果我们选择此种方案的话,最好选择一些小图片,不然得不偿失,在webpack中可以设置最大多少byte的图片压缩成base64

8.前端跨域
跨域:跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。
同源策略:相同协议、相同域名、相同端口
跨域解决方案:
1.jsonp(只能实现get请求)

利用script、img、ifame等标签不受同源策略的影响,(为了减轻web服务器的压力,将图片、文件等放在另一域名服务器下),这是被浏览器允许的,所以可以动态的生成script标签

 	var script = document.createElement('script');
    script.type = 'text/javascript';

    // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
    script.src = 'http://localhost:3001/find?callback=handleCallback';
    document.head.appendChild(script);

    // 回调执行函数
    function handleCallback(res) {
        console.log(res)
    }
const express=require('express')
const app=express()
const port=process.env.port||3001

app.get('/find',function(req,res){
    let {callback}=req.query
    let data={state:1};
    data = JSON.stringify(data);
 
   res.send(callback+'('+data+')')
})

app.listen(port)
console.log(`start listen port ${port}`)

2.document.domain+iframe 跨域

只适合于主域相同、子域不同的跨域场景
解决方案:设置document.domain='demo.com' 为相同的主域解决
父窗口:http://www.demo.com/a.html 内部使用iframe引入子窗口
子窗口:http://child.demo.com/b.html

3.postMessage(XDM,跨文档消息传送

  //目标域
    window.addEventListener('message',function(event){
      if(event.origin === '目标域'){
        console.log(event.data);
        //event.source只是window的一个代理对象,没有实际作用,调用postMessage就好
        event.source.postMessage('回执消息','来源域')
      }
    })
    //来源域
    document.iframes[0].postMessage('这是发送的消息','目标域');

4.CROS 跨域资源共享

普通跨域请求:浏览器已经原生支持了CROS,在请求时使用绝对URL即可,在第一次请求时,浏览器会多发送一次HTTP请求,请求方法为OPTIONS,原理在于浏览器会自动发送preflighted Request,发送CROS所需的Access-Control-Request-HeadersAccess-Control-Request-MethodOrigin字段。只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置

前端设置:// 前端设置是否带cookie
xhr.withCredentials = true;
后端Node

app.all("*", function (req, res, next) {
  //设置允许跨域的域名,*代表允许任意域名跨域
  res.header("Access-Control-Allow-Origin", "*");
  //允许访问cookie
  res.header("Access-Control-Allow-Credentials",'true');
  //允许的header类型
  res.header("Access-Control-Allow-Headers", "content-type");
  //设置cookie
  res.header("Set-Cookie",'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  )// HttpOnly的作用是让js无法读取cookie
  //跨域允许的请求方式 
  res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
  if (req.method.toLowerCase() == 'options')
    res.sendStatus(200);  //让options尝试请求快速结束
  else
    next();
}
)

5.nginx代理跨域
6.websocket协议跨域websoket
7.图像ping
9.href、src、url的区别
src: 指向外部资源所在位置,指向的内容会嵌入文档中该标签位置,会将资源下载并应用到该文档中。
href:指向网络资源位置,建立和当前元素或当前文档之间的链接
url(统一资源定位符):代表唯一的网上资源链接或者是服务器资源链接的地址。它的作用就是提供唯一性的资源地址,当你进行资源利用的时候访问该资源所存的地址
10.浏览器的回流和重绘
回流:页面进行位置计算、布局等操作时会进行重流
重绘:当发生color、background-color 等样式变化时会进行重绘

回流一定会触发重绘,重绘不一定回流
回流非常浪费浏览器性能,需要尽量避免回流操作
11.浏览器的缓存机制
缓存:存在于浏览器和服务端的一个副本,保存在本地磁盘中,会根据请求内容进行保存页面信息,当相同的URL访问时,浏览器会根据缓存机制确定是否使用副本.

第一次请求服务器会将页面最后修改时间通过Last-Modified标识符由服务端发送到客户端,客户端记录修改时间,同时服务端还会生成Etag值发送到客户端
在这里插入图片描述
缓存分为强缓存(本地缓存)和协商缓存

强缓存:浏览器输入URL,浏览器会查找缓存信息,找到相应缓存内容的header,通过cache-controlexpires字段判断缓存是否过期,如果没有过期则直接返回200(from cache)从本地缓存读取内容,如果已经过期,则进入协商缓存

协商缓存:浏览器通过If-None-Match请求头携带Etag值到服务端,与服务端的Etag值进行对比,如果没有变化则直接返回304,浏览器读取缓存.如果已经更改,服务端通过判断浏览器传送的If-Modefied-Since请求头携带的Last-Modified与服务端的Last-Modefied对比,如果没有修改则返回304,浏览器兑取缓存.如果已经更改,则响应请求返回200,返回新内容.

普通刷新只会启用协商缓存,忽略强缓存

cache-control常用字段

max-age:缓存有效时间,为相对值,如`max-age:3600`则3600S后失效
public:允许所有用户对其进行缓存
private:只允许终端用户对其进行缓存(代理等不能对其进行缓存)
no-cache:不使用强缓存,直接进行协商缓存
no-store:不进行缓存,每次请求都向服务器直接请求内容

expires字段:设置失效时间,为绝对值,GMT格式的时间戳,超出该时间则失效,当cacha-controlexpires同时存在时,cache-controlexpires优先级更高,另外expires为HTTP1.0的内容,cache-control为HTTP1.1的内容

为什么有了Last-Modified还要有Etag值

Etag:文件修改标识符,标识唯一的文件修改信息
Last-Modified:汶上上一次被修改的时间

1.一些文件周期性的被修改,但内容并未修改,这个时候希望浏览器认为其没有被修改
2.一些文件被频繁的修改,比如1s内修改了很多次,但是If-Modified-Since能识别的最小颗粒单位为S,该修改就会被忽略
3.某些服务器不能精确到准确的修改时间

11.前端渲染与后端渲染

前端渲染与后端渲染

  • 6
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
引用提供了一个关于2023年前端HTML CSS最全面试题汇总的资源。这个资源涵盖了前端知识点的全部内容,可以帮助你做好面试准备。除此之外,根据引用的描述,不同公司对前端面试的要求可能会有所不同。在某些公司中,前端面试可能会包括对接口文档的测试,验证接口的正常调用以及返回值是否符合要求,同时还需要了解前端所需的参数等等。因此,除了参考提供的面试题,你还应该了解所申请公司的具体要求,以便更好地准备面试。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [2023高频前端面试总结(附答案)](https://blog.csdn.net/weixin_45102366/article/details/125525247)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [2023前端HTML+CSS最全面试题和答案汇总](https://download.csdn.net/download/m0_61243965/87431738)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值