面试题

1.jsonp跨域及其实现方式

var url="http://www.xxx.com?callback=handle"
var script=document.createElement("script")
script.setAttribute("src",url)
// 允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

2.cors实现跨域

//服务端
Access-Control-Allow-Origin=*
//服务端
headers:{
     
    origin:localhost
}

3.手写节流函数trottle

function trottle(fn,delay)
{
     
    
// 函数节流: 规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
    let timer=null;
    return function(){
     
        if(!timer)
        {
     
            timer=setTimeOut(()=>{
     
                //apply接收数组
                fn.apply(this,arguments);
                timer=null;
            },delay)
        }
    }
}
function debounce(fn,delay)
{
     
    // 函数防抖: 在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。
    let timer=null;
    return function()
    {
     
        if(timer)
        {
     
           timer=null;
        }
        timer=setTimeOut(()=>{
     
            fn.apply(this.arguments)
        },delay)
    }
}

4.XSS与CSRF

XSS 攻击指的是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。
XSS 的本质是因为网站没有对恶意代码进行过滤,与正常的代码混合在一起了,浏览器没有办法分辨哪些脚本是可信的,从而导致了恶意代码的执行。
设置httponly禁止js读cookie

CSRF 攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被
攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。
CSRF 攻击的本质是利用了 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。
解决:指定请求头中的HTTP refer来自同一个域名,发送token

5.Cookie有哪些字段,如何禁止JS访问Cookie

domin,path,expires,key,value,httpOnly(禁止js访问cookie)

6.进程之间通信机制

管道、消息队列、信号、共享内存

7.HTTPS

HTTP 信息明文传输 80端口
https需要申请证书
HTTPS=HTTP+SSL(加密协议传输) 443端口
客户端向服务器发起HTTP请求,服务器返回SSL证书给客户端,然后客户端使用公钥进行加密
确保数据在传输的过程中不被窃取、改变、保证数据的完整性。

8.数据库

增:insert
删:delete
改:update
查:select

9.CSS选择器优先级

行内>ID>CLASS>Tag
多个选择器并列使用权重相加

10.CSS盒子模型

margin->border->padding->content
标准盒模型: width,height=content-box
IE盒模型:width,height=content+padding+border

11.两栏布局

.left
{
     
    float:left;
    width:200px;
}
.right{
     
    margin-left:200px;
}
.left
{
     
    float:left;
    width:200px;
}
.right{
     
    //触发BFC,内部元素对外部元素布局没有影响
     over-flow:hidden;
}
.wrapper{
     
    display:flex;
}
.left{
     
    width:200px;
}
.right{
     
    flex:1;
}

12.三栏布局

.wrapper{
     
    display:flex;
}
.left{
     
    width:200px;
    position:absolute;
    left:0;
}
.right{
     
    width:200px;
    position:absolute;
    right:0;
}
.center{
     
    margin-left:200px;
    margin-right:200px;
}

position的值介绍

1.static

static是默认值表示没有定位,该元素出现在正常的文档流当中

2.relative

相对定位,相对于元素正常出现在文档流中的位置进行定位

3.absolute

绝对定位,相对于最近的具有定位属性的父元素进行绝对定位
若不存在这样的父元素,就相对于body进行定位。

4.fixed

相对于浏览器窗口进行定位。

margin重叠

上下边距都有,取较大的那个
水平左右的边距永远不会重合
解决边距重叠
1.外层元素padding代替
2.透明边框border

圣杯布局(两侧宽度固定,中间宽度自适应的三栏布局)

在CSS中,圣杯布局是指两边盒子宽度固定,中间盒子自适应的三栏布局,其中,中间栏放到文档流前面,保证先行渲染;三栏全部使用“float:left”浮动,并配合left和right属性。

<html>
<body>
<div class="container">
    <div class="middle">middlediv>
    <div class="left">leftdiv>
    <div class="right">rightdiv>
div>
body>
<style>
.container{
      
    overflow:hidden;
    padding:0 200px;
}
.middle{
      
    float:left;
    width:100%;
	background:red;
	height:200px;
}
.left{
      
    float:left;
    position:relative;
    width:200px;
    left:-200px;
	background:purple;
	margin-left:-100%;//让左边部分上去
	height:200px;
}
.right{
      
    float:left;
    position:relative;
    width:200px;
    right:-200px;
	background:yellow;
	height:200px;
	margin-left:-200px;//让右边部分上去
}
style>
html>

双飞翼布局

跟圣杯布局差不多,只不过用margin代替padding,只有content在container里面




 
  
#center
#left
//左部分左浮动,右部分右浮动,中间设置margin
//flex布局,左右设置宽度,中间flex 1

11.for (var i = 0; i < 10; i++) { setTimeout(() => { console.log(i) }, 1000); }

//解决方案=>闭包让它立即执行
for(var i=0;i<10;i++)
{
     
    (function(i){
     
         setTimeout(()=>{
     
            console.log(i)
        },1000)
    })(i)
}
for (let i = 0; i < 10; i++) 
{
         //let是块级作用域,let定义的i会随着setTimeout进入队列
     setTimeout(() => {
              console.log(i)     }, 1000); }

13.对 == 的 === 的区别的了解

//==在判断不同的基本类型的时候会将其转为想同的基本类型再判断,而===严格判断

//对引用类型==和===无区别,都是进行引用地址的比较

14.实现一个深拷贝

//1.var newobj=JSON.parse(JSON.stringify(obj))
function deepCopy(obj)
{
     
    var res=Array.isArray(obj)?[]:{
     }
    for(let i in obj)
    {
     
        let value=obj[i]
        if(typeof value=="object")
        {
     
            res=deepCopy(obj);
        }
        res[i]=value
    }
    return res;
}

15.vue响应式原理

观察者模式
data是被观察的一方,发生改变的时候,通知所有观察者(watcher)做出响应比如更新视图
数据可以有多个观察者,要记录多个观察者,就需要一个数据结构dep来记录他们
struct dep
{
    int id;
    string subs;
}
watcher准确的来说是一个求值表达式或者函数

通过Object.defineProperty()替换data对象的getter,setter属性get,set
拿数据的时候,触发get方法,建立依赖关系
写入数据触发set方法,借助dep通知观察者

Object.defineProperty()缺点
    有一些对属性的操作,使用这种方法无法拦截,比如说通过下标方式修改数组数据或者给对象新增属性

16.vue异步更新

v-if:false->true
vue并不是在数据发生变化置换立即更新DOM的,而是异步的
只要监听到数据变化,vue就会开启一个队列记录数据变更,同步执行完才回去队列取异步任务===
nextTick

16.promise是什么,实现一下promise.ALL

promise是解决回调地狱的一钟方案,解决异步的问题
const PENDING="pending";
const FULFILLED="fulfilled";
const REJECTED="rejected";
function myPromise(executor)
{
     
    let that=this;
    that.status=PENDING;
    that.value=undefined;
    that.reason=undefined;
    that.onFulfilledCallBack=[]
    that.onRejectedCallBack=[]
    function resolve(value)
    {
     
         // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
        if(value instanceof Promise)
        {
     
            return value.then(resolve,reject);
        }
        setTimeout(()=>{
     
            //加settimeout是为了确保onFulfilledCallBack和
            //onRejectedCallBack异步执行,而且在promise.then之后执行
            if(that.status==PENDING)
            {
     
                that.status=FULFILLED;
                that.value=value;
                that.onFulfilledCallBack.forEach(fn=>fn(that.value))
            }
        },0)
    }
    function reject(reason)
    {
     
        setTimeout(()=>{
     
            if(that.status==PENDING)
            {
     
                that.status=REJECTED;
                that.reason=reason;
                that.onRejectedCallBack.forEach(fn=>fn(reason))
            }
        },0)
    }
    try{
     
        executor(resolve,reject)
    }
    catch(e)
    {
     
        reject(e)
    }
    that.then(onFulfilledCallBack,onRejectedCallBack)
    {
     
        if(that.status==FULFILLED)
        {
     
            onFulfilledCallBack(that.value)
        }
        else if(that.status==REJECTED)
        {
     
            onRejectedCallBack(that.reason)
        }
    }
    that.all(promises)
    {
     
        let results=[]
        let promiseCount=0;
        let promiseLength=promises.length;
        return new Promise((resolve,reject)=>{
     
            for(let value of promises)
            {
     
                Promise.resolve(value)
                .then(res=>{
     
                    promiseCount++;
                    results.push(res)
                    if(promiseCount==promiseLength)
                    return resolve(results)
                })
                .catch(e)
                {
     
                    reject(e)
                }
                
            }
        })
    }
}

17.commonjs与es6 module区别

commonjs 
静态复制更改,可以修改引入变量的值,在服务端使用
module.export
var axois=require("axios")//必须加载完成才能使用,而且未使用也要加载

module 动态引用
import axios from 'axios'
不允许更改引入变量的值
且需要用到才加载

18.cache-control字段及其作用

private:仅客户端可缓存
public:客户服务都可缓存
no-cache 告诉浏览器不使用缓存,发新请求
no-store 所有的内容都不会被缓存
max-age:缓存过期时间

19.react父子组件通信

父=>子 props
子=>父 传递setState函数,子传给父亲这个值
兄弟=>provider consumer 或者给父亲再给兄弟 或者redux
  1. 哪些操作会导致回流
DOM尺寸变化

20.content-type

type/subtype
告诉浏览器如何解析响应的数据
text/html;
image/JPEG
application/json
application/xml;
application/javascript
application/x-www-form-urlencoded 表单提交的时候用于中文转码

21.DNS

域名解析

22.flex垂直水平居中

justfy-content:center;
align-items:center;

23.for循环let和var区别

var存在变量提升,提升到全局作用域,只执行一次
let块级作用域,不存在变量提升,每次for循环时不同的块级作用域。

24.数组扁平化

function flat(arr)
{
     
    var newarr=[]
    arr.forEach(item=>{
     
        if(Array.isArray(item))
        {
     
            newarr.push(...flat(item))
        }
        else
        {
     
            newarr.push(item)
        }
    })
    return newarr;
}
function flat(arr)
{
     
    return arr.reduce((pre,cur)=>{
     
        pre.concat(Array.isArray(cur)?flat(cur):cur)
    },[])
    //若不指定为空数组,初始的时候pre为第一个元素
}

25.cookie和session的区别

cookie由服务器通过响应头的set-cookie字段进行设置,发起HTTP请求会将cookie自动添加到请求头中。占用带宽。
cookie存在客户端,session存在服务端
cookie通过HTTP发送可以通过拦截的方式获取到,不安全,还可通过本地拿到
cookie最多保存4k,而session没有大小限制,跟服务器内存有关

当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid。如果有sessionid,服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid,服务器会创建新的session对象,并把sessionid在本次响应中返回给客户端。

26.浏览器缓存机制

浏览器的缓存机制指的是通过在一段时间内保留已接收到的 web 资源的一个副本,如果在资源的有效时间内,发起了对这个资源的再一次请求,那么浏览器会直接使用缓存的副本,而不是向服务器发起请求。使用 web 缓存可以有效地提高页面的打开速度,减少不必要的网络带宽的消耗。

web 资源的缓存策略一般由服务器来指定,可以分为两种,分别是强缓存策略和协商缓存策略。

使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。强缓存策略可以通过两种方式来设置,分别是 http 头信息中的 Expires 属性和 Cache-Control 属性。

服务器通过在响应头中添加 Expires 属性,来指定资源的过期时间。在过期时间以内,该资源可以被缓存使用,不必再向服务器发送请求。这个时间是一个绝对时间,它是服务器的时间,因此可能存在这样的问题,就是客户端的时间和服务器端的时间不一致,或者用户可以对客户端时间进行修改的情况,这样就可能会影响缓存命中的结果。

Expires 是 http1.0 中的方式,因为它的一些缺点,在 http 1.1 中提出了一个新的头部属性就是 Cache-Control 属性,它提供了对资源的缓存的更精确的控制。它有很多不同的值,常用的比如我们可以通过设max-age 来指定资源能够被缓存的时间的大小,这是一个相对的时间,它会根据这个时间的大小和资源第一次请求时的时间来计算出资源过期的时间,因此相对于 Expires来说,这种方式更加有效一些。常用的还有比如 private ,用来规定资源只能被客户端缓存,不能够代理服务器所缓存。还有no-store ,用来指定资源不能够被缓存,no-cache 代表该资源能够被缓存,但是立即失效,每次都需要向服务器发起请求。

一般来说只需要设置其中一种方式就可以实现强缓存策略,当两种方式一起使用时,Cache-Control 的优先级要高于 Expires 。

使用协商缓存策略时,会先向服务器发送一个请求,如果资源没有发生修改,则返回一个 304 状态,让浏览器使用本地的缓存副本。
如果资源发生了修改,则返回修改后的资源。协商缓存也可以通过两种方式来设置,分别是 http 头信息中的 Etag 和 Last-Modified 属性。

服务器通过在响应头中添加 Last-Modified 属性来指出资源最后一次修改的时间,当浏览器下一次发起请求时,会在请求头中添加一个 If-Modified-Since 的属性,属性值为上一次资源返回时的 Last-Modified 的值。当请求发送到服务器后服务器会通过这个属性来和资源的最后一次的修改时间来进行比较,以此来判断资源是否做了修改。如果资源没有修改,那么返回 304 状态,让客户端使用本地的缓存。如果资源已经被修改了,则返回修改后的资源。使用这种方法有一个缺点,就是 Last-Modified 标注的最后修改时间只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,那么文件已将改变了但是 Last-Modified 却没有改变,
这样会造成缓存命中的不准确。

因为 Last-Modified 的这种可能发生的不准确性,http 中提供了另外一种方式,那就是 Etag 属性。服务器在返回资源的时候,在头信息中添加了 Etag 属性,这个属性是资源生成的唯一标识符,当资源发生改变的时候,这个值也会发生改变。在下一次资源请求时,浏览器会在请求头中添加一个 If-None-Match 属性,这个属性的值就是上次返回的资源的 Etag 的值。服务接收到请求后会根据这个值来和资源当前的 Etag 的值来进行比较,以此来判断资源是否发生改变,是否需要返回资源。通过这种方式,比 Last-Modified 的方式更加精确。

当 Last-Modified 和 Etag 属性同时出现的时候,Etag 的优先级更高。使用协商缓存的时候,服务器需要考虑负载平衡的问题,因此多个服务器上资源的 Last-Modified 应该保持一致,因为每个服务器上 Etag 的值都不一样,因此在考虑负载平衡时,最好不要设置 Etag 属性。


27.进程/线程的区别

进程是资源分配的基本单位,切换开销大
线程是任务执行的基本单位,必须依赖于进程执行。线程切换开销小,
因为共享一个进程的资源

28.事件循环

主线程同步任务先执行,所有的同步任务形成一个任务执行栈。遇到异步任务将异步任务放入callbacks对列
等同步任务执行完成之后再从callbacks队列取出异步任务进行执行
异步任务又分为宏任务和微任务,先执行微任务,再执行宏任务
微任务:promise.then
宏任务:settimeout setinterval setimmediate

29.tcp udp区别

tcp面向连接,更耗时,可靠传输=>使用流量控制和拥塞控制,只能一对一通信,适用于要求可靠传输如文件传输,首部最小20字节,但因为耗时变得不安全。TCP套接字由四个元组标识:源IP地址、源端口号、目标地址、目标端口号,所以只能一对一。
TCP 提供了拥塞控制机制,在网络拥塞的时候会控制发送数据的速率,有助于减少数据包的丢失和减轻网络中的拥塞程度。
TCP 提供了流量控制机制,保证了通信双方的发送和接收速率相同。如果接收方可接收的缓存很小时,发送方会降低发送速率,避免因为缓存填满而造成的数据包的丢失


udp:面向无连接,没有握手过程,传输更快,但是不能保证数据准时有序到达,不仅可以一对一还可以一对多,多对一,多对多。首部开销小,8字节。没有拥塞控制和流量控制,所以使用UDP发送速率没有限制。
udp套接字由两个元组标识:目标IP地址、目标端口,所以可以多对多

29.两个栈模拟队列

//两个栈大小为m和n
//大的做存储,小的做缓冲
//入队存大,出队先进小 在出
function in(value)
{
     
    mainStack.push(value);
}
function out()
{
     
    for(let i=0;i<mainStack.length;i++)
        subStack.push(mainStack.pop())
    return subStack.pop()
}

30.reduce实现map

Array.prototype.map=function(fn)
{
     
    let res=[]
    this.reduce((pre,cur,index,arr)=>{
     
        res.push(fn.apply(this,[cur,index,arr]))
    },[])
    return res;
}

30.闭包

function s()
{
     
    var count=0;
    return function()
    {
     
        return count++;
    }
    //这个不会被回收所以导致内存泄露
}

31.输入url到页面呈现

1.输入地址发送到DNS服务器取得对应域名
2.建立TCP链接
3.发送HTTP请求
4.获取返回结果
5.解析渲染页面

32.HTTP状态码

101切换协议
200请求成功
301永久重定向
302临时重定向
304没有修改
401未授权
404
405方法禁用
500

33.flex布局

子元素的float、clear属性将失效。
flex-dirction:决定主轴排列方向
flex-wrap:是否换行
flex-flow=flex-dirction+flex-wrap
justify-content:主轴对齐方式
align-items:交叉轴对齐方式

34.数组去重

arr.filter((item,index)=>{
     
    return arr.indexOf(item)==index
})
function unique(arr)
{
     
    arr.reduce((pre,cur)=>{
     
        if(!pre.includes(cur))
        {
     
            pre.push(cur)
        }
        else
        {
     
            return pre
        }
    },[])
}

35.tcp三次握手

为了维持可靠连接tcp通信双方必须维护一个序列号
三次握手保证双方的序列号都能被对方知道
1.客户端向服务端发送一个SYN(同步序列号),询问是否建立连接
2.服务端返回给客户端一个ACK(确认应答)和SYN,告诉可以连接
3.用户接收到ack和syn就可以开始传输数据了

client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”。主要目的防止server端一直等待,浪费资源。

36.事件委托和冒泡

事件捕获,从document开始触发,依次触发,一级一级往下传递到target
事件冒泡,从target开始,依次触发,一级一级往上传递到body
addEventListener(event,callback,useCapture)
事件委托:监听父元素给子元素绑定事件,useCapture=false
 ol.addEventListener('click',function(e){
     e.target获取真实元素
     let ele = e.target
     if(ele.tagName==='li'){
     }
  })

事件委托本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到
目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理。

使用事件代理我们可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗。并且使用事件代理我们还可以实现事件的动态绑定,比如说新增了一个子节点,我们并不需要单独地为它添加一个监听事件,它所发生的事件会交给父元素中的监听函数来处理。

37.变量提升

//函数提升优于变量提升
var x=6//不会有变量提升,因为初始化的变量不存在变量提升
console.log(v1);
var v1 = 100;
function foo() {
     
    console.log(v1);
    var v1 = 200;
    console.log(v1);
}
foo();
console.log(v1);
//undefined
//undefined
//200
//100

38.箭头函数和普通函数

箭头函数没有this,会自动绑定上下文的this
箭头函数不能作为构造函数
箭头函数没有原型
箭头函数用剩余参数取代arguments
用var声明的箭头函数没有变量提升

39.em,rem,px

px计算机屏幕分辨率
em:相对长度单位,相对于对象内文本字体尺寸,若没有font-size则继承父亲的
rem:相对于html元素的fontsize

40.css画三角形

.triangle{
     
    width:0;
    height:0;
    border-bottom:10px solid red;
    border-right:10px solid transparent;
    border-top:10px solid transparent;
}

41.JSON中超出int范围的id怎么处理

使用json-bigint
import jsonBig from 'json-bigint'
axios({
    method:"get",
    try{
        return jsonBig.parse(data)
    }
    catch()
})

42.类的继承

//父类Animal
//1.原型链继承
function Cat()
{
     
    Cat.prototype=new Animal()
    Cat.prototype.name=""
}
//2.构造继承
function Cat()
{
     
    Animal.call(this)
    this.name=""
    //不能继承原型的属性和方法
}
//3.实例继承
function Cat()
{
     
    cat=new Animal();
    cat.name="";
    return cat;
}
//4.组合继承(内构造,外原型链)
function Cat()
{
     
    Animal.call(this)
    this.name=""
}
Cat.prototype=new Animal()

43.+操作符

如果+号两边某个操作数是字符串或者能够通过某个步骤转换成字符串得话,就进行字符串的拼接操作
var s="5"+2+3   s="523"

44.工厂模式创建对象

//创建对象的方式
//1.var obj={} obj.name=''
//2.var obj=new Object()
//3.var obj={name:""}
//以上三种对象钟的方法不可共享
//工厂模式
function CreateObject(name)
{
     
    var obj=new Object()
    obj.name=""
    return obj
}
var factoryObj=new CreateObject("simple")

45.手写new

function myNew()
{
     
    var obj={
     }
    obj.__proto__=Constructor.prototype;
    let res=Constructor.apply(obj,arguments)//得到构造函数返回对象
    return typeof res=="object"?res:obj
}

46.函数柯里化

//收集参数,收集完成再执行函数
function curry(fn)
{
     
    let args=[]
    return function()
    {
     
        //利用闭包收集参数
        args=args.concat([...arguments])
        //这里的arguments是每次调用fn函数得到的参数
        if(args.length>=fn.length)
        {
     
            //参数收集完成执行函数
            fn(...args)
        }
        //参数未收集完,继续执行
        return arguments.callee
        
    }
}

47.label for的用处

//提升用户体验
label for与input绑定,点击label文字,表单自动聚焦

48.img的data-src
当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次),当图片出现在浏览器的可视区域内时,才设置图片真正的路径,让图片显示出来。这就是图片懒加载。

//data-src是真正的地址
1、就是创建一个自定义属性data-src存放真正需要显示的图片路径,而img自带的src放一张大小为1 * 1px的图片路径。
<img src="" data-src="">

49.data-*

自定义属性

50.算法之排序

//冒泡
function bubbleSort(arr)
{
     
    var length=arr.length;
    var temp;
    for(var i=0;i<length-1;i++)
    {
     
        for(var j=0;j<length-i-1;j++)
        {
     
            if (arr[j] > arr[j+1]) 
            {
             // 相邻元素两两对比
                temp = arr[j+1];        // 元素交换
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
        return arr;
    }
}
//选择排序
function selectSort(arr)
{
     
    var length=arr.length;
    var temp;
    for(var i=0;i<length-1;i++)
    {
     
        var minIndex=i;
        for(j=i+1;j<length;j++)
        {
     
            if(arr[j]<arr[minIndex])
            {
     
                minIndex=j;
            }
        }
        temp=arr[i];
        arr[i]=arr[minIndex];
        arr[minIndex]=temp;
    }
    return arr;
}
function insertSort(arr)
{
     
    var length=arr.length;
    for(let i=0;i<length-1;i++)
    {
     
        var preIndex=i;
        var currentItem=arr[i+1]
        while(preIndex>-1&&arr[preIndex]>currentItem)
        {
     
            arr[preIndex+1]=arr[preIndex];
            //依次往前进行比较
            preIndex--;
        }
        //currentItem是当前得最小值,当前preindex元素后面去
        arr[preIndex+1]=currentItem;

    }
    return arr;
}
//快排(通过一个中间元素将左右分开排序)
function quickSort(arr)
{
     
    var length=arr.length;
    if(length<=1)
        return arr;
    var middle=Math.floor(length/2);
    var middleEle=arr.splice(middle,1)[0];
    var left=[];
    var right=[];
    for(let i in arr)
    {
     
        if(arr[i]<middleEle)
        {
     
            left.push(arr[i]);
        }
        else
        {
     
            right.push(arr[i]);
        }
    }
    return quickSort(left).concat([middleEle],quickSort(right));
}
//归并
function mergeSort(arr)
{
     
    var length=arr.length;
    var middle=Math.floor(length/2)
    var left=arr.slice(0,middle);
    var right=arr.slice(middle);
    while(length<2)
        return arr;
    return merge(mergeSort(left),mergeSort(right));
}
function merge(left,right)
{
     
    var res=[];
    while(left.length&&right.length)
    {
     
        if(left[0]<right[0])
        {
     
            res.push(left.shift());
        }
        else{
     
            res.push(right.shift());
        }
    }
    return [...res,...left,...right];
}

51.算法之二叉树

//反转二叉树
//若根节点不为空,将左右节点交换,直到根节点未空
function reverseBinaryTree(root)
{
     
    if(root!=null)
    {
     
        var temp=root.left;
        root.left=root.right;
        root.right=temp;
        reverseBinaryTree(root.left);
        reverseBinaryTree(root.right);
    }
    return root;
}
//创建二叉树
function BinaryTree()
{
     
    //一颗二叉树具有的性质=>节点Node,插入节点方法insertNode,取得根节点方法getRoot
    var root=null;//根节点为空
    this.getRoot=function()
    {
     
        return root;
    }
    function Node()
    {
     
        //二叉树的节点
        this.key=key;
        this.left=null;
        this.right=null;
    }
    function insertNode(parentNode,childNode)
    {
     
        if(childNode.key<parentNode.key)
        {
     
            if(parentNode.left==null)
            {
     
                parentNode.left=childNode;
            }
            else
            {
     
                //直到找到没有儿子的左子树为止
                insertNode(parentNode.left,childNode);
            }
        }
        else
        {
     
            if(parentNode.right==null)
            {
     
                parentNode.right=childNode;
            }
            else
            {
     
                //直到找到没有儿子的右子树为止
                insertNode(parentNode.right,childNode);
            }
        }
    }
    //插入方法
    this.insert=function(key)
    {
     
        var node=new Node(key);
        if(root==null)
        {
     
            root=node;
        }
        else
        {
     
            insertNode(root,node);
        }
    }
}
var node=[1,2,5,6,7,9,5,2,5,6,3];
var binaryTree=new BinaryTree();
node.forEach(item=>{
     
    binaryTree.isert(item);
})
console.log(binaryTree.getRoot());
//前序遍历二叉树(写在BinaryTree里面)
this.preOrder=function(node)
{
     
    if(node!=null)
    {
     
        console.log(node.key);
        this.preOrder(node.left);
        this.preOrder(node.right);
    }
}
//中序遍历二叉树
this.inOrder(node)
{
     
    if(node!=null)
    {
     
        this.inOrder(node.left);
        console.log(node.key);
        this.inOrder(node.right);
    }
}
//后续遍历二叉树
this.postOrder(node)
{
     
     if(node!=null)
    {
     
        this.inOrder(node.left);
        this.inOrder(node.right);
        console.log(node.key);
    }
}
//广度优先遍历二叉树
// 该方法是以纵向的维度对dom树进行遍历,从一个dom节点开始,一直遍历其子节点,直到它的所有子节点都被遍历完毕之后在遍历它的兄弟节点
this.levelOrder(node)
{
     
    console.log(node.key)
    if(node.left)
    {
     
        this.levelOrder(node.left);
    }
    if(node.right)
    {
     
        this.levelOrder(node.right);
    }
}
//查找最小节点
this.findMinNode(node)
{
     
    if(node!=null)
    {
     
        while(node&&node.left!=null)
        {
     
            node=node.left;
        }
        return node.key;
    }
    return null;
}
//查找指定节点
this.findItem(node,key)
{
     
    if(node!=null)
    {
     
        if(key<node.key)
        {
     
            return this.findItem(node.left,key)
        }
        else if(key>node.key)
        {
     
            return this.findItem(node.right,key)
        }
        else
            return true;
    }
    return null;
}

52.算法之字符串

53.0.1+0.2的修复问题

//js的Number类型最多存放64位的浮点数
//而0.1和0.2在计算机中用补码存储的时候都是超过64位的循环数,所以js中0.1和0.2都是近似值
//近似值相加就会出现0.1+0.2=0.3000000004的结果
//解决
1.(0.1*10+0.2*10)/10=>整数不存在这个问题
2.(0.1+0.2).toFixed(1)=>四舍五入,指定小数位数

54.最大岛屿面积

function maxIslandArea(arr)
{
     
    var maxArea=0;
    for(let i=0;i<arr.length;i++)
    {
     
        for(let j=0;j<arr[0].length;j++)
        {
     
            if(arr[i][j]==1)
            {
     
                maxArea=maxArea>getArea(arr,i,j)?maxArea:getArea(arr,i,j)
            }
        }
    }
}
function getArea(arr,i,j)
{
     
    if(i==arr.length||i<0)
        return
    else if(j==arr.length||j<0)
        return
    if(arr[i][j]==1)
    {
     
        arr[i][j]=0;
        return 1+getArea(arr,i+1,j)+getArea(arr,i-1,j)+getArea(arr,i,j+1)+getArea(arr,i,j-1)
    }
    return 0;
}

55.接雨水

//按照列求,当前列所存的雨水为左右两边最高墙高度最小值减去当前列的高度
//若左边墙比当前列墙矮,则没水
function trap(height)
{
     
    //最两端一定不会有水
    var sum=0;
    var length=height.length;
    var leftMax=[];//记录左边数组最大值
    var rightMax=[];//记录右边数组最大值
    for(let i=1;i<length;i++)
    {
     
        //记录当前列左边最大值
        leftMax[i]=Math.max(leftMax[i-1],height[i]);
    }
    for(let i=length-1;i>=0;i--)
    {
     
        //记录当前列右边最大值
        rightMax[i]=Math.max(rightMax[i+1],height[i]);
    }
    for(let i=0;i<length;i++)
    {
     
        sum+=Math.min(leftMax[i],rightMax[i])-height[i];
    }
    return sum;

}

56.机器人路径

/**
 * 问题一:方格从左上角走到右下角的走法数
 * https://blog.csdn.net/sinat_31057219/article/details/105119906?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-6&spm=1001.2101.3001.4242
 * 走格子问题
 * 有一个XxY的网格,一个机器人只能请设计一个算法,计算机器人有多少种走法。走格点且只能向右或向下走,要从左上角走到右下角。
 * 
 * 给定两个正整数int x,int y,请返回机器人的走法数目
 */
//递归版本
function conutWays(x,y)
{
     
    if(x==1||y==1)
        return 1
    return countWays(x-1,y)+countWays(x,y-1)
}

//动态规划
function countWays(x,y)
{
     
    var dp=[][]
    //注意走到第一行末尾和第一列末尾只有一种走法
    for(let i=0;i<x;i++)
    {
     
        dp[i][0]=1;
    }
     for(let j=0;j<y;i++)
    {
     
        dp[0][j]=1;
    }
    //非边界
    for(let i=0;i<x;i++)
    {
     
        for(let j=0;j<y;j++)
        {
     
            dp[i][j]=dp[i-1][j]+dp[i][j-1]
        }
    }
    return dp[x-1][y-1];

}

57.倒数第K个节点

function getKthFromEnd(head,k)
{
     
    // 双指针,先让一个指针向前走K步,再让另一个开始走。
    var first=head;
    var second=head;
    var i=0;
    while(first!=null)
    {
     
        if(i<k)
        {
     
            first=first.next;
        }
        else
        {
     
            first=first.next;
            second=second.next;
        }
        i++;
    }
    return second;
} 

58.根节点到指定节点的路径

var path=[]//存储路径的结构
function getPath(root,target)
{
     
    if(root==null)
        return false;
    path.push(root)
    if(root.key==target.key)
        return true;
    if(root.left!=null)
    {
     
        return getPath(root.left,target)
    }
    if(root.right!=null)
    {
     
        return getPath(root.right,target)
    }
}

59.实现两个大数相加

//先将两个大数转换为字符串,然后比较两个字符串长度,长度不一致添0补齐
function bigNumPlus(a,b)
{
     
    let maxLength=Math.max(a.length,b.length);
    let m=String(a).padStart(maxLength,0).split(""); //以0填充,注意这是string类型的方法。
    let n=String(b).padStart(maxLength,0).split("");
    let sum="";
    let singleNumSum=0;//单个数字相加的和
    let carry=0;//进位
    //然后依次从个位开始加
    for(let i=maxLength-1;i>=0;i--)
    {
     
        singleNumSum=parseInt(m[i])+parseInt(n[i])+carry;
        carry=Math.floor(singleNumSum/10);//进位
        sum+=singleNumSum%10;
    }
    if(carry==1)
    {
     
        sum+="1";
    }
    return sum;
}

60.原型链和原型

每个构造函数都有一个显示原型prototype,prototype上有构造函数的属性和方法。
原型prototype是一个对象,拥有__proto__属性,执行构造函数的原型,这样依次连接,
直到顶层Object的prototype

js获取原型的方法
    p.constructor.prototype
    Object.getPrototypeOf(p)

61.翻转链表

function ListNode(val)
{
     
    this.val=val;
    this.next=null;
}
function reverseList(head)
{
     
    var pre=null;
    var cur=head;
    var temp=null;
    if(cur==null)
        return null
    while(cur)
    {
     
        temp=cur.next;//保存这个节点,以防止找不到
        cur.next=pre;//将原来的pre.next=>cur  改为cur.next=>pre
        pre=cur;//向前进
        cur=temp;//向前进
    }
    return pre;
}

浏览器引擎

浏览器引擎分为渲染引擎和JS引擎
在渲染的过程中遇到js会暂停文档的渲染去执行JS,如果想让JS在渲染之后执行,可以把script标签放在body下面
或者把script标签放在head里面,给它加一个async(异步)或defer(延迟引入)属性

BFC与IFC

BFC 块级格式上下文(元素垂直方向排下去)

BFC内垂直方向的margin会重叠,计算BFC高度的时候,浮动元素也会参与计算,所以达到清除浮动的效果
触发BFC
float,overflow:hidden,display:flex,block

IFC

行内元素从左到右排列,不够就往下一行排。

undefined和undeclared的区别

undefined声明了没有赋值,undeclared未声明

垃圾回收

计算机的动态内存不再需要的时候就应该释放,让出内存。

  • 引用计数
let user={
     
    name:"simple"
}
//现在user就引用了{name:"simple"}这个对象
user=null;
//现在{name:"simple"}变成不可到达的了,不能访问,JS就会回收他


//若是
let user={
     
    name:"simple"
}
let admin=usr;
//现在{name:"simple"}就被两个对象引用
user=null;
//即使执行这个,{name:"simple"}还可以通过admin获取,故未被回收

//循环引用问题
function func() {
     
    let obj1 = {
     };
    let obj2 = {
     };

    obj1.a = obj2; // obj1 引用 obj2
    obj2.a = obj1; // obj2 引用 obj1
}
// 当函数 func 执行结束后,返回值为 undefined,所以整个函数以及内部的变量都应该被回收,但根据引用计数方法,obj1 和 obj2 的引用次数都不为 0,所以他们不会被回收。

// 要解决循环引用的问题,最好是在不使用它们的时候手工将它们设为空。上面的例子可以这么做:
obj1 = null;
obj2 = null;

commonjs AMD CMD import

commosjs静态引入,可修改

var axios=require('axios')
axios.get===   //容易在引入的时候卡死

AMD,先定义所有依赖,等依赖加载完成再执行回调函数,依赖前置

require(['axios'],callback)
// CMD
define(function(require, exports, module) {
     
  var a = require("./a");
  a.doSomething();
  // 此处略去 100 行
  var b = require("./b"); // 依赖可以就近书写
  b.doSomething();
  // ...
});

// AMD 默认推荐
define(["./a", "./b"], function(a, b) {
     
  // 依赖必须一开始就写好
  a.doSomething();
  // 此处略去 100 行
  b.doSomething();
  // ...
});

CMD 不用定义依赖,需要的时候再动态引入,CMD 推崇
就近依赖,只有在用到某个模块的时候再去 require。

双边距折叠问题

两个外边距都为正取较大的那个
一正一副,相加

寻找出现次数最多的单词

function findMostWord(article) {
     
  // 合法性判断
  if (!article) return;

  // 参数处理
  article = article.trim().toLowerCase();

  let wordList = article.match(/[a-z]+/g),
    visited = [],
    maxNum = 0,
    maxWord = "";

  article = " " + wordList.join("  ") + " ";

  // 遍历判断单词出现次数
  wordList.forEach(function(item) {
     
    if (visited.indexOf(item) < 0) {
     

      // 加入 visited 
      visited.push(item);

      let word = new RegExp(" " + item + " ", "g"),
        num = article.match(word).length;

      if (num > maxNum) {
     
        maxNum = num;
        maxWord = item;
      }
    }
  });

  return maxWord + "  " + maxNum;
}

Math.ceil()和Math.floor()

Math.ceil()向上取整
Math.floor()向下取整

keep-alive的作用

保存组件的状态,防止组件重复渲染。设置在外面

路由守卫

router.beforeEach((to,from,next)=>{
     
    //to即将进入页面的路由信息
    //from即将离开的页面路由信息
    //next交出控制权  next(false)中断导航,回到from next()=router.push
})

$route 和 $router 的区别?

$route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。而 $router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。

vue生命周期函数

  • beforeCreated
    vue实例的data和挂载对象el均为undefined
  • created
vue实例的data已经有值了,但是el还没有挂载,有data所以在此时进行请求发送
  • beforeMounted
vue实例data已经有值,el也挂载上去了,但此时还是虚拟DOM
  • mounted
虚拟dom转化为真实dom
  • beforeUpdate updated beforeDestory destoryed

什么是 Proxy ?

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”,即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

Set 和 WeakSet 结构?

  • 1.ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
  • 2.WeakSet 结构与 Set 类似,也是不重复的值的集合。但是 WeakSet 的成员只能是对象,而不能是其他类型的值。WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,

map和foreach的区别

foreach直接修改原数组,没有返回值
map创建一个新数组并返回

怎样冻结一个对象

Object.freeze()方法
一个被冻结的对象再也不能被修改,不能添加新属性,不能删除已有属性,不能修改属性

vue路由原理


Map 和 WeakMap 结构?

  • 1.Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
  • 2.WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。但是 WeakMap 只接受对象作为键名( null 除外),不接受其他类型的值作为键名。而且 WeakMap 的键名所指向的对象,不计入垃圾回收机制。

使用settimeout实现setinterval

function mySetInterval(fn,delay)
{
     
    function interval()
    {
     
        fn();
        setTimeout(interval,delay);
    }
    setTimeout(interval,delay);
}

mouseover 和 mouseenter 的区别?

当鼠标移动到元素上时就会触发 mouseenter 事件,类似 mouseover,它们两者之间的差别是 mouseenter 不会冒泡。

由于 mouseenter 不支持事件冒泡,导致在一个元素的子元素上进入或离开的时候会触发其 mouseover 和 mouseout 事件,但是却不会触发 mouseenter 和 mouseleave 事件。

发布/订阅(观察者模式)

类似于事件监听,但是可以通过‘消息中心’,了解现在有多少发布者,多少订阅者

谈谈你对 webpack 的看法

我当时使用 webpack 的一个最主要原因是为了简化页面依赖的管理,并且通过将其打包为一个文件来降低页面加载时请求的资源
数。

我认为 webpack 的主要原理是,它将所有的资源都看成是一个模块,并且把页面逻辑当作一个整体,通过一个给定的入口文件,webpack 从这个文件开始,找到所有的依赖文件,将各个依赖文件模块通过 loader 和 plugins 处理后,然后打包在一起,最后输出一个浏览器可识别的 JS 文件。

Webpack 具有四个核心的概念,分别是 Entry(入口)、Output(输出)、loader 和 Plugins(插件)。

Entry 是 webpack 的入口起点,它指示 webpack 应该从哪个模块开始着手,来作为其构建内部依赖图的开始。

Output 属性告诉 webpack 在哪里输出它所创建的打包文件,也可指定打包文件的名称,默认位置为 ./dist。

loader 可以理解为 webpack 的编译器,它使得 webpack 可以处理一些非 JavaScript 文件。在对 loader 进行配置的时候,test 属性,标志有哪些后缀的文件应该被处理,是一个正则表达式。use 属性,指定 test 类型的文件应该使用哪个 loader 进行预处理。常用的 loader 有 css-loader、style-loader 等。

插件可以用于执行范围更广的任务,包括打包、优化、压缩、搭建服务器等等,要使用一个插件,一般是先使用 npm 包管理器进行安装,然后在配置文件中引入,最后将其实例化后传递给 plugins 数组属性。

使用 webpack 的确能够提供我们对于项目的管理,但是它的缺点就是调试和配置起来太麻烦了。但现在 webpack4.0 的免配置一定程度上解决了这个问题。但是我感觉就是对我来说,就是一个黑盒,很多时候出现了问题,没有办法很好的定位。

双向数据绑定原理?

vue 通过使用双向数据绑定,来实现了 View 和 Model 的同步更新。vue 的双向数据绑定主要是通过使用数据劫持和发布订阅者模式来实现的。

首先我们通过 Object.defineProperty() 方法来对 Model 数据各个属性添加访问器属性,以此来实现数据的劫持,因此当 Model 中的数据发生变化的时候,我们可以通过配置的 setter 和 getter 方法来实现对 View 层数据更新的通知。

function Archiver() {
  var temperature = null;
  var archive = [];

  Object.defineProperty(this, 'temperature', {
    get: function() {
      console.log('get!');
      return temperature;
    },
    set: function(value) {
      temperature = value;
      archive.push({ val: temperature });
    }
  });

  this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]

数据在 html 模板中一共有两种绑定情况,一种是使用 v-model 来对 value 值进行绑定,一种是作为文本绑定,在对模板引擎进行解析的过程中。

如果遇到元素节点,并且属性值包含 v-model 的话,我们就从 Model 中去获取 v-model 所对应的属性的值,并赋值给元素的 value 值。然后给这个元素设置一个监听事件,当 View 中元素的数据发生变化的时候触发该事件,通知 Model 中的对应的属性的值进行更新。

如果遇到了绑定的文本节点,我们使用 Model 中对应的属性的值来替换这个文本。对于文本节点的更新,我们使用了发布订阅者模式,属性作为一个主题,我们为这个节点设置一个订阅者对象,将这个订阅者对象加入这个属性主题的订阅者列表中。当 Model 层数据发生改变的时候,Model 作为发布者向主题发出通知,主题收到通知再向它的所有订阅者推送,订阅者收到通知后更改自己的数据。

实现bind、apply、call 待做

bind

Function.prototype.myBind(obj)=function()
{
     
    var that=this;
    //获取参数,第0位是this
    var args=[...arguments].slice(1)
    return function()
    {
     
        that.apply(obj,args);
    }
}

apply和call的区别

其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。

js获取文件名

filename.slice(String.prototype.lastIndexOf(.))

documen.write 和 innerHTML 的区别?

document.write 的内容会代替整个文档内容,会重写整个页面。

innerHTML 的内容只是替代指定元素的内容,只会重写页面中的部分内容。

实现一个异步ajax

function getJSON(url) {
     
  // 创建一个 promise 对象
  let promise = new Promise(function(resolve, reject) {
     
    let xhr = new XMLHttpRequest();

    // 新建一个 http 请求
    xhr.open("GET", url, true);

    // 设置状态的监听函数
    xhr.onreadystatechange = function() {
     
      if (this.readyState !== 4) return;

      // 当请求成功或失败时,改变 promise 的状态
      if (this.status === 200) {
     
        resolve(this.response);
      } else {
     
        reject(new Error(this.statusText));
      }
    };

    // 设置错误监听函数
    xhr.onerror = function() {
     
      reject(new Error(this.statusText));
    };

    // 设置响应的数据类型
    xhr.responseType = "json";

    // 设置请求头信息
    xhr.setRequestHeader("Accept", "application/json");

    // 发送 http 请求
    xhr.send(null);
  });

  return promise;
}

[“1”, “2”, “3”].map(parseInt) 答案是多少?

parseInt() 函数能解析一个字符串,并返回一个整数,需要两个参数 (val, radix),其中 radix 表示要解析的数字的基数。(该值介于 2 ~ 36 之间,并且字符串中的数字不能大于 radix 才能正确返回数字结果值,若radix为0,则radix=10)。


此处 map 传了 3 个参数 (element, index, array),默认第三个参数被忽略掉,因此三次传入的参数分别为 "1-0", "2-1", "3-2"

因为字符串的值不能大于基数,因此后面两次调用均失败,返回 NaN ,第一次基数为 0 ,按十进制解析返回 1。

数组随机排序

function randomSort(arr) {
     
  var result = [];

  while (arr.length > 0) {
     
    var randomIndex = Math.floor(Math.random() * arr.length);
    result.push(arr[randomIndex]);
    arr.splice(randomIndex, 1);
  }

  return result;
}
arr.sort(function(){
     
        return Math.random() - 0.5;
})

OSI七层模型

从上到下依次为
7.应用层:

文件传输,常用协议有HTTP,FTP。定义应用进程之间的交互和通信规则

6.表示层

数据格式化,代码转换,数据加密

5.会话层

主机之间建立、解除会话=>写信,填地址

4.传输层

提供端对端接口 TCP/UDP=>投递新建到快递公司或者邮局,为不同主机上的不同进程提供逻辑通信的功能

3.网络层

为数据包选择路由,寻址=>IP,路由器

2.数据链路层

数据通道,建立联络和拆除联络。网卡、交换机

1.物理层

在物理媒介进行传播

HTTP请求头

  • Accept告诉服务器自己接受什么介质类型
  • Accept-charset接收的字符集
  • Accept-Encoding 接受的编码方法,通常指定压缩方法,是否支持压缩gzip
  • Accept-Language
  • Authorization 传输自己的身份信息
  • Host
  • cookie
  • origin
  • user-agent 表明自己的身份,哪种浏览器
  • Referer 表明从哪个URL请求当前URL
  • cache-control 进行缓存控制

HTTP响应头

allow 服务器支持哪些请求方法
Date
Expires 设置文档过期时间
last-modified 文档最后改动时间
set-cookie给客户端设置cookie

cookie localStroage sessionStorage indexDB

localStroage同源,永久存储
sessionStorage关闭窗口就没了,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage也是不一样的。

localStorage和sessionStorage都只能存字符串类型
indexDB存大量的数据,保证用户离线之后还能进行操作

前端SEO

Search Engine

(1)控制链接数量,不能太多
(2)导航尽量采用文字形式,若采用img一点要加alt和title属性,a标签加title
(3)控制页面大小,减少HTTP请求,提高加载速度
(4)每个页面设置不同内容
(5)要尽可能全面概述网页内容
(6)标签语义化
(7)利用CSS布局,将重要内容放前面

hash和history路由

hash中的#可以定位到具体标题处,通过onhashchange监听hash值得改变,任何一个hash得变化都会被浏览器记录下来,所以hash模式下,可以用浏览器得前进后退,但是只能改变#后面得。改变#不会重新发请求。

history.go(1)==forward()前进  history.go(-1)==back()后退
pushState(stateObj,title,url),每次url变化都会发请求

性能优化

1.使用缓存 Cache-Control localStorage  对一些永远不变得使用强缓存
2.懒加载 
3.使用精灵图
4.提前加载
5.用icon font代替图片图标
6.使用事件委托
7.对scroll、inputChange这类高频率事件做防抖处理

git基本操作

git init //初始化仓库
git clone //拷贝远程仓库
git add .//将文件添加到暂存区
git commit //提交暂存区到本地仓库
git reset //版本回退
git diff //比较暂存区和工作区得差异
git rm //删除工作区文件
git log //查看提交历史纪录
git remote //远程仓库连接
git pull //下载远程代码并合并
git push //上传到远程仓库


git branch name //git创建分支
git checkout name //切换分支
git merge name//合并到主分支
git branch //列出分支
git branch -d name //删除分支

css动画

@keyframes name
{
     
    /* 1. from to*/
    from{
     }
    to{
     }
    /*2.百分比*/
    0%{
     }
    20%{
     }
}
div{
     
    animation:name 5s infinite
}

大顶堆与小顶堆

大顶堆:每个节点的值都大于或等于其左右节点的值
小顶堆:每个节点的值都小于或等于其左右节点的值
[二叉树与数组之间转换=>](https://www.cnblogs.com/lanhaicode/p/10546257.html)
堆排序的过程
    将待排序序列n构造成一个大顶堆,此时整个序列最大值是堆顶的节点,将其与末尾元素交换,此时末尾为最大值。然后将剩余n-1个元素重新构造出一个堆,如此反复.

    升序使用大顶堆,降序使用小顶堆

    1.构造大顶堆
    leftNode=2*i+1
    rightNode=2*i+2
    大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 
    小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2] 
    首先找到最后一个非叶子节点,arr.length/2-1就是了,比较和其子树值,满足大顶堆就不交换,否则交换,然后依次向上,就是当前节点下标减一,直到顶节点,满足大顶堆后进行排序。

    2.排序过程
    交换跟节点与末尾元素的值,剩下的n-1个元素若不满足大顶推,则再次构造大顶堆,然后如此反复。
//构建大顶堆
function MaxHeap(array){
     
	//开始位置是最后一个非叶子节点
	let start = Math.floor(array.length/2-1);
	for(let i = start;i>=0;i--){
     
		console.log(array);
		createHeap(array,i);
	}
	//打印构造好的大顶堆
	console.log(array);
	function createHeap(array,index){
     
		//左子节点
		let leftNode = 2*index+1;
		//右子节点
		let rightNode = 2*index+2;
		let max = index;
		//左右子节点与根节点比较,找出最大的数
		if(leftNode<array.length&&array[leftNode]>array[max]){
     
			max = leftNode;
		}
		if(rightNode<array.length&&array[rightNode]>array[max]){
     
			max = rightNode;
		}
		//交换位置
		if(max != index){
     
			let temp = array[max];
			array[max] = array[index];
			array[index] = temp;
			//交换位置后,不确定该位置下是否还是大顶堆,需要重排
			createHeap(array,max);
		}
	}
}
//构造小顶堆
function MinHeap(array){
     
	//同样是从最后一个非叶子节点开始处理
	let start = Math.floor(array.length/2-1);
	//构造小顶堆
	for(let i = start;i>=0;i--){
     
		createHeap(array,i);
	}
	console.log(array);
	function createHeap(array,index){
     
		//左叶子节点
		let leftNode = 2*index+1;
		//右叶子节点
		let rightNode = 2*index+2;
		let min = index;
		if(leftNode<array.length&&array[leftNode]<array[min]){
     
			min = leftNode;
		}
		if(rightNode<array.length&&array[rightNode]<array[min]){
     
			min = rightNode;
		}
		//交换位置
		if(min != index){
     
			let temp = array[min];
			array[min] = array[index];
			array[index] = temp;
			//交换位置之后不确定该min位置是否还是符合小顶堆,需要重排
			createHeap(array,min);
		}
	}
}


从尾到头打印链表

function printReverse(root)
{
     
    if(root!=null)
    {
     
        if(root.next!=null)
        {
     
            printReverse(root.next);
        }
        console.log(root.value);
    }
}
//非递归,利用栈结构先进后出
function printReverse(root)
{
     
    var arr=[];
    var node=root.next;
    while(node!=null)
    {
     
        arr.push(node);
        node=node.next;
    }
    while(arr)
    {
     
        var s=arr.pop();
        console.log(s.value);
    }
}

如何判断单链表是否有环

//使用快慢指针,慢指针每次向前一步,快指针每次向前两步,若两指针相遇则有环路
function isLoop(root)
{
     
    var slow=root;
    var fast=root;
    while(slow!=null&&fast.next!=null)
    {
     
        slow=slow.next;
        fast=fast.next.next;
    }
    if(slow==fast)
    {
     
        return true;
    }
    else
    {
     
        return false;
    }
}
//求环的长度,第一次相遇之后的第二次相遇,快指针比慢指针多跑了一圈就是长度

关系数据库与非关系数据库

关系数据库,一个关系模型就是一个二维表,关系数据库是按照二维表存储的,容易理解。但是面对大型数据,因为多表的关联连接导致查询效率低。
而非关系数据库是按照键值对来存储的

设计模式

  • 单例模式
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
单例模式有 3 个特点:
    单例类只有一个实例对象;
    该单例对象必须由单例类自行创建;
    单例类对外提供一个访问该单例的全局访问点。

    通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

//实现单列模式
class SingleTon{
     
    private static  SingleTon instance = new SingleTon();    //保证 instance 在所有线程中同步
    private SingleTon();
    {
     }//避免在外部被实例化
    public static SingleTon getInstance()
    {
     
        return isntance;
    }

}
//http://c.biancheng.net/view/1322.html待完成

get和post的区别

get参数通过url传递放在请求头中,post参数放在requestbody中
get更不安全,参数在url中暴露出来,且从参数有长度的限制
GET 的最大长度显示是因为浏览器和 web 服务器限制了 URI 的长度

get 请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,一般用于对服务器资源不产生影响的场景,所以可以使用缓存。而post用于对服务器资源产生影响的场景。

post 不同,post 做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此 get 请求适合于请求缓存。

尾递归

function fn()
{
     
    //js语句
    callback()
}
//进入下一个函数不再需要上一个函数的环境了,得出结果直接返回。

//非尾递归
function fn()
{
     
    //js语句
    callback()
    //js语句
}
// 下一个函数结束以后此函数还有后续,所以必须保存本身的环境以供处理返回值。
}

类数组

//函数的 arguments就是类数组,拥有length属性但是不具备数组的方法

//类数组转换为数组
//1
Array.prototype.slice.call(arrLike)
//2
Array.from(arrLike)

执行上下文

在 JavaScript 代码开始执行时,首先进入全局环境,此时全局上下文被创建并入栈,之后当调用函数时则进入相应的函数环境,此时相应函数上下文被创建并入栈,当处于栈顶的执行上下文代码执行完毕后,则会将其出栈。

所以在执行上下文栈中,栈底永远是全局上下文,而栈顶则是当前正在执行的函数上下文。

移动端1px问题

1px是css中的逻辑像素,而每个手机的屏幕大小不同,就会导致1px在不同设备下显示的宽度不一样。

解决方案

  • 1.媒体查询=>devicePixelRatio
.border {
      border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
     
    .border {
      border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
     
    .border {
      border: 0.333333px solid #999 }
}
  • 2.使用伪类缩放
border::after{
     
    transfrom:scaleY(0.5)
}

移动端和PC的不同

1.PC端主要考虑的是浏览器的兼容性,而移动端考虑得更多的是手机的兼容性如屏幕大小之类的。
2.在时间处理上,移动端考虑更多的是触摸事件,缺少hover事件,还要考虑弹出手机键盘的处理。
3.

typescript

运行typescript
tsc xxx.ts
ts比js有更多的规范,类型、接口

小程序与H5的区别

1.运行环境不同
    H5运行环境是浏览器,而小程序的运行环境是微信,所以无法使用window和document对象
2.获取系统级权限不同
    小程序可能要获得更多的权限如相册、定位、网速等
3.运行的流畅度不同
    打开H5,实际上是打开一个网页,而网页需要在浏览器中渲染。所以加载这一过程,会给人明显的「卡顿」感觉,面对复杂的业务逻辑或者丰富的页面交互时尤为明显。

    而微信小程序,它的代码直接在微信上运行,省去了通过浏览器渲染的步骤,因此,在微信中使用小程序,才会比H5流畅很多。

小程序特点

1.触手可及,用完即走
    不需要下载,而且小程序还可以将图标生成到手机桌面,不占内存。
2.成本更低
    开发一个原生APP动辄十几万,微信小程序的开发成本相对较低,且准入门槛低、申请流程简单,后台操作简单易行。
3.节省手机空间
4.微信10亿用户——潜在消费者

小程序技术总结

云开发环境配置

//在App.vue
wx.cloud.init({
     
    env: 'mycloud-7gvadbzce145beeb',//云开发的环境ID
    traceUser: true
});

上传图片

wx.cloud.uploadFile({
     
    cloudPath: 'static/' + fileName,//上传路径
    filePath: img, // 文件路径
})
//上传完成会返回相应的fileid,从数据库拿文件路径fileID就可以了

// 下载文件
wx.cloud.downloadFile({
     
  fileID: '', // 文件 ID
  success: res => {
     
    // 返回临时文件路径
    console.log(res.tempFilePath)
  },
  fail: console.error
})
// 删除就是deleteFile

操作数据库

var db=wx.cloud.database()
    let users =db.collection("user")
    users.add({
     
        data:user
    })

获取当前路由

//getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。
let pages = getCurrentPages();
let currPage = null;
if (pages.length) {
     
   currPage = pages[pages.length - 1];
}
console.log(currPage)   

调整第一次加载的页面

在static->app.json中改变pages的顺序

使用腾讯地图

var QQMapWX = require('../../libs/qqmap-wx-jssdk.js');
var qqmapsdk;
qqmapsdk = new QQMapWX({
     
            key: 'B4VBZ-R6DWS-WVKOX-6KXBL-GDA2Q-ZWBTP'
})
qqmapsdk.reverseGeocoder({
     
success:res=>{
     
    console.log(res)
    this.address=res.result.ad_info.city
},
fail:err=>{
     
    console.log('用户拒绝定位')
    this.address="北京市"
}
})


 "permission": {
     
    "scope.userLocation": {
     
    "desc": "你的位置信息将用于小程序位置接口的效果展示"
    }
  },

用户评论

云函数封装百度人工智能API并在用户提交时调用,API接口将会根据用户的评论提取若干个关键词,取出第一个,并与用户提交的数据一并保存至云数据库中

四次挥手过程

第一次挥手:

Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

第二次挥手:

Server收到FIN后,发送一个ACK给Client,Server进入CLOSE_WAIT状态。

第三次挥手:

Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。

第四次挥手:

Client收到FIN后,Client进入TIME_WAIT状态,发送ACK给Server,Server进入CLOSED状态,完成四次挥手。

var let const区别

var 在函数中声明其作用域为整个函数
let 为块级作用域,形如for(let i in xxx)每次循环都会为i创建新的绑定,且let不允许重复声明
const 定义常量,不可修改

箭头函数与function的区别

箭头函数没有自己的this对象,在执行的时候,绑定其执行环境的this
箭头函数没有构造函数
箭头函数没有arguments对象
箭头函数没有变量提升,不能在定义之前使用

async await

异步同步化
async隐式地返回一个promise对象,它的return值就相当于 Promise.resolve
内部await接受一个promise对象,必须等待接受了这个promise对象之后才执行下一条语句。

递归求二叉树深度

  function deepPath(tree)
  {
     
    if(tree == null)
    {
     
     return 0
    }
    else
    {
     
     let left = deepPath(tree.left)
     let right = deepPath(tree.right)    
     return 1+Math.max(left,right)
   }
 }

伪类与伪元素

单冒号表示伪类,来表示元素的一些特殊的状态,如hover、link、visited、active
双冒号表示伪元素,表示元素的一些特殊的位置,如after、before

css中可继承的属性

1.字体系列
font、font-size、font-family、font-weight
2.文本系列属性
text-aligh、text-indent、text-shadow、line-height
3.光标属性
cursor
4.元素可见性
visiability、color

当一个属性不是继承属性的时候,我们可以通过将其值设置为inherit来是的其继承

position的值为absolute和relative的定位原点是?

absolute
    相对于定位值部位static的第一个父元素的padding-box的左上角
relative
    相对于自己元素本身正常位置进行定位

static
    没有定位,出现在正常流中

flex布局

flex布局是css3新增的一个布局方式,将元素的display设置为flex使得其成为一个flex容器,他的所有子元素都会成为其项目。

一个容器默认有两条轴,一个是水平主轴,一个是与主轴垂直的交叉轴,flex-dirction决定主轴的方向,justifi-content决定主轴的排列方式,align-items决定侧轴的排列方式,flex-wrap决定换行方式。

在空间有剩余的时候,可以用flex-grow来决定项目的放大比列(默认为0不放大),空间不足的时候,用flex-shrink来决定项目的缩小比列(默认为1缩小)。

flex是flex-grow、flex-shrink、flex-basis的简写:默认值为:0 1 auto
第三个参数表示: flex-basis给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小
flex: 1; === flex: 1 1 auto;

如何设计一个满屏的品字布局

上面的div宽100%,
下面的两个div分别宽50%,
然后用float或者inline使其不换行即可

li 与 li 之间有看不见的空白间隔是什么原因引起的?有什么解决办法?

有时,在写页面的时候,会需要将
  • 这个块状元素横排显示,此时就需要将display属性设置为inline-block,此时问题出现了,在两个
  • 元素之间会出现大约8px左右的空白间隙 浏览器的默认行为是把inline元素间的空白字符(空格换行tab)渲染成一个空格,也就是我们上面的代码
  • 换行后会产生换行字符,而它会变成一个空格,当然空格就占用一个字符的宽度。 解决办法 ul { letter-spacing:-5px; //letter-spacing 属性增加或减少字符间的空白(字符间距)。 } 之后记得设置li内字符间隔 .wrap ul li{letter-spacing: normal;}
  • width:100%和width:auto的区别

    width:100%使得元素box的宽度等于父元素content-box的宽度
    width:auto使得元素撑满整个父元素,margin、border、padding、content区域会自动分配水平空间。
    

    为什么要清除浮动

    当元素设置为浮动的时候,可以左右游动,不属于正常的文档流,元素浮动之后不会影响块级元素的布局,只会影响内联元素的布局,当文档流中的包含框高度小于浮动框的时候,就会出现高度塌陷。所有需要清除浮动
    .clear::after{
    content:'';
    display:table;//也可以是'block',或者是'list-item'
    clear:both;
    }
    

    CSS 优化、提高性能的方法有哪些?

    加载性能:
    
    (1)css压缩:将写好的css进行打包压缩,可以减少很多的体积。
    (2)css单一样式:当需要下边距和左边距的时候,很多时候选择:margin:top 0 bottom 0;但margin-bottom:bot
    tom;margin-left:left;执行的效率更高。
    (3)减少使用@import,而建议使用link,因为后者在页面加载时一起加载,前者是等待页面加载完成之后再进行加载。
    
    选择器性能:
    
    (1)关键选择器(key selector)。选择器的最后面的部分为关键选择器(即用来匹配目标元素的部分)。CSS选择符是从右到
    左进行匹配的。当使用后代选择器的时候,浏览器会遍历所有子元素来确定是否是指定的元素等等;
    
    (2)如果规则拥有ID选择器作为其关键选择器,则不要为规则增加标签。过滤掉无关的规则(这样样式系统就不会浪费时间去匹
    配它们了)。
    
    (3)避免使用通配规则,如*{}计算次数惊人!只对需要用到的元素进行选择。
    
    (4)尽量少的去对标签进行选择,而是用class。
    
    (5)尽量少的去使用后代选择器,降低选择器的权重值。后代选择器的开销是最高的,尽量将选择器的深度降到最低,最高不要超过
    三层,更多的使用类来关联每一个标签元素。
    
    (6)了解哪些属性是可以通过继承而来的,然后避免对这些属性重复指定规则。
    
    渲染性能:
    
    (1)慎重使用高性能属性:浮动、定位。
    
    (2)尽量减少页面重排、重绘。
    
    (3)去除空规则:{}。空规则的产生原因一般来说是为了预留样式。去除这些空规则无疑能减少css文档体积。
    
    (4)属性值为0时,不加单位。
    
    (5)属性值为浮动小数0.**,可以省略小数点之前的0。
    
    (6)标准化各种浏览器前缀:带浏览器前缀的在前。标准属性在后。
    
    (7)不使用@import前缀,它会影响css的加载速度。
    
    (8)选择器优化嵌套,尽量避免层级过深。
    
    (9)css雪碧图,同一页面相近部分的小图标,方便使用,减少页面的请求次数,但是同时图片本身会变大,使用时,优劣考虑清
    楚,再使用。
    
    (10)正确使用display的属性,由于display的作用,某些样式组合会无效,徒增样式体积的同时也影响解析性能。
    
    (11)不滥用web字体。对于中文网站来说WebFonts可能很陌生,国外却很流行。web fonts通常体积庞大,而且一些浏
    览器在下载web fonts时会阻塞页面渲染损伤性能。
    
    可维护性、健壮性:
    
    (1)将具有相同属性的样式抽离出来,整合并通过class在页面中进行使用,提高css的可维护性。
    (2)样式与内容分离:将css代码定义到外部css中。
    

    在网页中应该使用偶数还是奇数字体

    应该使用偶数字体
    1.偶数字体更容易和其他部分构成比列关系
    2.低版本的IE 6会把奇数字体强制转化为偶数
    

    margin和padding的使用场景

    margin:隔开元素与元素
        需要在border外侧添加空白时
        空白处不需要背景色时
        上下两个相连的盒子之间的空白需要相互抵消的时候,如15PX+20PX,将得到20PX的空白
    padding:隔开元素与内容
        需要在border内侧添加空白的时候
        空白处需要背景色时
        上下两个相连的盒子之间的空白希望等于两者之和时,如15px+20px=35px
    

    为什么不建议使用通配符初始化 CSS 样式

    采用
    *
    {
        margin:0;
        padding:0;
    }
    这样的写法的好处是写起来很简单,但是通配符需要把所有的标签都遍历一遍,当网站的样式比较多的时候,这样写就大大加大了网站运行的负载。
    

    css包含块

    在 CSS2.1 中,很多框的定位和尺寸的计算,都取决于一个矩形的边界,这个矩形,被称作是包含块( containing block )。 一般来说,(元素)生成的框会扮演它子孙元素包含块的角色;我们称之为:一个(元素的)框为它的子孙节点建造了包含块。包含块是一个相对的概念。

    hi
    以上代码为例,DIV 和 TABLE 都是包含块。DIV 是 TABLE 的包含块,同时 TABLE 又是 TD 的包含块,不是绝对的。 “一个框的包含块”,指的是“该框所存在的那个包含块”,并不是它建造的包含块。比如,上述代码中,TABLE 的包含块,说的是 DIV 建造的包含块,而不是 TABLE 自身建造的包含块。TABLE 建造的包含块,可以称作 TD 的包含块。

    全屏滚动的原理

    假设有N个内容需要全屏滚动,设置父容器的高度为N00%,同时设置父容器overflow:hidden,然后通过更改父容器的top值来加载不同的页面
    

    什么是响应式设计

    响应式设计是一个网站能够兼容多个终端,而不是为每个终端做一个特定的版本,基本原理是通过媒体查询检测不同的设备屏幕尺寸做处理,页面必须有meta声明的viewport
    

    如果需要手写动画,你认为最小时间间隔是多少

    多数显示器的默认刷新频率是60hz,即一秒刷新60次,所以理论上最小间隔为1/60*1000ms=16.7ms
    

    移除display:inline-block元素间的空格

    1.使用margin负值(内部元素)
    2.使用letter-spacing或者word-spacing设置为0(内部元素)
    3.父元素font-size:0
    

    一个高度自适应的div,里面两个div,一个高度100px,希望另一个div填满剩下的高度怎么做

    1.外层div position:relative; 自适应div position:absolute;top:100px;bottom:0;
    2.外层div display:flex; flex-dirction:column;
    内层div flex-grow:1; 或者flex:1;
    

    常见的图片格式

    1.bmp
        支持索引色和直接色的点阵图,由于没有压缩导致其体积很大
    2.gif
        无损压缩的是用索引色的点阵图,支持动画和透明色,体积小
    3.jpeg
        有损压缩的使用直接色的点阵图,由于使用直接色,色彩较为丰富.一半用来存储照片,但是使用直接色导致其体积较大
    4.svg
        矢量图,放大缩小不会产生锯齿或者失真,一般用来制作logo或者图标
    

    css精灵图(sprites)

    将一个页面所涉及到的所有图片都包含到一张大图中去,然后利用css的background-position进行背景的定位。
    
    这样做能够减少网页的http请求,提高页面的性能。
    缺点是图片合并、布局麻烦
    

    rem

    fontsize of root element=>html
    

    画一条0.5px的线

    transform:scaleY()
    

    css中transition和animation的区别

    transition: 强调过渡=>关注css property的变化
    需要触发一个事件,比如鼠标移上去、焦点、点击。
    
    animation:多个关键帧,实现自由动画;关注元素本身的变化
    不需要触发任何事件也可随时间变化达到一种动画效果;
    与transition不同是animation可以通过@keyframe控制当前帧属性,更灵活。
    

    为什么元素的height:100%会失效

    对于普通文档流中的元素,高度的百分比想要起作用,其父级元素必须有一个可以生效的高度值。
    
    如果父级元素没有显示的指定高度,并且该元素不是绝对定位,则计算值为:auto,无法参与计算
    

    margin:auto的填充规则

    用来计算元素对应方向应该获得的剩余间距大小,触发margin:auto有一个前提条件就是width或者height为auto的时候。
    
    1.如果一侧为定值,一侧auto,则auto为剩余空间的大小
    2.如果两侧均为auto,则平分剩余的空间
    

    line-height的特殊性

    行距=line-height减去font-size
    

    white-space

    设置落中的文本是否换行
    

    实现单行/多行文本溢出的省略

    /*单行文本溢出*/
    p
    {
         
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
    }
    /*多行文本溢出*/
    p {
         
      position: relative;
      line-height: 1.5em;
      /*高度为需要显示的行数*行高,比如这里我们显示两行,则为3*/
      height: 3em;
      overflow: hidden;
    }
    

    doctype的作用是什么

    告诉浏览器的解析器用什么文档标准解析这个文档
    标准模式:
        按照最新的标准进行解析
    兼容模式:
        向后兼容的方式,也就是按照旧的标准进行解析
    doctype不存在或者格式不正确会导致文档以兼容模式呈现
    

    XML、HTML、XHTML之间的区别

    HTML是超文本标记语言,主要用于规定怎么显示网页
    
    XML为可扩展标记语言,与HTML最大的区别再于XML的标签是可以自己创建的,数量无限多,而HTML的标签数量是固定的。
    
    XHTML比HTML更规范严格,标签必须小写且必须闭合
    

    行内元素和块级元素的区别

    格式上
        行内元素不会以新行开始,而块级元素会新起一行。
    内容上
        行内元素只能包含文本和其他的行内元素,而块级元素可以包含行内元素和其他块级元素。
    属性上
        行内元素设置width和height无效(可以设置line-height),左右的margin和padding也是无效的。
    

    什么是空元素

    标签内没有内容的HTML标签为空元素
    常见的空元素有br hr img input link
    

    页面导入样式的时候,link和@import有什么区别

    1.从属关系上看
        @import是css提供的语法规则,只能导入css文件,而林肯不仅可以导入css文件.还可以引入网站图标
    2.加载顺序的 区别
        加载页面的时候,link标签引入的css被同时加载.@import引入的css在页面加载完毕之后才加载
    

    浏览器的渲染原理

    1.首先解析收到的文档,根据文档定义构建一颗DOM树,DOM树是由DOM元素及其属性节点组成的。
    
    2.对CSS进行解析,生成CSSDOM规则树。
    
    3.根据DOM树和CSSDOM树规则构建渲染树。渲染树的节点被成为渲染对象。渲染对象是一个包含有颜色和大小等属性的举行,渲染对象和DOM元素相对应。
    
    4.当渲染对象被创建并添加刀树中,他们呢并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也叫重排或回流)
    
    5.布局结束之后就是绘制阶段了,遍历渲染树并调用渲染对象的paint方法将他们的内容显示在屏幕上。
    
    为了更好的用户体验,渲染引擎并不会等到所有的HTML都解析完成之后再去构建和布局render树,而是解析完一部分就显示一部分的内容。
    

    为什么不建议直接操作DOM

    直接操作DOM会引起页面的回流或者重绘,从而引起性能上的消耗。
    

    HTML5的新特性

    canvas
    支持音视频的video和audio元素
    存储上:localStroage和sessionStorage
    语义化标签:article、footer、header、nav、section
        用正确的标签做正确的事
        使得内容结构化,结构更清晰
        有利于爬虫和SEO
    表单控件:calendar、date、Email、search
    获取用户当前位置:getCurrentPosition()
    

    b和strong的区别以及i和em的区别

    被b和strong包围的文字都将会被加粗,被i和em包围的文字都将以斜体的形式呈现
    
    但是b和i分别表示无意义的加粗和斜体
    而em和strong是语义化标签
    

    JavaScript 有几种类型的值?你能画一下他们的内存图吗?

    涉及知识点:

    • 栈:原始数据类型(Undefined、Null、Boolean、Number、String)
    • 堆:引用数据类型(对象、数组和函数)
    两种类型的区别是:存储位置不同。
    原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
    
    引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
    

    内部属性[[class]]是什么

    所有typeof返回值为object的对象都包含一个内部属性,这个属性无法直接访问,可以通过Object.prototype.toString.call()来访问
    
    Object.prototype.toString.call( [1,2,3] );
    // "[object Array]"
    

    undefined==null

    双等号返回true
    三等号返回false
    

    如何安全获取undefined的值

    void 0
    

    typeof NaN的结果是什么

    NaN意指不是一个数字,即执行数学运算没有成功,这是失败后返回的结果
    所以typeof NaN的接果是 number
    
    NaN是一个特殊值, NaN!=NaN的结果为true
    

    Array构造函数只有一个参数

    Array构造函数只有一个参数的时候,该参数会被作为数组的预设长度(lenght),而非数组元素。这样创建出来的只是一个空数组,只不过他的length值被设置成了指定的值。
    

    valueOf

    valueOf方法返回指定对象的原始值
    
    对象	返回值
    Array	返回数组对象本身。
    Boolean	布尔值。
    Date	存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。
    Function	函数本身。
    Number	数字值。
    Object	对象本身。这是默认情况。
    String	字符串值。
     	Math 和 Error 对象没有 valueOf 方法。
    

    什么情况下会发生布尔值的隐式类型转换

    (1) if (..) 语句中的条件判断表达式。
    (2) for ( .. ; .. ; .. ) 语句中的条件判断表达式(第二个)。
    (3) while (..) 和 do..while(..) 循环中的条件判断表达式。
    (4) ? : 中的条件判断表达式。
    (5) 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)。
    

    Number() parseInt() parseFloat的区别

    Number()可以用于任何数据类型转换成数值
    parseInt()、parseFloat(): 专门用于把字符串转换成数值;非数字部分字符串在转换过程中会被去除
    var num1=parseInt("num123");    //NaN
    var num2=parseInt("");          //NaN
    var num3=parseInt("123.45")     //123
    var num4=parseInt("101010",2)   //42
    var num5=parseInt("123num")     //123
    var num6=parseInt("0xff")       //255
    
    

    常用正则表达式

    1.匹配16进制颜色值
        var reg=/#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g
    2.匹配QQ号
        var regex = /^[1-9][0-9]{4,10}$/g;
    
    4.手机号码正则
    var regex = /^1[34578]\d{9}$/g;
    
    

    随机交换数组内的元素(洗牌算法)

    function randomSort(arr)
    {
         
        var randomIndex,temp,len=arr.length;
        for(let i=0;i<len;i++)
        {
         
            randomIndex=Math.floor(Math.random()*(len-i))+i;
            temp = arr[i];
            arr[i] = arr[randomIndex];
            arr[randomIndex] = temp;
            //或者
            /*
            *
            [array[index], array[randomIndex]] = [array[randomIndex], array[index]];
            */
        }
        return arr;
    }
    

    如何判断一个对象是否属于某个类

    1.使用instanceof判断构造函数的prototype属性是否出现在对象的原型链中的任何位置
    functiuon myinstanceof(left,right)
    {
        let leftp=left.__proto__;
        let rightp=right.prototype;
        return leftp==rightp?true:false;
    }
    2.通过对象的constructor属性来判断
    3.通过Object.prototype.toString()
    

    对于json的理解

    JSON是一种轻量级的数据交换格式,可以表示数字、布尔值、数组、字符串和对象
    json.parse()将json转换为js对象
    json.stringfy()将js对象转换为json对象
    

    浏览器的同源策略

    一个域下的js脚本在未经允许的情况下不能访问另外一个域的内容。这里的同源指的是两个域的协议、域名端口号必须相同,否则不属于同一个域。
    
    
    第一个是当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。
    
    第二个是当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。
    
    第三个是当前域下 ajax 无法发送跨域请求。
    

    js中的作用域与变量提升

    变量提升的表现是,无论我们在函数中何处位置声明的变量,好像都被提升到了函数的首部,我们可以在变量声明前访问到而不会报错。
    
    造成变量声明提升的本质原因是 js 引擎在代码执行前有一个解析的过程,创建了执行上下文,初始化了一些代码执行时需要用到的对象。当我们访问一个变量时,我们会到当前执行上下文中的作用域链中去查找,而作用域链的首端指向的是当前执行上下文的变量对象,这个变量对象是执行上下文的一个属性,它包含了函数的形参、所有的函数和变量声明,这个对象的是在代码解析的时候创建的。这就是会出现变量声明提升的根本原因。
    

    哪些操作会导致内存泄漏

    1.使用未声明的变量,意外地创建了一个全局变量
    2.被遗忘地定时器,设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
    3.闭包
    

    hash和history两种模式的区别

    //hash模式
        /*hash模式背后的原理是onhashchange事件,可以在window对象上监听这个事件:*/
        window.onhashchange = function(event){
         
         console.log(event.oldURL, event.newURL);
         let hash = location.hash.slice(1); 
         document.body.style.color = hash;
    }
    /*
    更关键的一点是,因为hash发生变化的url都会被浏览器记录下来,从而你会发现浏览器的前进后退都可以用了,同时点击后退时,页面字体颜色也会发生变化。这样一来,尽管浏览器没有请求服务器,但是页面状态和url一一关联起来。
    */
    
    
    history路由
    /*
    随着history api的到来,前端路由开始进化了,前面的hashchange,你只能改变#后面的url片段,而history api则给了前端完全的自由
    
    */
    

    什么是 CSP?

    CSP 指的是内容安全策略(content-security-policy),它的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截由浏览器自己来实现。
    
    通常有两种方式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的方式 
    

    使用setTimeout实现setInterval

    function myinterval(fn,delay)
    {
         
        let timer=true;//控制定时器是否继续执行
        function interval()
        {
         
            if(timer)
            {
         
                fn();
                setTimeout(()=>{
         interval()},delay);
            }
        }
        setTimeout(()=>{
         interval()},delay);
        // 返回控制器
        return timer;
    }
    

    let 和 const 的注意点?

    • 1.声明的变量只在声明时的代码块内有效
    • 2.不存在声明提升
    • 3.存在暂时性死区,如果在变量声明前使用,会报错
    • 4.不允许重复声明,重复声明会报错

    vue组件通信

    (1)父子组件间通信
    
    第一种方法是子组件通过 props 属性来接受父组件的数据,然后父组件在子组件上注册监听事件,子组件通过 emit 触发事
    件来向父组件发送数据。
    
    第二种是通过 ref 属性给子组件设置一个名字。父组件通过 $refs 组件名来获得子组件,子组件通过 $parent 获得父组
    件,这样也可以实现通信。
    
    
    (2)兄弟组件间通信
    
    第一种是使用 eventBus 的方法,它的本质是通过创建一个空的 Vue 实例来作为消息传递的对象,通信的组件引入这个实
    例,通信的组件通过在这个实例上监听和触发事件,来实现消息的传递。
    
    第二种是通过 $parent.$refs 来获取到兄弟组件,也可以进行通信。
    

    方法、computed 和 watch 的差异?

    <div id="app">
        <p>{
         {
         fullName}}</p>
    </div>
    
    new Vue({
         
        data: {
         
            firstName: 'Xiao',
            lastName: 'Ming'
        },
        computed: {
         
            fullName: function () {
         
                return this.firstName + ' ' + this.lastName
            }
        }
    })
    
    
    有多个模板,方法会执行多次,而计算属性只执行一次
    (1)computed 是计算一个新的属性,并将该属性挂载到 Vue 实例上,而 watch 是监听已经存在且已挂载到 Vue 实例上的数据,所以用 watch 同样可以监听 computed 计算属性的变化。
    
    (2)computed 本质是一个惰性求值的观察者,具有缓存性,只有当依赖变化后,第一次访问 computed 属性,才会计算新的值。而 watch 则是当数据发生变化便会调用执行函数。
    
    (3)从使用场景上说,computed 适用一个数据被多个数据影响,而 watch 适用一个数据影响多个数据。
    

    vue中key值得作用

    第二种情况是 v-for 中使用 key。用 v-for 更新已渲染过的元素列表时,它默认使用“就地复用”的策略。如果数据项的顺序发生了改变,Vue 不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处的每个元素。因此通过为每个列表项提供一个 key 值,来以便 Vue 跟踪元素的身份,从而高效的实现复用。这个时候 key 的作用是为了高效的更新渲染虚拟 DOM。
    

    keep-alive 组件有什么作用?

    如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 组件包裹需要保存的组件。
    

    如何封装一个 javascript 的类型判断函数?

    function getType(value) {
         
      // 判断数据是 null 的情况
      if (value === null) {
         
        return value + "";
      }
    
      // 判断数据是引用类型的情况
      if (typeof value === "object") {
         
        let valueClass = Object.prototype.toString.call(value),
          type = valueClass.split(" ")[1].split("");
    
        type.pop();
    
        return type.join("").toLowerCase();
      } else {
         
        // 判断数据是基本数据类型的情况和函数的情况
        return typeof value;
      }
    }
    

    如何判断一个对象是否为空对象?

    function checkNullObj(obj) {
         
      return Object.keys(obj).length === 0;
    }
    

    手写一个 jsonp

    function jsonp(url, params, callback) {
         
      // 判断是否含有参数
      let queryString = url.indexOf("?") === "-1" ? "?" : "&";
    
      // 添加参数
      for (var k in params) {
         
        if (params.hasOwnProperty(k)) {
         
          queryString += k + "=" + params[k] + "&";
        }
      }
    
      // 处理回调函数名
      let random = Math.random()
          .toString()
          .replace(".", ""),
        callbackName = "myJsonp" + random;
    
      // 添加回调函数
      queryString += "callback=" + callbackName;
    
      // 构建请求
      let scriptNode = document.createElement("script");
      scriptNode.src = url + queryString;
    
      window[callbackName] = function() {
         
        // 调用回调函数
        callback(...arguments);
    
        // 删除这个引入的脚本
        document.getElementsByTagName("head")[0].removeChild(scriptNode);
      };
    
      // 发起请求
      document.getElementsByTagName("head")[0].appendChild(scriptNode);
    }
    

    HTTP协议

    HTTP有两种连接模式,一种是持续连接,一种是非持续连接。
    非持续连接
        服务器必须为每一个请求的对象建立和维护一个全新的连接。
    持续连接
        持续连接下,TCP连接默认不关闭,可以被多个请求复用,采用持续连接的好处是可以避免每次TCP连接三次握手所花费的时间.
    
    在HTTP1.0之前使用的是非持续连接,但是可以在请求的时候加上Connection:keep-alive来要求服务器不要关闭TCP连接。在HTTP1.1之后,默认采用的是持续连接
    

    HTTP 请求报文

    HTTP 报文有两种,一种是请求报文,一种是响应报文。

    HTTP 请求报文的格式如下:

    
    GET / HTTP/1.1
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
    Accept: */*
    
    

    HTTP 请求报文的第一行叫做请求行,后面的行叫做首部行,首部行后还可以跟一个实体主体。请求首部之后有一个空行,这个空行不能省略,它用来划分首部与实体。

    请求行包含三个字段:方法字段、URL 字段和 HTTP 版本字段。

    方法字段可以取几种不同的值,一般有 GET、POST、HEAD、PUT 和 DELETE。一般 GET 方法只被用于向服务器获取数据。
    POST 方法用于将实体提交到指定的资源,通常会造成服务器资源的修改。HEAD 方法与 GET 方法类似,但是在返回的响应
    中,不包含请求对象。PUT 方法用于上传文件到服务器,DELETE 方法用于删除服务器上的对象。虽然请求的方法很多,但
    更多表达的是一种语义上的区别,并不是说 POST 能做的事情,GET 就不能做了,主要看我们如何选择。更多的方法可以参
    看文档。

    HTTP 响应报文

    HTTP 报文有两种,一种是请求报文,一种是响应报文。

    HTTP 响应报文的格式如下:

    HTTP/1.0 200 OK
    Content-Type: text/plain
    Content-Length: 137582
    Expires: Thu, 05 Dec 1997 16:00:00 GMT
    Last-Modified: Wed, 5 August 1996 15:55:28 GMT
    Server: Apache 0.84
    
    
      Hello World
    
    

    HTTP 响应报文的第一行叫做状态行,后面的行是首部行,最后是实体主体。

    状态行包含了三个字段:协议版本字段、状态码和相应的状态信息。

    实体部分是报文的主要部分,它包含了所请求的对象。

    常见的状态有

    200-请求成功、202-服务器端已经收到请求消息,但是尚未进行处理
    301-永久移动、302-临时移动、304-所请求的资源未修改、
    400-客户端请求的语法错误、404-请求的资源不存在
    500-服务器内部错误。

    一般 1XX 代表服务器接收到请求、2XX 代表成功、3XX 代表重定向、4XX 代表客户端错误、5XX 代表服务器端错误。

    更多关于状态码的可以查看:

    《HTTP 状态码》

    首部行

    首部可以分为四种首部,请求首部、响应首部、通用首部和实体首部
    
    请求首部
        Accept:可接受媒体资源的类型
        Accept-charset:可接收的字符集
        Host:请求的主机名
        user-agent
        cookie Secure   仅在 HTTPS 安全通信时才会发送 Cookie
    
    响应首部
        Etag:资源的匹配信息
        Location:客户端重定向的URI
        set-cookie
    通用首部
        Cache-Control:控制缓存策略
        Connection:管理持久连接
        Date:创建报文的时间
    实体首部
        Allow:资源可支持的HTTP方法
        Content-Length:实体主体的大小
        Expires:实体主体的过期时间
        Last-Modified:资源最后修改时间
    

    HTTP1.1协议的缺点

    默认采用持久连接,多个请求可以复用一个TCP,但是在TCP里面,数据请求的通信次序是固定的,服务器只有在处理完一个请求的响应之后,才会进行下一个请求的处理,如果前面的响应特别慢的话,就会造成多请求排队等待的情况,这种情况被称为队头堵塞,解决方法是
        1.减少请求数(雪碧图)
        2.同时打开多个持久连接
    

    HTTP2协议

    在HTTP2中仍然复用TCP连接,但是在一个连接里,客户端和服务端可以同时发生多个请求或者回应,不用按照顺序一一发送,这样就避免了对头堵塞。
    
    数据流
        由于HTTP的请求不是按照顺序发送的,同一个连接里面连续的数据包可能属于不同的请求,因此必须对数据包做标记,指明它属于哪个请求,HTTP2将每个请求或回应的数据包称为一个数据流。每个数据流都有一个独一无二的编号,数据包发送的时候,都必须标记数据流的ID,用来区分它属于哪个数据流
    
    头信息压缩
        HTTP2实现了头信息的压缩,由于HTTP 1.1不带有状态,每次请求都必须附上所有的信息,请求的很多东西都是重复的比如cookie和user agent,每次请求都会附带,浪费了很多的带宽,也影响速度。
        http2对这一点做了优化,引入了头信息压缩机制,使用gzip压缩之后再发送,另一方面客户端和服务端同时维护一张头信息表,所有的字段都存入这个表,生成一个索引号,以后就不发送同样的字段了。只发送索引号,提高速度
    
    服务器请求
        http2允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送,使用服务器推送,提前给客户端推送必要的资源,这样可以相对减少一些延迟的时间,发送的都是静态资源。
    

    HTTP2协议的缺点

    由于HTTP2使用过了多路复用,多个数据流使用同一个TCP连接,遵守流量状态控制和拥塞控制,只要一个数据流遭遇拥塞,剩下的数据就没法发出去,这样就导致了后面的所有数据都被阻塞。这是TCP协议的问题,不是HTTP2的问题
    

    HTTP3协议

    由于 TCP 本身存在的一些限制,Google 就开发了一个基于 UDP 协议的 QUIC 协议,并且使用在了 HTTP/3 上。 QUIC
    协议在 UDP 协议上实现了多路复用、有序交付、重传等等功能
    
    

    HTTPS协议

    HTTP协议使用明文方式发送,可能被第三方窃听
    HTTP报文可能被第三方截取后修改通信内容,接收方没办法发现报文修改的内容
    HTTP害存在认证问题,第三方可以冒充他人进行通信
    
    
    HTTPS基于HTPP协议不过它加了TLS/SSL来对数据进行加密,第三方没有办法窃听,并且它提供了一种校验机制,信息一旦被篡改,通信的双方会立刻发现,它害配备了身份证书,防止身份被冒充
    

    什么是正向代理和反向代理?

    我们常说的代理也就是指正向代理,正向代理的过程,它隐藏了真实的请求客户端,服务端不知道真实的客户端是谁,客户端请求的服务都被代理服务器代替来请求。
    
    反向代理隐藏了真实的服务端,当我们请求一个网站的时候,背后可能有成千上万台服务器为我们服务,但具体是哪一台,我们不知道,也不需要知道,我们只需要知道反向代理服务器是谁就好了,反向代理服务器会帮我们把请求转发到真实的服务器那里去。反向代理器一般用来实现负载平衡。
    

    怎么实现多个网站之间共享登录状态

    在多个网站之间共享登录状态指的就是单点登录。多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
    
    我认为单点登录可以这样来实现,首先将用户信息的验证中心独立出来,作为一个单独的认证中心,该认证中心的作用是判断客户端发
    送的账号密码的正确性,然后向客户端返回对应的用户信息,并且返回一个由服务器端秘钥加密的登录信息的 token 给客户端,该
    token 具有一定的有效时限。当一个应用系统跳转到另一个应用系统时,通过 url 参数的方式来传递 token,然后转移到的应用站
    点发送给认证中心,认证中心对 token 进行解密后验证,如果用户信息没有失效,则向客户端返回对应的用户信息,如果失效了则将
    页面重定向会单点登录页面。
    

    你可能感兴趣的:(面试题)