前端面试题总结

q1、js有哪些数据类型

八种:number string boolean null undefined object symbol bigInt

其中Object引用数据类型有:array date function

2、什么是变量提升?

(1)分变量提升和函数提升

(2)变量提升只能是提升到当前作用域的顶部,不会超出作用域

(3)函数声明会提升,但是函数表达式不会提升

(4)具名函数表达式同样不会提升

3、说说闭包

(1)闭包的作用域链包括:自己的,包含他的函数的,以及全局的

(2)外层函数每次执行就会创建一个新的内存地址,不同执行获得的闭包各不影响

(3)其实就是函数不在当前词法作用域内执行,而是在别的词法作用域执行,会把当前的活动对象添加到自己的作用域链上

4、==和===的区别

(1)===值和类型都必须一样,例外 NaN === NaN ---false

(2)==会进行一些类型转换

        null == undefined ---true

        字符串和数值比较,字符串转数字

        布尔值转 0 1 再比较

        对象转基础类型,按照tostring

5、说一说this

(1)默认绑定:独立函数调用,默认绑定到window

(2)隐式绑定:作为对象的方法调用,this是此对象,但是注意隐式丢失,函数别名

(3)new绑定:构造函数里的this,指向实例

(4)可以通过 call aplly bind做一个显式绑定

(5)普通函数是运行时绑定,箭头函数是定义时绑定

6、数组、对象的遍历方法有那些?

(1)数组:for循环、for of、forEach、map(最后这俩区别是:map可以直接return出想要的数据,但是注意会形成一个新数组不会改变原数组,forEach不能return所以没法直接在遍历的时候操作原数据,但是!回调里面可以改,回调函数可以传入(item, index, arr),forEach返回值Undefined)

(2)对象:Obejct.keys Object.values  for-in

7、箭头函数和普通函数的区别?

(1)箭头函数不能作为构造函数使用

(2)箭头函数中的this是父级函数的上下文

(3)箭头函数没有arguments对象,可用...args 获取到(args就是参数数组)

(4)箭头函数不能通过call apply bind绑定,会忽略第一个参数,但是第二个参数序列可以传进去

(5)没有原型属性

(6)不能当作Generator函数,不能使用yield关键字

8、如何解决跨域?

(1)jsonp,利用script中的src标签告诉服务器前端回调函数的名称, 服务器通过拿到回调函数名,让改函数以执行的状态(eg: fn( data ))返回,并且把数据放到参数里面

 

(2)document.domain + iframe(这个必须是主域名是一样的)

父窗口中利用frame标签嵌入一个子窗口,然后父窗口和子窗口都强制设置基础主域名 document.domain = "fordmc.com",这样子集就能通过window.parent拿到父窗口的数据了

(3)location.hash + iframe

通过改变 iframe标签的src里面的hash值,然后在子窗口里面监听onhashchange事件,可以取到location.hash值

 

(4)window.name + iframe

利用window.name这个属性,只要设置了,无论url怎么变,这个值都不会变实现跨域

 

(5)window.postMessage

 

 (6)CORS:access-control-allow-origin,要携带cookie还要加 access-control-allow-credentials

(7)配置proxy代理

 (8)websocket:本质上没有使用Http响应头,因此也没有跨域的限制

前端:

后端:

 

 (9)Nigx反向代理:后端配置

9、ES6新增语法

对象简写、数组对象的解构赋值、let const(块级作用域、暂时性死区、const常量只体现一层如果是引用数据类型的话)、模板字符串、...rest、...展开语法、symbol、bigInt、还有一些数组的API,promise, async await,class关键字

10、attribute property区别

attribute是标签属性,property是dom属性通过某个dom的点运算取到,attribute通过getAttribute和setAttribute 这种API去设置,但是dom有原生的attribute,自定义的没有

11、内存泄漏

常见的内存泄漏原因:意外的全局变量、没有清空定时器和回调函数、闭包、脱离引用的dom

12、script标签的 async defer有什么作用及区别?

(1)相同点:这俩都是为了不阻塞后面的资源的加载、都会异步加载执行

(2)不同点:

        async是加载完成就立即执行,因此谁先加载完就执行谁没有顺序,会在window的load事件之前执行

        defer按照脚本顺序执行,会在document.DOMContentLoaded事件之前执行

13、数组常用API

        concat, copywithin, entries, every, fill, filter, find, findIndex, flat, flatMap, forEach, from, includes, indexof, isArray, join, keys, map, of, pop, push, shift, unshift, reduce, reverse, slice, splice, sort, some

14、怎样实现深拷贝?

(1)有哪些方法是深拷贝:es6的...、Json.parse(Json.stringfy(待拷贝对象))、数组中的slice, concat

(2)手写深拷贝

function deepCopy(obj) {
  if(typeof obj == 'object'){
    let result = Array.isArray(obj) ? []:{}
    for(let key in obj){
      result[key] = typeof obj[key] == 'object' ? deepCopy(obj[key]):obj[key]
    }
    return result
  }else{
    return obj
  }
}

let arr = [1,2,3]
let ans = deepCopy(arr)
ans.push(4)
console.log(ans, 'ans'); // [ 1, 2, 3, 4 ] ans
console.log(arr, 'arr'); // [ 1, 2, 3 ] arr

15、异步编程都可以通过哪些方法实现?

回调:回调地狱

promise: then支持链式调用,如果返回promise那then函数返回的promise的装态完全由promise决定,如果返回的不是Promise都会包装成一个成功的promise返回,如果没有返回值,也会包装成一个成功的promise只是成功的值是undefined

generator(返回一个迭代器,通过调用next方法去一步一步执行,传入next的参数,可以给到生成器函数里面的yield,同样类似于return,写到 yield后面的值,也会返回给到next()函数,里面的value, next()函数的返回值是这个eg: { done: false, value: undefined })

async await: async包装的函数返回一个promise, 遇到await就停止执行函数,等await 后面的promise resolve后直接拿到值,本质上是一个语法糖,然后async await是不能捕获异常的,只能通过try catch来捕获,其实就是把 * 替换成了 async,把yeild替换成了await

16、前端性能优化

减少http请求次数,DNS查询次数、避免重定向、懒加载、减少操作dom、css避免使用计算属性、使用CDN、使用expires cache control 配置Etags、合并压缩代码、使用精灵图、开启Gzip acceptEncoding contentEncoding、配置webpack减少单独的模块解释,一些loader(babel排除node modules)的使用排除某些不需要的文件夹、缓存Lodaer的结果、多线程打包、热替换、动态加载组件。

18、说下作用域链

函数作用域、全局作用域、变量查找会先在当前作用域找、找不到就往上找直到window

Let const具有块级作用域 也就是说不是整个函数内声明都能找到 严格按照顺序不会有变量提升

eval能够改变词法作用域,传入eval的变量可以在eval执行的位置找到,尽管定义的时候并不是在执行eval位置定义的

with能够传入一个对象,然后作用域就会变成传入的对象,如果操作了传入对象没有的属性,小心内存泄漏

function test(str) {
    eval(str);
    console.log(a + b); //输出3
}
test("var a = 1; var b = 2");

function test (obj) {
    with(obj) {
        a = 2;
    }
}
var o1 = {
    a: 3
}
var o2 = {
    b: 3         
}
test(o1);
console.log(o1.a); //2
test(o2);
console.log(o2.a); //undefined
//接下来我们输出一下全局a的值,我们会发现a泄漏到了全局变量上
console.log(a);//2

19、原型链

实例的隐式原型指向构造函数的显示原型、构造函数的原型有个constructor属性指向构造函数

1、Foo(),Object(),Function()这三个构造函数,是通过构造函数Function()构造而来,所以Foo(),Object(),Function() 是 Function()的实例对象,因此Foo(),Object(),Function()的__proto__属性,都指向Function.prototype

2、f1,O1分别是Foo(),Object()的实例对象,因此f1,O1的__proto__属性分别指向Foo.prototype和Object.prototype

3、所有函数的原型对象都是一个Object空对象,因此Function.prototype实际上也是一个Object实例对象,所以Function.prototype.__ptoro__指向构造Function.prototype这个Object空对象的prototype,即 Object.prototype,同理:Foo.prototype.__ptoro__指向构造Foo.prototype这个Object空对象的prototype,即 Object.prototype.

20、继承的几种方式

(1)类式继承:父类实例对象挂载到子类prototype上

(2)构造函数继承:构造函数里面调用父类的构造函数通过call把当前this传进去

(3)组合继承:结合前两种继承

(4)原型式继承:在父类构造函数传入一个对象,声明过渡函数,设定过渡函数的原型为这个对象,然后生成一个此构造函数的实例,然后返回。

(5)寄生式继承:对原型继承的第二次封装,并且封装过程中进行方法属性的拓展

(6)寄生组合式继承:寄生继承+构造函数继承

21、js垃圾回收机制

(1)标记清除法:“进入环境” "离开环境"进行标记,离开环境的清除即可

(2)引用计数法:变量被引用一次就计数加一,为0代表没被引用,清楚即可

v8内存分两个区域:新生代和老生代,不同的区域采用不同的垃圾回收机制

(1)新生代:这个区域相对较小但是垃圾回收特别频繁。采用scavenge算法进行垃圾回收,

scavenge算法:把内存分两个区域,from, to,首先放入from, 然后回收时,如果from区域有活动对象,就会复制到to区域进行保存,然后清空from区域,然后 from, to角色切换。

(2)老生代:新生代中的对象在存活一段时间后就会被转移到老生代内存区(转移条件:是否经历过scavenge算法,to空间内存占比是否已经超过25%),此区域垃圾回收频率较低。采用:标记清除 和 标记整理算法进行垃圾回收

标记清除算法:垃圾回收期会在内部构建一个根列表(全局对象,函数局部变量和参数,当前前嵌套调用链上的其他函数的变量和参数),从根节点出发可以访问到的标记为活动的,访问不到的视为垃圾。

标记整理算法:就是遍历的时候把活动对象往堆内存的一端移动。

(引入增量标记去解决垃圾回收的全停顿问题)

参考:一文搞懂V8引擎的垃圾回收_gxhlh的博客-CSDN博客_v8垃圾回收

23、为什么0.1+0.2>0.3

JS中的number类型是采用双精度浮点数规范,采用64位存储,1位符号位,11位指数偏移值,剩下52位分数值,0.1在转换成二进制的时候会形成无限循环数,但是只能保留52位,因此存在一个四舍五入,因此导致大于0.3,解决方案就是Number.EPSILON(一个无线小的数)

24、判断数据类型的几种方法

(1)typeof: 输出的是数据类型的字符串,基本数据类型能分辨出来,typeof function 是 ‘function’,然后引用数据类型是 object,typeof null也是object(原因是最开始使用的是低位存储变量类型信息,000开头代表对象,null全是0所以判断为object) typeof undefined 是undefined

(2)instanceof: 主要用于判断引用数据类型是谁那种构造函数的实例来判断类型,会沿着原型链查找,如果一直沿着原型链都查找不到那就返回 false

实现以下instanceof

function myInstanceOf (L, R) {
    let LP = L.__proto__
    let RP = R.prototype
    while (true) {
        if (LP === null) {
            return false
        }
        if (LP === RP) {
            return true
        }
        LP = LP.__proto__
    }
}

let ans = myInstanceOf({ a: 1 }, Array) // false
let ans2 = myInstanceOf([1, 2], Array) //true
console.log(ans);
console.log(ans2);

(3)constructor: 不稳定 重写prototype会丢失,null undefined无效

(4)Object.prototype.toString这个能最准确判断出是什么类型

25、手写call apply bind

call

Function.prototype.myCall = function(context, ...args){
  if(context === undefined || context === null){
    context = window
  }else{
    context = Object(context) // 如果传入的为原始值,要把上下文包装成对象
  }
  const fn = Symbol('fn') // 只所以使用symbol 是无法确定传入的context会不会出现同名属性情况
  context[fn] = this
  const res = context[fn](...args)
  delete context[fn]
  return res // 返回的是函数执行结果
}

const obj = {a:1}
function test(val){
  console.log(this.a + val)
}

test.myCall(obj, 3)

apply

Function.prototype.myApply = function(context) {
  if(typeof this !== 'function') {
    throw new TypeError('Error')
  }
  context = context || window
  context.fn = this
  let result
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

let obj = {
  c:1
}
function test(a,b){
  console.log(a+b+this.c);
}
test.myApply(obj, [1,2]) // 4

bind

Function.prototype.myBind = function(context) {
  if(typeof this !== 'function') {
    throw new TypeError('Error')
  }
  const _this = this
  args = [...arguments].slice(1)
  return function F() {
    if(this instanceof F){
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

let obj = {
  c:1
}
function test(a,b){
  console.log(a+b+this.c);
}
let foo = test.myBind(obj)
foo([1,2]) // 4

26、字面量创建对象和New创建对象有什么区别?

1.字面量创建对象不会调用Object构造函数,而new是会实现一个原型链的继承

2..new关键字内部做了什么

(1)创建一个空对象

(2)将空对象的隐式原型指向构造函数的显示原型

(3)将this指向实例对象

(4)判断构造函数有没有返回值,如果有就返回,没有就返回之前创建的空对象

3..手写一个new

function myNew(fn, ...args){
  let obj = {}
  obj.__proto = fn.protptype
  let result = fn.apply(obj, args)
  return result instanceof Object ? result : obj
}

function Add(a,b) {
  this.a = a
  this.b = b
}

let instance = myNew(Add, 1,2)
console.log(instance.a); // 1
console.log(instance.b); // 2

4.new和Object.create()创建的对象有什么区别?

new创建的对象是会继承构造函数的属性和方法的,但是Object.create()创建出来的实例自身不会继承属性和方法,只是会把原型继承下来,所以构造函数的方法也能够找到(是通过原型找到的)

27、为什么JS是单线程?

js本身就是一种脚本语言实现浏览器的一些交互操作dom 如果多线程的话就会冲突,但是后来实现了web worker可以实现多线程,但是有些限制,不能操作dom 不能使用 window document对象,不能alert confirm等等,通过postMessage onMessage等与主线程去通信

28、手写一个promise

终于写完了.....

// 手写一个promise

// 面试题版本
class myPromise {
    constructor(excutor) {
        this.state = 'pending'
        this.value = undefined
        this.reason = undefined
        this.onResolveCallbacks = []
        this.onRejectCallbacks = []
        const resolve = (value) => {
            if (this.state == 'pending') {
                this.state = 'fulfilled'
                this.value = value
                this.onResolveCallbacks.forEach(fn => fn())
            }
        }
        const reject = (reason) => {
            if (this.state == 'pending') {
                this.state = 'rejected'
                this.reason = reason
                this.onResolveCallbacks.forEach(fn => fn())
            }
        }
        try {
            excutor(resolve, reject)
        } catch (error) {
            reject(error)
        }
    }
    then (onFulfilled, onRejected) {
        if (this.state == 'fulfilled') {
            onFulfilled(this.value)
        }
        if (this.state == 'rejected') {
            onRejected(this.reason)
        }
        if (this.state == 'pending') {
            this.onResolveCallbacks.push(() => onFulfilled(this.value))
            this.onRejectCallbacks.push(() => onRejected(this.reason))
        }
    }
}

let p = new myPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(200)
    }, 2000)
})

p.then(res => {
    console.log(res, '1');
})

p.then(res => {
    console.log(res, '2');
})


// 详细版
class mypromise {
    constructor(executor) {
        // 存状态
        this.state = 'pending'
        // 存成功的值
        this.value = undefined
        // 存失败的值
        this.reason = undefined

        // 成功和失败的事件队列
        this.onFulfilledCallback = []
        this.onRejectedCallback = []

        // resolve逻辑
        const resolve = (value) => {
            if (this.state === 'pending') {
                this.state = 'fulfilled'
                this.value = value
                // 等真正异步结束调resolve的时候,在依次执行成功回调队列
                this.onFulfilledCallback.forEach(fn => fn())
            }
        }
        // reject逻辑
        const reject = (reason) => {
            if (this.state === 'pending') {
                this.state = 'rejected'
                this.reason = reason
                // 等真正异步结束调resolve的时候,在依次执行失败回调队列
                this.onRejectedCallback.forEach(fn => fn())
            }
        }

        // 立即执行传入promise的函数:因此promise参数函数的函数体内语句是同步的
        try {
            executor(resolve, reject)
        } catch (error) {
            reject(error)
        }
    }

    then (onFulfilled, onRejected) {
        // 实现链式调用,因此要返回一个promise
        let promise2 = new mypromise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                // 异步是因为,如果同步的话,resolvePromise传入的promise2还拿不到
                setTimeout(() => {
                    try {
                        // 为了下一次的then调用能够拿到上一次then函数里面的返回值
                        let x = onFulfilled(this.value)
                        // 为了实现then函数里面返回promise的情况
                        resolverPomise(x, resolve, reject, promise2)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            }
            if (this.state === 'rejected') {
                setTimeout(() => {
                    try {
                        // 为了下一次的then调用能够拿到上一次then函数里面的返回值
                        let x = onRejected(this.reason)
                        // 为了实现then函数里面返回promise的情况
                        resolverPomise(x, resolve, reject, promise2)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            }
            // 处理异步得时候,就把要then里面要执行得函数暂存到队列
            // 直到调用了 resolve或者reject再去执行then里面的函数
            if (this.state === 'pending') {
                this.onFulfilledCallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 为了下一次的then调用能够拿到上一次then函数里面的返回值
                            let x = onFulfilled(this.value)
                            // 为了实现then函数里面返回promise的情况
                            resolverPomise(x, resolve, reject, promise2)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
                this.onRejectedCallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 为了下一次的then调用能够拿到上一次then函数里面的返回值
                            let x = onRejected(this.reason)
                            // 为了实现then函数里面返回promise的情况
                            resolverPomise(x, resolve, reject, promise2)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
            }
        })
        return promise2
    }
}

function resolverPomise (x, resolve, reject, promise2) {
    // 为了捕获return自己的错误情况
    if (x === promise2) {
        console.error('循环引用')
    }
    if (x instanceof mypromise) {
        x.then((value) => {
            resolve(value)
        }, err => {
            reject(err)
        })
    } else {
        resolve(x)
    }
}

// 四个API
mypromise.resolve = function (val) {
    return new mypromise((resolve, reject) => {
        resolve(val)
    })
}
mypromise.reject = function (reason) {
    return new mypromise((resolve, reject) => {
        reject(reason)
    })
}
mypromise.race = function (promises) {
    return new mypromise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(resolve, reject)
        }
    })
}
mypromise.all = function (promises) {
    let arr = []
    let count = 0
    function processData (index, data, resolve) {
        arr[index] = data
        count++
        if (count === promises.length) {
            resolve(arr)
        }
    }
    return new mypromise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(res => {
                processData(i, res, resolve)
            }, reject)
        }
    })
}

// 测试代码
let p1 = new mypromise((resolve, reject) => {
    setTimeout(() => {
        resolve(100)
    }, 1000)
})

let p2 = new mypromise((resolve, reject) => {
    setTimeout(() => {
        resolve(200)
    }, 2000)
})
let p3 = new mypromise((resolve, reject) => {
    setTimeout(() => {
        resolve(300)
    }, 3000)
})

mypromise.all([p1, p2, p3]).then((res) => {
    console.log(res)
})

29、宏任务、微任务执行顺序的一些例题

// // 第一题
console.log(1)
setTimeout(()=>{
  console.log(2)
},0)
Promise.resolve().then(()=>{
console.log(3)
})
console.log(4)
// // 执行顺序:1 4 3 2

// // 第二题
console.log(1);
setTimeout(() => {
  console.log(2);
}, 0);
 
new Promise((resolve, reject) => {
  console.log(3);
  resolve()
}).then(()=>{
  console.log(4);
});
console.log(5);
// // 执行顺序:1 3 5 4 2

// // 第三题
console.log(1)
setTimeout(function(){
  console.log(2);
  let promise = new Promise(function(resolve, reject) {
      console.log(3);
      resolve()
  }).then(function(){
    console.log(4)
  });
},1000);
setTimeout(function(){
  console.log(5);
  let promise = new Promise(function(resolve, reject) {
      console.log(6);
      resolve()
  }).then(function(){
    console.log(7)
  });
},0);
let promise = new Promise(function(resolve, reject) {
    console.log(8);
    resolve()
}).then(function(){
  console.log(9)
}).then(function(){
  console.log(10)
});
console.log(11)
// // 执行顺序:1 8 11 9 10 5 6 7 2 3 4

// 第四题
console.log('1');
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
// 执行顺序: 1 7 6 8 2 4 3 5 9 11 10 12

30、vue中webpack devserver 代理如何配置?

proxy里面配置,target,ws,changeOrigin,pathRewrite等

 devServer: {
     proxy: {
         '/api': {
             //要访问的跨域的域名
             target: 'http://www.test.com',
             ws: true, // 是否启用websocket
             changOrigin: true, // 开启代理
             pathRewrite: {
                 '^/api': ''
             }
         }
     }
 }

注意:pathRewirite如果不写,就只是修改代理域名,如果写会修改代理的域名及后面的路径

31、关于模块化commonjs ES6有什么区别?

commonjs:

以下代码文件名:a.js

// commonjs模块化
function funA () {
    console.log('a');
}
function funB () {
    console.log('b');
}

// 导出
// 分别导出
exports.funA = funA
exports.funB = funB
// 统一导出
module.exports = { funA, funB }

 以下代码文件名:b.js

// 分别导入
const funcs = require('./a.js')
funcs.funA() // a
funcs.funB() // b

// 统一导入
const { funA, funB } = require('./a.js')
funA() //a
funB() //b

ES6:

以下代码文件名:a.mjs

// ES6模块化
//分别导出
export function funA () {
    console.log('a');
}
export function funB () {
    console.log('b');
}

// 统一导出
export { funA, funB }

// 默认导出
export default { name: 'hui' }

以下代码文件名:b.mjs

// 分别导入
import { funA, funB } from "./a.mjs";
funA()
funB()

// 统一导入
import * as funcs from './a.mjs'
funcs.funA()
funcs.funB()

// 默认导入
import name from './a.mjs'
console.log(name);

总结:

(1)es6模块化就是通过export关键字进行分别导出,通过 export { 变量标识符1,变量标识符2 }进行统一导出,通过 export defalt { key: value }进行默认导出(默认导出可匿名)。但是请注意变量标志符这几个字,也就说统一导出的并不是实际变量,export { key: value }这种写法错误!!里面只能写变量标识符。但是默认导入的是一个真实的对象,里面是可以写key:value的。然后对于导入的话,就用import关键字进行导入就好,有两点,一是可以起别名,二是统一导入和默认导入可以一起。

(2)commonjs模块化就是给exports对象添加属性来导出,分别导出就给exports添加不同的属性,统一导出就给 module.exports = {  } 这个对象里面统一添加key:value。导入的话 就用require引入一下就好。注意一下exports 和 module.exports,其实都是指向同一块引用,所以别随便给 module.exports重新赋值一个对象,会导致导入的对象丢失。

(3)区别:commonjs是加载后立即执行,es6是静态加载,即运行时加载,node默认支持commonjs,如果node里想用es6,文件后缀名得是 .mjs。然后es6还支持再导出!export { a } from ''./a.js'这种,还支持动态导入,返回一个promise 即 import('a.js').then(a => { let b = a.func1 })这种,好高级!终于写完了这一条....

32、TCP三次握手,为啥不是四次,两次?

图片转载于:

三次握手:

四次挥手:

 (1)为什么是三次而不是两次:

        因为第一次挥手:客户端发送SYN给服务器,服务器可以确定客户端发送消息和自己接收消息的能力

        第二次挥手:服务器发SYN+ACK给客户端,客户端可以确认自己接收消息的能力和服务器发送消息的能力

        至此,服务器并不能确认自己发送消息的能力是否正常,因此服务器需要第三次挥手来确认自己发出去的消息被正常接收。

(2)为什么不是四次呢?

没有必要,因为再多一次也并不能使链接更可靠,会造成资源浪费

(3)为什么握手是三次,而挥手是四次呢?

因为服务器接收到FIN报之后,很可能不会立即关闭socket,因此只需要一个ACK告知客户端我明白你要关闭的需求了,然后需要等到服务器端把还没发送完的数据发送完之后,再次发送FIN报,去主动断开连接,因此是四次。

33、HTTP的结构,有哪些头部字段,以及各种状态的含义

(1)结构:报文首部+空行+报文主体,其中,报文首部=请求行(或者状态行)+各种首部字段,其中请求行包含:请求方法,请求URI,http版本,状态行包含:http版本,状态码,原因短语

(2)各种首部字段:

        a.通用首部字段:

                cache-control: public,no-cache(请求:不要缓存的,响应:可缓存要向我确认),no-     store,s-maxage(只适用公共缓存服务器),max-age,min-fresh(超过这个有效时间的就不返回了),max-stale(过期了但没超过这个时间,仍然接收),only-if-cached(是缓存的才返回),must-revalidate(会忽略max-stale),no-transform(防止缓存或代理压缩图片等行为)

                connection:Upgrade(控制upgrade这个字段不再转发给代理),keep-alive(管理持久连接)

                date:时间

                trailer:expires(说明报文主体后记录了哪些首部字段,用于分块传输编码)

                upgrade:可以升级协议版本,但仅限于邻接服务器,因此要和connection;uograde一起使用

                via:记录传输路径,避免请求回环

        b.请求首部字段:

                accept: 告知媒体类型及优先级

                authorization:用于携带认证信息,一般用于响应401返回码

                from:电子邮件

                host:

                if-match,if-none-match: 匹配资源是否发生变化,看Etag值,无法使用弱Etag值

                if-modified-since(if-unmodified-since):如果资源发生改变会响应304

                if-range:次字段值如果跟Etag值或者更新资源时间一致,则匹配上了,响应206(partial content),如果没匹配上就返回所有

                max-forwards:每次转发,数值减一

                range:

                referer:原始资源URI

        c.响应首部字段

                accept-range,age(这个缓存多久之前向服务器确认过),Etag(强Etag,弱Etag,弱的意思是只有发生根本改变才会变)

                Location:配合3xx:redirection的响应,指定重定向的URI

                vary:accept-language(只会持有相同自然语言的返回缓存,即对缓存做一个控制

        d.实体首部字段

                content-encoding.length....

                expires:过期时间

                last-modified

        e.为cookie服务的首部字段

                cookie:

                setcookie:expires,path,domain,secure(仅在https才会发送cookie),httponly(加以限制,cookie不能被脚本访问)

(3)状态码

 

33、网络七层协议

34、http1.0 http1.1 http2的比较

http1.1优化内容

        (1)长连接:默认开启connection:kepp-alive,一个tcp连接可以并行多个http请求

        (2)缓存优化:http1.0主要使用if-modified-since expires来控制缓存,1.1新增了Etag,if-match,if-none-match,if-unmodified-since等来优化缓存策略

        (3)新增24个错误状态响应码:比如406(not acceptable),407(需要经过代理服务器授权),409(confict 状态冲突)

        (4)新增请求方式:put delete trace options connect

        (5)host优化:一个IP对应多个host虚拟主机

        (6)增加range:充分利用带宽和连接

http2优化内容:

        (1)二进制分帧:将传输信息分割为更小的消息和帧,采用二进制编码格式进行封装,兼容1.1 http1.1中的首部信息封装到headers帧中,request body封装到data帧中

        (2)多路复用:允许单一http连接发起多重的请求-响应,由于二进制分帧机制,每个数据流都拆分成互不依赖的帧,并且可以乱序发送再重组(基于帧首部的流标识符)

        (3)头部压缩:双方各缓存头部字段表,重复的头部字段只需要发送一次,如有变化再将变化的部分加入到header帧中即可,hpack头部压缩算法效果比之前的gzip好

        (4)请求优先级:每个流可以带一个31比特的优先值,高优的帧先发送,但不绝对,以免阻塞

        (5)服务端推送:可以把客户端需要的资源伴随index.html一起发送,所以静态资源通过服务器推送可极大提升速度

35、http https比较(还未完成、不是很懂)

(1)http缺点:明文通信,没有身份验证,没有完整性验证,因此https实际上就是:http+通信加密+证书+完整性保护,这些是基于SSL实现的

(2)SSL:SSL协议分两层,上层是:握手协议+密码变化协议+警告协议,下层是:记录协议(为高层协议提供数据封装压缩加密等基本功能)

SSL握手协议几个阶段:

        第一阶段:clientHello(客户端支持的协议版本、客户端随机数、sessionID、加密套件、压缩方法),serverHello(服务器支持的协议版本、服务器随机数、加密套件(选客户端提供的)、sessionId、压缩算法(首选客户端提供的))。至此,两边都有两份随机数,以及确定了加密算法密钥交换和信息验证。

        第二阶段:服务器单向发消息:数字证书和根CA链、服务器密钥交换、证书请求、服务器握手完成

        第三阶段:客户端单向发送消息:证书、客户机密钥交换(发送预备主密钥(公钥加密的))、证书验证

        第四阶段:.....

(3)https加密原理(未完成,不是很懂...)

36、浏览器存储

cookie(4k、字段有:key-value、path domain httpOnly secure expires ) localStorage(5M、持久化) sessionStorage(5M、仅限于当前窗口关了就没了) indexDb (对象方式存储)api:setItem getItem removeItem

37、get post区别

(1)get请求参数体现在url身上、而且只能用url编码,所以需要转码解析,post不会体现在url

(2)由于url大小的限制,get传输的数据量有限

(3)get产生一个TCP数据包,把header和data一起发送出去,而post产生两个数据包,第一次响应100 continue 第二次才响应200 ok

(4)get回退无害、post会再次提交请求

(5)get浏览器会主动cache 而Post不会除非手动设置

w3c标准答案:

38、对比TCP,UDP(未完成)

描述TCP建立连接的三次握手过程,如果最后一次握手失败会怎样处理? - 知乎

TCP原理,滑动窗口,拥塞窗口说清楚

39、从浏览器输入URL都发生了什么?

按照这个顺序讲,具体内容看本文其他地方

40、讲讲CDN(内容分发网)

(1)组成:

        中心节点(智能调度服务器):根据传输距离、边缘节点负载情况分配最合适的边缘节点,做好边缘节点的负载均衡

        边缘节点(代理服务器):对源服务器的缓存

(2)DNS和CDN的关系:

        DNS有两种记录类型:域名:IP映射记录(A类型) 和 域名:域名映射记录(CNAME)

        CNAME类型要访问域名应该访问哪台合适的服务器呢?就需要CDN来调度

(3)CDN工作过程

 

41、XSS CSRF攻击,OWASP Top10(未完成)

42、回流、重绘

回流:删除增加dom元素,元素位置发生改变、元素尺寸发生改变、页面初始渲染、浏览器窗口大小改变

重绘:仅仅是一些样式的改变,不会影响页面布局

如何减小回流重绘:可以先将元素脱离文档流、然后应用多重改变、再带回文档流。隐藏元素、应用修改、重新显示。使用文档片段在当前DOM之外构建一个子树,再把它拷贝回文档。将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换原始元素

43、事件冒泡,事件捕获,事件委托

冒泡:目标元素先触发 addEventListener() 第三个参数为false

捕获:父元素先触发 addEventListener() 第三个参数为true

事件委托:利用冒泡,目标元素的事件发生也会冒泡到父元素,通过e.target判断来写逻辑就行

44、手写防抖节流

function debounce (fn, wait) {
    let timer = null
    return function () {
        const context = this
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(context, argumens)
        }, wait)
    }
}

function throttle(fn, delay) {
    let prev = Date.now();
    return function() {
      const context = this;
      const curr = Date.now()
      if (curr - prev >= delay) {
        prev = curr;
        return fn.apply(context, arguments);
      }
    }
  }
  

45、函数柯里化原理(未完成)

46、requestAnimationFrame

类似于setTimeout,但是不用设置事件间隔,他的时间间隔紧跟浏览器刷新频率,把每一帧中所有dom操作集中起来在一次回流或重绘中完成,这样的话就不会丢帧,并且在隐藏或不可见的元素中requestAnimationFrame不会进行回流重绘,然后返回值是一个id,用于cancelAnimationFrame停止动画。

47、JS常见设计模式(未完成)

48、Vue双向绑定原理

有一个碎片化文档的数据劫持,在劫持下来的每个元素节点分别去compile处理,处理的时候就遇到v-model这种属性就把此元素的value值进行了一个绑定(数据来源是vm中的data),以上是简单的内容绑定,1、从view-model,vm中有个观察者类,具体就是用 Object.defineProperty写get set,起到监听的作用,然后在做碎片化处理的时候,是要即时取到最新的数据的,所以有事件回调来完成,把最新的值放到vm data里面去,2、model-view,(就是如何在改data中的数据时候,多个用到此数据的页面一起更新):订阅/发布模式(有个发布者,就是指定调啥函数名,这个函数名就是绑定在订阅者原型上的,所以一发布,订阅者就能执行响应的回调),然后具体怎么实现的:首先是需要给每个属性生成一个对象容器,然后再观察某个属性的时候的get里面将对应的一个watcher(这个watcher就绑定了是哪个节点,然后有update方法和get方法) push到这个对象容器里面去,这样就让这个容器里面装入了同一个属性用到的不同的节点且有update方法,然后再观察的时候触发set,就马上调用发布者的notify功能,触发dep里面每个watcher的update方法。

理解VUE2双向数据绑定原理和实现 - 简书

49、MVVM(感觉没啥好说的啊)

model-view-viewModel

50、Vue声明周期(父子生命周期)

父beforeCreate > 父created > 父beforeMount > 子beforeCreate > 子created > 子beforemount > 子mounted > 父mounted > 父beforeUpdate > 子beforeUpdate > 子updated > 父updated > 父beforeDestory > 子beforeDestory > 子destoryed > 父destoryed

51、Vue中nextTick

怎么用:当改变数据后,需要基于更新后的dom进行某些操作时,就写在nextTick回调那种

我使用过的场景:

(1)在做todo-list组件的时候,在点击编辑时候,需要把代办事项那一条展示为input框去编辑并且聚焦,这个Input框是根据v-show决定的,点击按钮的回调函数里刚把input的显示置为true但是,此时需要拿到更新后的input框才能做一个聚焦操作,因此用到了nextTick。

nextTick源码分析:

(1)首先nextTick是可以传两个参数: 回调函数 和 环境对象

(2)nextTick做了什么:把传入的函数放入callback数组,并且执行timerFunc函数

(3)timerFunc函数: 去判断当前环境是否支持原生Promise,原生MutationObserver,试图把回调放入微任务队列去执行,如果不支持,则检查是否支持setImmediate,不支持就用setTimeout。就是对环境进行一个降级处理,去执行flushCallbacks函数

(4)flushCallbacks函数:就是for循环执行callback队列

const callbacks = []
let pending = false
let timerFunc

export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    timerFunc()
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}


// timerFunc
export let isUsingMicroTask = false
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  //判断1:是否原生支持Promise
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  //判断2:是否原生支持MutationObserver
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  //判断3:是否原生支持setImmediate
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  //判断4:上面都不行,直接用setTimeout
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

function flushCallbacks () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}


52、computed和watch的区别

(1)computed是可以缓存的,只有依赖数据发生改变才会重新计算,watch不支持缓存

(2)computed是需要return拿到结果的,因此不支持异步,它返回实际上是通过调用getter,他也有setter,watch是针对某一个属性监听,然后调用回调,也不需要return什么值,因此支持异步操作。

(3)computed更适合于多对一,即一个数据依赖于多个数据,而watch适合于一对多,即一个数据改变惠影响多个数据。

53、vue-router两种模式(顺便把原生history对象那些API讲一讲区别啥的)

(1)vue-router这两种模式本质上是借助浏览器特性实现的前端路由,即为了实现SPA(单页面应用,跳转页面而不刷新的特性),然后有两种模式hash模式和history模式

(2)hash模式:原理是hashHistory对象上的push和replace方法,会触发onhashChange事件,URL会带上#,在实际发送请求的时候不会带上#后面的字符串,但是可以监听onhashChange事件,当hash值变化的时候进行响应的页面替换

(3)history模式:原理是H5新增的historyAPI,pushState和replaceState,这两个API参数分别是stateObj(这个是文档状态信息对象),title,url,会触发popState事件,来响应页面更新信息,这种模式必须要求服务器与每个url都有隐射,要不会报404,要解决这个问题可以在服务器上添加一个回退路由,当匹配不到任何资源时,提供一个index.html的资源就行了。

54、虚拟dom和Diff算法、vue中key的作用(感觉有点难,先跳过)

55、vue组件间通信方式

(1)props(父->子), $emit(子->父)

(2)ref(子->父)

(3)$bus(兄弟):用一个vue实例,提供了一个共享空间

(4)provide, inject(父->子):非响应式的

(5)$parent, $children(这是个数组且是无序的,访问的数据也不是响应式的)

(6)$attrs(除声明的props之外的属性), $listeners (所有监听器,通过v-on="$listeners"可以实现子组件继承父组件的事件)

56、Vuex中有哪些属性?

state:

getter(state, getters): 可以返回一个函数实现给getter传参,

  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }

store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

 mutation(state, payload): 必须是同步,用$commit触发,这个

action(context): 通过$dispatch来触发mutation中的函数,可以有异步操作,

 这个图有条线没画,就是可以不经过actions直接commit触发mutations里面的操作

57、vueRouter钩子函数

(1)全局钩子

        前置守卫:beforeEach((to, from, next) => {....}),一定要走next,确保走任一逻辑也要next,去执行管道内的下一个钩子,全部钩子执行完了,导航的状态才是confirmed

        解析守卫:beforeResolve((to, from, next) => {....}): 在导航被确认之前,在组件内守卫以及异步组件被解析之后执行。

        后置钩子:afterEach((to, from) => {....}): 没有next!

(2)路由独享守卫:

        

 (3)组件内守卫

 (4)完整的导航解析流程:

 58、webpack常用的几个对象

(1)开发环境配置

/*
    开发环境配置
 */
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            // 处理css资源
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            // 处理less资源
            {
                test: /\.less$/,
                use: ['style-loader', 'css-loader', 'less-loader']
            },
            // 处理图片资源
            {
                test: /\.(png|gif|jpg)$/,
                loader: 'url-loader',
                options: {
                    limit: 8 * 1024,
                    esModule: false,
                    name: '[hash:10].[ext]',
                    outputPath: 'images'
                }
            },
            // 处理html中的图片资源
            {
                test: /\.html$/,
                loader: 'html-loader'
            },
            // 处理其他资源
            {
                exclude: /\.(html|js|css|less|jpg|png|gif)$/,
                loader: 'file-loader',
                options: {
                    name: '[hash:10].[ext]',
                    outputPath: 'media'

                }
            }
        ]
    },
    plugins: [
        // 处理html资源
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    // 搭建开发服务器 devServer
    devServer: {
        compress: true,
        port: 3000,
        open: true
    },
    mode: 'development'
}

(2)生产环境配置

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

// 复用loader
const commonCssLoader = [
    // 把css文件提取出来
    MiniCssExtractPlugin.loader,
    'css-loader',
    // 对css做兼容性处理
    // 注意:还需要在package.json中定义browserslist
    // browserslist默认使用production配置
    // 想要使用development需要定义node环境变量
    // process.env.NODE_ENV = 'development'
    {
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            // 指示postcss以什么插件进行工作
            plugins: () => [
                require('postcss-preset-env')()
            ]
        }
    }
]
// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production'

module.exports = {
    entry: './src/js/index.js',
    output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            // 处理css
            {
                test: /\.css$/,
                use: [
                    ...commonCssLoader
                ]
            },
            // 处理less
            {
                test: /\.less$/,
                use: [
                    ...commonCssLoader,
                    'less-loader'
                ]
            },
            /*
                正常来讲:一个文件只能被一个loader处理。
                当一个文件要被多个loader处理,那么一定要指定loader执行顺序:
                    先执行eslint 再执行babel
             */
            // 处理js
            // js语法检查:需要在package.json中eslintConfig -->airbnb
            {
                test: /\.js$/,
                exclude: /node_modules/,
                // 优先执行
                enforce: 'pre',
                loader: 'eslint-loader',
                // 自动修复
                options: {
                    fix: true
                }
            },
            // js兼容性处理
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    presets: [
                        // 因为默认的babel不能识别一些语法,但是@babel/polyfill又会全部引入,体积太大
                        // 所以按需加载会比较好
                        [
                            '@babel/preset-env',
                            {
                                // 实现按需加载
                                useBuiltIns: 'usage',
                                corejs: { version: 3 },
                                // 具体兼容到哪个版本
                                targets: {
                                    chrome: '60',
                                    firefox: '50'
                                }
                            }
                        ]
                    ]
                }
            },
            // 处理图片
            {
                test: /\.(jpg|png|gif)$/,
                loader: 'url-loader',
                options: {
                    limit: 8 * 1024,
                    name: '[hash:10].[ext]',
                    outputPath: 'imgs',
                    // "html-loader"使用的是commonjs模块化规范
                    // 所以要关闭掉'url-loader'的es6模块化
                    esModule: false
                }
            },
            // 处理html中的图片
            {
                test: /\.html$/,
                loader: 'html-loader',
            },
            // 处理其他文件
            {
                exlude: /\.(js|css|less|html|jpg|png|gif)/,
                loader: 'file-loader',
                options: {
                    outputPath: 'media'
                }
            }

        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            // 压缩html
            minify: {
                // 去除空格
                collapseWhitespace: true,
                // 移除注释
                removeComments: true
            }
        }),
        new MiniCssExtractPlugin({
            filename: 'css/built.css'
        }),
        // css压缩
        new OptimizeCssAssetsWebpackPlugin()
    ],
    // 调成production, js自动压缩
    mode: 'production'
}

59、flex布局

写一些关键词,然后大概讲讲就行:

div {
    /* 生成的框体 */
    display: flex;
    display: inline-flex;
    /* 方向 */
    flex-direction: column;
    flex-direction: column-reverse;
    flex-direction: row;
    flex-direction: row-reverse;
    /* 换行 */
    flex-wrap: nowrap;
    flex-wrap: wrap;
    /* 简写 */
    flex-flow: column wrap;
    /* 主轴排布 */
    justify-content: end;
    justify-content: start;
    justify-content: space-around; /* 这个是两边更小 */
    justify-content: space-between;
    justify-content: space-evenly; /* 这个是均分 */
    /* 垂轴对齐方式 */
    align-items: center;
    align-items: stretch;
    align-items: start;
    align-items: baseline;
    /* 弹性因子简写 */
    flex: 1 2 200px;
    /* 修改显示顺序 */
    order: 1;
}

(1)给父元素设定 display:flex属性,里面的元素就自动有了弹性,如果装不下就压缩 有空隙就拉宽。flex-flow 是 flex-direction flex-wrap的简写

(2)flex: 2 1 100px 最后的值最小宽度 也就是说 盒子的可用内容减去这个100px之后 再拿去分,2代表增长因子 1代表缩减因子

(3)align-items 默认值的strech 会拉伸 center不会改变原来的大小且置中,如果父盒子没高度,子元素谁最高就会把父盒子撑开,那strech属性的效果就会和最高的元素一样高。align-self可以单独在某个元素上设置而覆盖

order属性控制排布顺序

60、grid布局

自己写的一篇博客

CSDNhttps://mp.csdn.net/mp_blog/creation/editor/123227118

61、Px em rem vw vh rpx

 62、结构伪类选择器?

 only-child: 是父类的唯一子元素,

这块可以说一下

 63、doctype标签和meta标签

(1)Doctype文档声明标签,H5不再基于于SGML,不需要对DTD进行引用了,因此 <!DOCTYPE html>就足够

(2)meta原信息标签:有几个属性,http-equiv(可以设定一些值,expires, charset, refresh,content-type), name, scheme,然后这些声明的属性的值放在 content属性里面。

http-equiv这个的作用:让服务器添加响应头,如果值是refresh,可以重定向

64、BFC

块级格式化上下文

BFC特性:

(1)内部的Box会在垂直方向,一个接一个地放置。
(2)Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
(3)每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
(4)BFC的区域不会与float box重叠。
(5)BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。

(6)浮动元素也会参与高度计算

如何触发:

(1)根元素
(2)float属性不为none
(3)position不为static和relative
(4)overflow不为visible
(5)display为inline-block, table-cell, table-caption, flex, inline-flex

作用:

(1)利用不同的BFC外边距不会重叠,解决边距塌陷的问题

(2)利用开启BFC后,浮动元素也参数高度计算,清除浮动

65、清除浮动

(1)额外标签发:增加一个标签,增加clear类,.clear { clear: both }

(2)父级添加overflow: hidden触发BFC

(3)after伪元素法:是给父元素设置为元素

 (4)双伪元素法:

 66、link标签和impot

(1)link标签属于HTML标签,import是css提供的一种方式

(2)link在页面加载时,引入的css会被同时加载,import会在页面加载完了之后再加载,会出现闪烁现象

(3)link支持js操作dom

67、git常用命令

git add, git commit, git push, git branch, git checkout, git pull --rebase, git rebase --continue, git stash, git pop, git log

68、keep-alive

1、此标签包裹动态组件,会缓存不活动的组件实例,而不是销毁他们

2、有三个属性:

        include: 字符串或正则,名称匹配的组件会被缓存

        exclude: 字符串或正则,排除不要被缓存的组件

        max: 数字,最多可以缓存多少实例

3、被keep-alive缓存的组件多了两个钩子函数(created就不会重复调用了):

        activated: 组件激活时调用

        deactivated: 组件停用时调用

4、使用方式:

        1、inclue属性

// include 只缓存组件名字为home的组件,其他组件不会缓存,而exclude恰好相反
<keep-alive include="home">
   <router-view />
</keep-alive>

        2、meta+v-if

{
      path: '/',
      name: 'home',
      meta:{
        keepAlive:true
      },
      component: Home
}

<keep-alive>
      <router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />

69、单页面应用多页面应用的区别

 70、为什么Vue中data必须是一个函数

组件复用的时候,如果是个对象,那个每个组件都会指向同一个data的引用,但是是函数的画,每次都会执行函数返回一个新的引用空间,使每个组件的数据独立开来

71、delete Vue.delete

删除数组的话,delete只是把每个元素改为undefined/empty,Vue.delete会在内存中清空数组

删除对象的话,都一样,清内存

72、SPA首屏加载慢怎么解决

首屏时间:performance.getEntriesByName("first-contentful-paint")[0].startTime

解决方案:

(1)减小入口文件体积:vue异步组件技术(require)、vue-router路由懒加载(import)

(2)利用缓存:cache-control, etag, last-modified

(3)UI框架按需加载

(4)对于图片资源进行压缩

(5)组件重复打包:在webpack的config文件中,修改CommonsChunkPlugin的配置,inChunks为3表示会把使用3次及以上的包抽离出来,放进公共依赖文件,避免了重复加载组件

(6)开启Gzip压缩

plugins: [new CompressionPlugin({
                    test: /\.js$|\.html$|\.css/, //匹配文件名
                    threshold: 10240, //对超过10k的数据进行压缩
                    deleteOriginalAssets: false //是否删除原文件
                })]

(7)服务器端渲染

73、vue页面第一次加载会触发哪几个钩子

beforeCreate, created, beforeMount, mounted

生命周期:

74、vue中的事件修饰符

prevent, stop, once, capture, self, passive

75、vue中$event

在事件回调函数中,$event是事件对象

在自定义事件中,是事件触发时传入的参数

76、vue中过滤器filter

可以全局注册,可以组件内注册

用在{{}}语法中和v-bind中

可以串联使用,前一个的返回值作为后一个的参数传入,另外还可以自己传参数进去

获取不到this!!

77、v-clock

就是可以用这个指令作为一个css属性选择器,然后这上面的样式会在vue实例编译完成后移除,可以用来解决屏幕闪动问题

[v-cloak]{
    display: none;
}

78、vuex分模块命名空间(未完成)

79、vue用到了哪些设计模式?

观察者模式和发布订阅模式

80、vue函数式组件(未完成)

81、H5新增标签

H5新增属性及标签_yanyihan16的博客-CSDN博客_h5新增

82、如何实现响应式布局(还未完成....)

(1)圣杯布局

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        body,
        html {
            height: 100%;
        }

        header {
            height: 200px;
            background-color: yellow;
        }

        footer {
            height: 200px;
            background-color: yellow;
        }

        .con {
            padding: 0 200px;
        }

        .con:after {
            content: '';
            display: block;
            height: 0;
            clear: both;
            visibility: hidden;
        }

        .center {
            float: left;
            height: 200px;
            width: 100%;
            background-color: pink;
        }

        .left {
            float: left;
            width: 200px;
            height: 200px;
            position: relative;
            margin-left: -100%;
            left: -200px;
            background-color: aquamarine;
        }

        .right {
            float: left;
            width: 200px;
            height: 200px;
            position: relative;
            right: -200px;
            margin-left: -200px;
            background-color: aquamarine;
        }
    </style>
</head>

<body>
    <header>header</header>
    <div class="con">
        <div class="center">center</div>
        <div class="left">left</div>
        <div class="right">right</div>
    </div>
    <footer>footer</footer>
</body>

</html>

 (2)双飞翼布局

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        body,
        html {
            height: 100%;
        }

        header {
            height: 200px;
            background-color: yellow;
        }

        footer {
            height: 200px;
            background-color: yellow;
        }

        .con:after {
            content: '';
            display: block;
            height: 0;
            clear: both;
            visibility: hidden;
        }

        .center {
            float: left;
            height: 200px;
            width: 100%;
            background-color: pink;
        }

        .left {
            float: left;
            width: 200px;
            height: 200px;
            margin-left: -100%;
            background-color: aquamarine;
        }

        .right {
            float: left;
            width: 200px;
            height: 200px;
            margin-left: -200px;
            background-color: aquamarine;
        }

        .mc {
            margin-left: 200px;
            margin-right: 200px;
        }
    </style>
</head>

<body>
    <header>header</header>
    <div class="con">
        <div class="center">
            <div class="mc">center</div>
        </div>
        <div class="left">left</div>
        <div class="right">right</div>
    </div>
    <footer>footer</footer>
</body>

</html>

 效果同上

响应式布局的实现方式_Chx.zhang的博客-CSDN博客_如何实现响应式布局

响应式布局的原理和实现方法_jason_stellina的博客-CSDN博客_响应式布局的实现方法和原理

83、css选择器及优先级

!important > 行内样式 > ID选择器 > 类选择器=伪类=属性 > 标签 > 通配符 > 继承 > 浏览器默认属性

84、表单中readonly和disabled有什么区别

 85、preload prefetch

 

 

 86、讲一讲DNS

(1)啥叫:迭代查询,递归查询

查询过程:

 87、懒加载(未完成)

图片懒加载:

        若在src中填入图片地址,浏览器就可以发起请求,加载一张图片。要实现懒加载,可以定义一个虚拟src属性即data-src存放原图地址,而属性src填写 一张低质量的空白占位图。当页面滚动时,遍历图片判断是否在可视区域内,如果在则将data-src中真实地址放到src中,实现按需加载。另外页面滚动是一个触发频繁的事件,可以利用防抖或节流函数优化一下。


作者:贾西贝Xx
链接:https://juejin.cn/post/7116111515660320781
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

88、堆栈区别

 89、函数式编程(设计柯里化啥的,未完成)

90、ajax

//第一步:创建XMLHttpRequest对象
var xmlHttp;
if (window.XMLHttpRequest) {            //非IE
    xmlHttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {       //IE
    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP")
}


//第二步:设置和服务器端交互的相应参数,向路径http://localhost:8080/JsLearning3/getAjax准备发送数据    
var url = "http://localhost:8080/JsLearning3/getAjax";
xmlHttp.open("POST", url, true);

//第三步:注册回调函数
xmlHttp.onreadystatechange = function() {
    if (xmlHttp.readyState == 4) {
        if (xmlHttp.status == 200) {
            var obj = document.getElementById(id);
            obj.innerHTML = xmlHttp.responseText;
        } else {
            alert("AJAX服务器返回错误!");
        }
    }
}

 XMLHttpRequest的属性:

  XMLHttpRequest的方法:

91、callee caller

(1)callee是函数arguments对象的一个属性,指向此arguments所属的函数

 (2)caller保存着当前函数的调用者是谁

 92、数组去重

// 方法一:利用新旧数组对比,把旧数组里新数组没有的放进新数组
function uniqueArr1 (arr) {
    let newArr = [arr[0]]
    arr.forEach(item => {
        if (newArr.indexOf(item) === -1) {
            newArr.push(item)
        }
    });
    return newArr
}

// 方法二:for循环遍历数组,如果出现重复元素,那么遍历的下标i
// 一定不等一indexOf返回的结果,因为indexOf函数只会找最先出现的位置
function uniqueArr2 (arr) {
    for (let i = 0; i < arr.length; i++) {
        if (i !== arr.indexOf(arr[i])) {
            arr.splice(i, 1)
            i--
        }
    }
    return arr
}

// 方法三:利用对象属性值唯一去重
function uniqueArr3 (arr) {
    let obj = {}
    let newArr = []
    arr.forEach((item, index, array) => {
        if (!obj.hasOwnProperty(item)) {
            obj[item] = 1
            newArr.push(item)
        } else {
            obj[item]++
        }
    })
    return newArr
}

// 方法四:利用set(本质上和三是一样的吧)一行去重
const arr = [1, 2, 2, 2, 3, 4, 4, 5]
let res = Array.from(new Set(arr))
console.log(res)

93、css水平居中垂直居中

垂直水平居中的几种实现方式_飞鸿小子的博客-CSDN博客_垂直水平居中

一篇全面搞定垂直水平居中的常见11种方式(超详细)_竹子南的博客-CSDN博客_水平垂直居中的多种方式垂直水平居中的几种实现方式_飞鸿小子的博客-CSDN博客_垂直水平居中一篇全面搞定垂直水平居中的常见11种方式(超详细)_竹子南的博客-CSDN博客_水平垂直居中的多种方式

94、babel编译原理

参考这篇就行:

babel原理_一起来看看 Babel 做了什么_Raxxian的博客-CSDN博客

过程:

 .babelrc主要的两个配置项:plugins是指定应该怎么转换语法,要转换箭头函数就添加箭头函数的插件,presets用于简化plugins, 如果使用@babel/presets-env,就可以转换成目标环境所支持的语法,当然别的语法版本,es2015,es2016等等都可以写

对于语法转换,只是新增的语法形式的转换,对于新增的API和对象,需要@babel/polyfill来处理,可以直接引入,但是就会很庞大,因此最好的方式是配置useBuiltIns,如下:

 

 95、H5C3新特性

 

 96、如何实现自适应(这道题好好研究一下总结写一篇博文也是自己薄弱的地方)

左侧宽度固定,右侧自适应

97、rgba和opacity有什么区别

opacity子元素会继承,rgba不会

98、如何垂直居中一个浮动元素

定位用absolute浮动会失效,所以用relative

positon: relative; top: 50%

99、CSS哪些属性可以继承

CSS有哪些属性可以继承?_IT修真院的博客-CSDN博客_css可以继承的属性有哪些

100、常用的bom dom对象

这个参考pad上之前总结的笔记

101、js创建函数的几种方式

构造函数、声明式、函数表达式..

102、SEO优化

(1)合理使用meta标签,设置title discription keyword

(2)HTML语义化做好

(3)非装饰性图片加alt

(4)SSR服务器端渲染

(5)预渲染prerender-spa-plugin

103、浏览器渲染

浏览器渲染原理及流程_南一师兄的博客-CSDN博客_浏览器渲染原理及流程

浏览器渲染原理_夏安   的博客-CSDN博客_浏览器渲染原理

 

104、map set有啥去区别

set类似于数组,不能重复

map类似于对象,但是键可以不止是字符串这种数据类型

105、webpack plugins loader的区别

面试官:说说Webpack中Loader和Plugin的区别?编写Loader,Plugin的思路?_动感超人,的博客-CSDN博客

loader是运行在打包之前,plugins是运行在整个打包过程,webpack打包的时候会广播出许多事件,plugins可以监听,然后利用webpack提供的API改变输出结果 

106、string类型的一些API

charAt, charCodeAt, concat, endswith, includes, indexOf, match, padEnd, trimEnd, replace, repeat, split, splice,

107、Vue3新特性

(1)组合式api,setUp函数,里面去写一组功能,这样比较集中

(2)使用proxy替代object.defineProperty

(3)支持树摇,每个功能就分别引入,创建也不是直接new Vue,而是createApp,这样webpack打包不会把没有用到的东西打包进bundle

(4)生命周期:在生命周期前面加on来访问生命周期,在setUp中使用

(5)reactive返回响应式对象,ref返回带value属性的ref对象,获取值要.value, toRef函数,作用是保留源对象的响应式关联,结构之后保证还是响应式的

(6)还有watchEffect

108、css布局面试题(未完成)

109、line-height如何继承

(1)写具体像素比如20px就直接继承20px

(2)写纯数字就,子元素font-size * 这个数子

(3)写百分比就, 父元素font-size * 百分比 再继承

110、手写reduce

Array.prototype.myReduce = function (fn, initVal) {
  let arr = Array.prototype.slice.call(this)
  let res = initVal ? initVal : arr[0]
  let start = initVal ? 0 : 1
  for (let i = start; i < arr.length; i++) {
    res = fn(res, arr[i], i, arr)
  }
  return res
}
let arr = [1, 2, 3]
let res = arr.myReduce((pre, cur) => pre + cur, 10)
console.log(res);

110、扁平化数组

// 普通递归
function flatten (arr) {
  let res = []
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      res = res.concat(flatten(arr[i]))
    } else {
      res.push(arr[i])
    }
  }
  return res
}
let arr = [1, [2, 3]]
let res = flatten(arr)
console.log(res);

// 利用reduce
function flatten (arr) {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur)
  }, [])
}
let arr = [1, [2, 3]]
let res = flatten(arr)
console.log(res);

// 利用some函数逐层展平
function flatten (arr) {
  while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)
  }
  return arr
}
let arr = [1, [2, 3]]
let res = flatten(arr)
console.log(res);

// ES新API flat
function flatten (arr) {
  return arr.flat(Infinity)
}
let arr = [1, [2, 3]]
let res = flatten(arr)
console.log(res);

// 利用json API
function flatten (arr) {
  let str = JSON.stringify(arr)
  str = str.replace(/(\[|\])/g, '')
  str = `[${str}]`
  return JSON.parse(str)
}
let arr = [1, [2, 3]]
let res = flatten(arr)
console.log(res);

111、proxy VS Object.defineProperty

 

 

区别: 

(1)Proxy代理整个对象,Object.defineProperty只代理对象上的某个属性。

(2)vue中,Proxy在调用时递归,Object.defineProperty在一开始就全部递归,Proxy性能优于Object.defineProperty

(3)对象上定义新属性时,Proxy可以监听到,Object.defineProperty监听不到

(4)数组新增删除修改时,Proxy可以监听到,Object.defineProperty监听不到

(5)Proxy不兼容IE,Object.defineProperty不兼容IE8及以下

112、map VS weakMap

(1)WeakMap只接受对象作为key,如果设置其他类型的数据作为key,会报错。

(2)WeakMap的key所引用的对象都是弱引用,只要对象的其他引用被删除,垃圾回收机制就会释放该对象占用的内存,从而避免内存泄漏。

(3)由于WeakMap的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性。

(4)没有clear()方法,也不能遍历

113、正则(未完成)

114、位运算(未完成)

115、严格模式下特性

 116、 ["1","2","3"].map(parseInt)结果,并解释原因

 

117、vue的生命周期

讲这个流程图以及不同的钩子需要注意的问题

参考博文:

超详细vue生命周期解析(详解)_ら陈佚晨的博客-CSDN博客_vue生命周期

118、事件循环

执行过程:
(1)同步任务在主线程中执行栈执行

(2)遇到异步任务,就进入event Table进行回调注册,等指定事件完成,就把回调Push到消息队列

(3)当执行栈为空检查微任务队列,依次执行,再检查宏任务,进入执行栈执行,执行栈清空了,再检查微任务队列,如此循环

宏任务:setTimeout, setInterval, setImmediate, i/o(ajax读数据),ui render, dom事件

微任务:promise, mutaionObserver, process.nextTick

119、进程线程

进程:进程是程序的实体,系统进行资源分配和调度的基本单位

线程:cpu调度和分配的最小单位,每条线程并行执行不同的任务

他们的区别:

  1. 进程是一次执行过程,动态产生动态消亡的,进程切换系统开销大,线程切换效率高
  2. 进程之间有独立的空间,互不影响,同一个进程内的线程共享相同的资源
  3. 进程的通信方式很多:无名管道、有名管道、信号机制、信号灯、共享内存、消息队列、套接字socket。线程的通信方式:全局变量,信号量,互斥锁

 120、一些看代码输出题

// 第1题
var name = "222"
var a = {
    name: "111",
    say: function () {
        console.log(this.name);
    }
}
var fun = a.say;
fun(); // undefined
a.say(); // 111

var b = {
    name: "333",
    say: function (fun) {
        fun();
    }
}
b.say(a.say); // undefined
b.say = a.say; // 333
b.say();

// 第2题
if(!("a" in window)) {
    var a = 1;
}
alert(a);    //    undefiend


// 第3题
for (var i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i); // 5 5 5 5 5
    }, 1000);
}


for (var i = 0; i < 5; i++) {
    (function (i) {
        setTimeout(() => {
            console.log(i); // 0 1 2 3 4
        })
    })(i)
}

console.log(i); // 5

130、cookie token session

参考这篇总结一下:

稳了!这才是cookie,session与token的真正区别_javaQQ群 866865133的博客-CSDN博客

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值