前端面试重点总结

前端面试重点总结

CSS

冒泡

一.flex布局

设为 Flex 布局以后,子元素的float、clear和vertical-align属性将失效。

容器的属性

1.flex-direction

flex-direction属性决定主轴的方向(即项目的排列方向)。

.box {
flex-direction: row | row-reverse | column | column-reverse;
}

row(默认值)主轴为水平方向,起点在左端
row-reverse主轴为水平方向,起点在右端
column主轴为垂直方向,起点在上沿
column-reverse主轴为垂直方向,起点在下沿
2.flex-wrap

flex-wrap属性定义,是否换行。

nowrap(默认)不换行
wrap换行,第一行在上方
wrap-reverse换行,第一行在下方。
3.flex-flow

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

.box {
flex-flow: || ;
}

4.justify-content

justify-content属性定义了 项目 在 主轴 上的对齐方式。

flex-start(默认值)左对齐
flex-end右对齐
center居中
space-between两端对齐,项目之间的间隔都相等
space-around每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍
5.align-items

align-items属性定义项目在交叉轴上如何对齐。【单行、侧轴】

flex-start交叉轴的起点对齐
flex-end交叉轴的终点对齐。
center交叉轴的中点对齐。
baseline项目的第一行文字的基线对齐。
stretch(默认值)如果项目未设置高度或设为auto,将占满整个容器的高度。
6.align-content

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。【多行、侧轴】
flex-start:与交叉轴的起点对齐。
flex-end:与交叉轴的终点对齐。
center:与交叉轴的中点对齐。
space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
stretch(默认值):轴线占满整个交叉轴。

项目的属性

1.order

order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

2.flex-grow

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。

3.flex-shrink

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。负值对该属性无效。

4.flex-basis

flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

.item {
flex-basis: | auto; /* default auto */
}
它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。

5.flex

flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。

.item {
flex: none | [ <‘flex-grow’> <‘flex-shrink’>? || <‘flex-basis’> ]
}
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。

建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。

6.align-self

align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。
.item {
align-self: auto | flex-start | flex-end | center | baseline | stretch;
}
该属性可能取6个值,除了auto,其他都与align-items属性完全一致。

圣杯布局、双飞翼布局

https://blog.csdn.net/qq_38128179/article/details/86533976

要求:
header和footer各自占领屏幕所有宽度,高度固定。 中间的container是一个三栏布局。
三栏布局两侧宽度固定不变,中间部分自动填充整个区域。 中间部分的高度是三栏中最高的区域的高度。

方法:
网格布局
flex布局
相对定位+float
calc

媒体查询

eg:当横屏时背景为蓝色

@media only screen and (orientation: landscape) {
    body {
        background-color: lightblue;
    }
}

orientation:portrait | landscape
portrait:指定输出设备中的页面可见区域高度大于或等于宽度
landscape: 除portrait值情况外,都是landscape

rem和em的区别

rem指相对根元素的字体大小的单位
em是以父级元素为参考
手动设定:

<style> html{ font-size: 18px;  }  </style>    1rem = 18px

js方式自适应:

const c = ()=>{

   let w = document.documentElement.clientWidth; // 获取设备的宽度

   let n = (20*(w/320)>40?40+'px':(20*(w/320)+'px')) //20为参数  320设备宽度  40显示最大字号

   document.documentElement.style.fontSize = n        // 设置根字体大小

}

window.addEventListener('load',c)     //初始值

window.addEventListener('resize',c)     //屏幕改变的是的值

</script>

自媒体查询 常用在适配不同的设备显示场景下,根据屏幕大小显示不同页面,但功能大致不变的这么一个技术。rem布局 它是一个长度单位,这个长度单位是根据‘根元素’的字体大小的单位确定的(根元素字体大小 = 1rem)。响应式布局 它是在页面宽度改变时,整个页面或者页面中的局部随着页面的缩放进行响应并实时变化(响应式布局和自媒体查询有区别)。在如今复杂的开发中这几个技术越来越密不可分,在项目中可以通过实际需求进行搭配。

JS

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

JS的基本数据类型

基本数据类型:String,Boolean,Number,Undefined,Null;
引用(复杂)数据类型:Object(Array,Date,RegExp,Function);
ES6新增:
基本:Symbol 类型
复杂:Set 类型、Map 类型、WeakSet 类型、WeakMap 类型、TypedArray 类型

Q1:怎么让一个对象具有一个私有属性?(Symbol有什么用?)
Answer:用 Symbol 作为对象的 key 即可

{ // 私有作用域
  let a = Symbol()
  window.oo = {
    name: '对象',
    age: 18,
    [a]: '这是一个私有属性'
  }
  console.log(oo[a]) // 这是一个私有属性
}
// 不能再作用域外打印...
此时对象 oo 就有一个私有属性,该属性在作用域外就无法正确打印。

Q2:怎么快速去重一个数组?
Answer:用 Set 加 Array.form()
这是一道很经典的面试题,在此之前先讲讲 es6 之前的去重方法:

let array = [1, 2, 3, 4, 4, 5, 5, 6]
~function uniq() {
  let result = []
  let hash = {}
  for (let i=0; i<array.length; i++) {
    hash[array[i]] = true
  }
  for (let key in hash) {
    result.push(key)
  }
  console.log(result)
  return result
}()

但是该方法有巨大的弊端,去重数组中不能有对象,而且该方法返回的结果中都是字符串,所以无法对这样的数组进行去重。

当我们使用 Set 时

let a = {a: 11}
let array = [0, 1, '1', '22', 22, a, a, 66]
~function uniq() {
  return Array.from(new Set(array)) // 装比写法 [... new Set(array)]
}()

甚至连对象的引用也能去重,很简略的方法。

Q3:Map有啥用?
Answer:map 更像是对象的拓展,他的 key 可以是任意类型,不再像之前的对象 key 只能是字符串,也就是这个特性,我们可以去优化之前的去重,但是也没有必要,因为已经有 set 了。
🌰

let myMap = new Map()
 
let keyObj = {}
let keyFunc = function () {}
let keyString = "a string"
 
// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键keyObj关联的值");
myMap.set(keyFunc, "和键keyFunc关联的值");

Q4:WeakMap 和 Map 的区别?
Answer:

WeakMap 区别就是对 key 的引用是弱引用
WeakMap 的 key 只能是对象
需要了解的就是弱引用是啥了:
文章:ES2015 WeakMap的学习和使用

Q5:WeakSet 和 Set 的区别?
Answer:同上

Q6:TypedArray 有什么用?
Answer:这个类型用的途径更少了,主要用于二进制文件(音频,文件,视频,图片…)的处理,一般用不到,除非你自己开发轮子。
基本数据类型和引用数据类型的区别:
1、保存位置不同:基本数据类型保存在栈内存中,引用数据类型保存在堆内存中,然后在栈内存中保存了一个对堆内存中实际对象的引用,即数据在堆内存中的地址,JS对引用数据类型的操作都是操作对象的引用而不是实际的对象,如果obj1拷贝了obj2,那么这两个引用数据类型就指向了同一个堆内存对象,具体操作是obj1将栈内存的引用地址复制了一份给obj2,因而它们共同指向了一个堆内存对象;
为什么基本数据类型保存在栈中,而引用数据类型保存在堆中?
1)堆比栈大,栈比堆速度快;
2)基本数据类型比较稳定,而且相对来说占用的内存小;
3)引用数据类型大小是动态的,而且是无限的,引用值的大小会改变,不能把它放在栈中,否则会降低变量查找的速度,因此放在变量栈空间的值是该对象存储在堆中的地址,地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响;
4)堆内存是无序存储,可以根据引用直接获取;
按引用访问:js不允许直接访问保存在堆内存中的对象,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值;

ECMAScript中所有函数的参数都是按值来传递的,对于原始值,只是把变量里的值传递给参数,之后参数和这个变量互不影响,对于引用值,对象变量里面的值是这个对象在堆内存中的内存地址,因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因,因为它们都指向同一个对象;
2、基本数据类型使用typeof可以返回其基本数据类型,但是NULL类型会返回object,因此null值表示一个空对象指针;引用数据类型使用typeof会返回object,此时需要使用instanceof来检测引用数据类型;
3、定义引用数据类型需要使用new操作符,后面再跟一个构造函数来创建;
1)使用new操作符创建对象;

var obj1 = new Object();
obj1.a = 1;

2)使用对象字面量表示法创建对象;

var obj1 = {
  a: 1,
  b: 2
}

3)可以通过点表示法访问对象的属性,也可以使用方括号表示法来访问对象的属性;

堆栈

对象和数组的区别

对象是包含已命名的值的无序集合,而数组则是包含已编码的值的有序集合。
在这里插入图片描述

自己实现Symbol

// 要求:
// sym('key') 创建一个新的值
// sym.for('key') 检测创建的话取值,未创建新建值
// sym.keyFor(value) 取sym值对应的key
// new sym() 报错,Uncaught TypeError: sym is not a constructor

const sym = (() => {
    const cache = new Map();
    function fn(key) {
        if (this instanceof fn) {
            throw TypeError('sym is not a constructor');
        }
        return {};
    }
    fn.for = function(key) {
        if (!cache.has(key)) {
            cache.set(key, {});
        }
        return cache.get(key);
    }
    fn.keyFor = function(value) {
        for (const [k, v] of cache) {
            if (v === value) {
                return k;
            }
        }
    }
    return fn;
})();

new sym();
Uncaught TypeError: sym is not a constructor
    at new fn (<anonymous>:6:19)
    at <anonymous>:1:1

var name1 = sym('name');
var name2 = sym('name');
name1 === name2; // false
var name3 = sym.key('name');
var name4 = sym.key('name');
sym.keyFor(name3); // 'name'
sym.keyFor(name4); // 'name'
sym.keyFor(name2'); // undefined
let a={},
        b=Symbol('1'),
        c=Symbol('1');
    a[b]="123";
    a[c]="456";
    console.log(a[b]);//"123"
    console.log(a[c]);//"456"
    //Symbol是唯一的

Object.prototype.toString()应用

    let a = {},
        b = {
            n: '1'
        }
        c = {
            m: '2'
        }
    a[b] = "123";
    a[c] = "456";
    console.log(a[b]);//456
    //对象引用类型值必须存成字符串object Object,
    //a[b]=a[object Object],a[c]=a[object Object],a[c]将a[b]覆盖

如果此方法在自定义对象中未被覆盖,toString() 返回 “[object type]”,其中 type 是对象的类型。以下代码说明了这一点:

var o = new Object();
o.toString(); // returns [object Object]

Object.prototype.toString()与valueOf的区别

1、toString()和valueOf()的主要不同点在于,toString()返回的是字符串,而valueOf()返回的是原对象

2、由于undefined和null不是对象,所以它们toString()和valueOf()两个方法都没有

3、数值Number类型的toString()方法可以接收转换基数,返回不同进制的字符串形式的数值;而valueOf()方法无法接受转换基数

4、时间Date类型的toString()方法返回的表示时间的字符串表示;而valueOf()方法返回的是现在到1970年1月1日00:00:00的数值类型的毫秒数

5、包装对象的valueOf()方法返回该包装对象对应的原始值

6、使用toString()方法可以区分内置函数和自定义函数

闭包

    var test=(function(i)
    {
        return function()
        {
            alert (i*=2);
        }
    })(2);//立即执行函数,test不是函数,而是一个函数执行,是一个表达式,把函数执行的结果返回test
    test(5);

弹出结果为字符串4
注意:alert弹出结果都会调用toString()方法
在这里插入图片描述
闭包内存不销毁

    let a=0,b=0;
    function A(a){
        A=function(b)
        {
            alert(a+b++);
        }
        alert(a++);
    }
    A(1);
    A(2);

在这里插入图片描述
此时全局作用域下的a=0,b=0
闭包作用:保存私有&保护全局

面向对象

function Foo(){
    getName = function(){
        console.log(1);
    };
    return this;
} 
 
Foo.getName = function(){
    console.log(2);
}
 
Foo.prototype.getName = function(){
    console.log(3);
}
 
var getName = function(){
    console.log(4);
}
 
function getName(){
    console.log(5);
}
 
//输出下列结果
 
Foo.getName();//2
 
getName();//4
 
Foo().getName();//1
 
getName();//1
 
new Foo.getName();//2
 
new Foo().getName();//3
 
new new Foo().getName();//3

分析:
在这里插入图片描述

new函数

同步异步

1.概念

浏览器多线程,JS单线程
(1)同步任务

同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务

(2)异步任务

异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务

(3)异步机制

(1)所有同步任务都在主线程上执行,行成一个执行栈
(2)主线程之外,还存在一个任务队列,只要异步任务有了结果,就会在任务队列中放置一个事件
(3)一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面还有哪些事件,那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行
(4)主线程不断的重复上面的第三步

(4)事件循环Event Loop
单线程从从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中读取新的任务,如果没有任务,就会等到,直到有新的任务,这就叫做任务循环,因为每个任务都是由一个事件触发的,因此也叫作事件循环。
Event Loop 包含两类:一类是基于 Browsing Context,一种是基于 Worker。二者的运行是独立的,也就是说,每一个 JavaScript 运行的"线程环境"都有一个独立的 Event Loop,每一个 Web Worker 也有一个独立的 Event Loop。

2.宏任务微任务

2.1任务队列

根据规范,事件循环是通过任务队列的机制来进行协调的。一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。setTimeout/Promise 等API便是任务源,而进入任务队列的是他们指定的具体执行任务。

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

  • 在此次 tick 中选择最先进入队列的任务(oldest task),如果有则执行(一次)
  • 检查是否存在 Microtasks,如果存在则不停地执行,直至清空 Microtasks Queue
  • 更新 render
  • 主线程重复执行上述步骤

在上诉tick的基础上需要了解几点:

  • JS分为同步任务和异步任务
  • 同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
  • 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
    在这里插入图片描述
2.2宏任务(macro)task

每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。

浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染,流程如下:

(macro)task->渲染->(macro)task->…

宏任务包含:

  • script(整体代码)
  • setTimeout
  • setInterval
  • I/O
  • UI交互事件
  • postMessage
  • MessageChannel
  • setImmediate(Node.js 环境)
setTimeout

setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。
setTimeout作用:
(1)函数去抖
让一个函数在一定间隔内没有被调用时,才开始执行被调用方法。比如当你在使用 google 搜索内容的时候,有些关键词输入到一半,谷歌会展示一个可选列表,根据你当前输入的内容作出的一个猜测联想。需要监听文字改变,每一次改变都会调用一次回调函数,现在需要的一种实现是在用户停止键盘事件一段时间后,去发送一个请求。

var debounce = function(method, context) {
 clearTimeout(method.tId);
 method.tId = setTimeout(function(){
  method.call(context);
 },100);
}

(2)轮训任务
js中可以使用setInterval开启轮询,但是这种存在一个问题就是执行间隔往往就不是你希望的间隔时间。

比如有个轮询任务间隔是100ms,但是执行方法的时间需要450ms,那么在200ms、300ms、400ms本来是计划中执行任务的时间,浏览器发现第一个还未执行完,那么就会放弃2、3、4次的任务执行,并且在500ms之后再次执行任务,这样的话,其实再次执行的间隔就只有50ms。使用setTimeout构造轮询能保证每次轮询的间隔。

setTimeout(function () {
 console.log('我被调用了');
 setTimeout(arguments.callee, 100);
}, 100);

(3)延迟js引擎的调用

var div = document.createElement('div');
div.innerHTML = '我是一个div';
div.setAttribute('style', 'width:200px; height:200px;background-color:#f00; ');
document.body.appendChild(div);
setTimeout(function() {
 console.log('我被调用了');
}, 1000);
2.3微任务microtask

在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。

所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。

微任务包含:

  • Promise.then
  • Object.observe
  • MutaionObserver
  • process.nextTick(Node.js 环境)
2.4为什么有了宏任务,还会有微任务存在?

因为宏任务太占用性能,当需要一些较早就准备好的方法,排在最后才执行的时候,又不想新增一个宏任务,那么就可以把这些方法,一个一个的放在微任务队列里面,在这个宏任务中的代码执行完后,就会执行微任务队列。

2.5运行机制

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

执行一个宏任务(栈中没有就从事件队列中获取)
执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
如图:
在这里插入图片描述

3.异步编程

3.1.回调函数

什么是回调函数?

1.自己定义的
2.自己没有调用
3.最终会执行

优点:简单、容易理解和部署。
缺点:不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数

3.2.promise
3.2.1Promise包括以下几个规范
  • 一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)
  • 一个promise的状态只能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换。一旦状态改变,就不会再变,任何时候都可以得到这个结果。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
  • promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致
  • then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用,同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象
  • 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
3.2.2Promise 优缺点

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

3.2.3在使用Promise时,我们需要检测一些浏览器是否支持Promise
    if(typeof(Promise)==="function") {
    console.log("支持");
}
else {
    console.log("不支持");
}

Promise是异步的,是指他的then()和catch()方法,Promise本身还是同步的,所以这里先执行a变量内部的Promise同步代码。(同步优先)

3.2.4promise基本原理是什么

Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。

3.2.5主要解决什么问题

地狱回调

3.2.6then为什么可以链式调用?

Promise.prototype.then 方法返回的是一个新的 Promise 对象,因此可以采用链式写法

3.2.7冒泡

Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。

3.2.8Promise.all方法

Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

var p = Promise.all([p1,p2,p3]);

上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)

p 的状态由 p1、p2、p3 决定,分成两种情况:

  • (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
3.2.9Promise.race方法

Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

var p = Promise.race([p1,p2,p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。

如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。

3.2.10Promise.resolve 方法,Promise.reject 方法

有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。

var jsPromise = Promise.resolve($.ajax('/whatever.json'));

上面代码将 jQuery 生成 deferred 对象,转为一个新的 ES6 的 Promise 对象。

如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。

var p = Promise.resolve('Hello');
 
p.then(function (s){
  console.log(s)
});
// Hello

上面代码生成一个新的Promise对象的实例p,它的状态为fulfilled,所以回调函数会立即执行,Promise.resolve方法的参数就是回调函数的参数。

如果Promise.resolve方法的参数是一个Promise对象的实例,则会被原封不动地返回。

Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。

var p = Promise.reject('出错了');
 
p.then(null, function (s){
  console.log(s)
});

// 出错了
上面代码生成一个Promise对象的实例,状态为rejected,回调函数会立即执行

3.2.11

==注意 new Promise() 是同步方法,resolve才是异步方法。 ==

3.3.async/await
3.3.1async

作为一个关键字放在函数的前面,表示该函数是一个异步函数,意味着该函数的执行不会阻塞后面代码的执行 异步函数的调用跟普通函数一样
async的内部实现原理就是如果该函数中有一个返回值,当调用该函数时,默认会在内部调用Promise.solve() 方法把它转化成一个Promise 对象作为返回,若函数内部抛出错误,则调用Promise.reject()返回一个Promise 对象
既然async返回的是一个Promise 对象,那么Promise 的所有用法他都可以用,如Promise.catch捕获异常等

3.3.2await

await即等待,用于等待一个Promise对象。它只能在异步函数 async function中使用,否则会报错
它的返回值不是Promise对象而是Promise对象处理之后的结果
await表达式会暂停当前 async function的执行,等待Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function,若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。​如果 await 操作符后的表达式的值不是一个 Promise,那么该值将被转换为一个已正常处理的 Promise。

3.3.3优点

与Promise对比
1、不再需要多层.then方法
假设一个业务分很多步骤完成,并且每个步骤都是异步,依赖上一个步骤的结果。

function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}

// Promise方式
function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

// async await方式
async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}
doIt();

2、可以对Promise进行并行处理
在这里插入图片描述

3.4.事件监听

任务的执行不取决于代码的顺序,而取决于某个事件是否发生

3.5.发布订阅模式

也叫观察者模式

setTimeout函数

创建实例的过程是什么

(new的过程)

创建的过程中为什么要把proto指向构造函数的prototype

每个函数都有prototype属性即原型对象,通过函数实例化出来的对象有个__proto__属性,指向原型对象。
原型对象的用途是为每个实例对象存储共享的方法和属性,它仅仅是一个普通对象,所有的实例都共享同一个原型对象

继承的方法有几种?

https://zhuanlan.zhihu.com/p/37735247
https://www.jianshu.com/p/983db4bdfef3
1.原型链继承
在创建子类型的实例时,不能向父类传递参数
第二个是引用类型值的原型,当一个实例改变这个值的时候,在所有的实例上都有所表现,所以会用借用构造函数的方式继承
通过原型链继承的时候不能用对象字面量创建原型方法,这样会重写原型链
2.借用构造函数继承(伪造对象、经典继承)
3.实例继承(原型式继承)
4.组合式继承
5.寄生组合继承
6.es6继承

寄生组合相比于组合继承的优势在什么地方

由于组合式继承会调用两次父类的构造函数,第一次是在创建子类原型的时候,第二次是调用子类构造函数内部调用父类的时候,
在第一次调用父类构造函数的时候,Child.prototype会得到一个name 属性,它是父类的实例属性,不过现在在子类的原型中,
在调用子类构造函数的时候,又一次调用父类的构造函数,不过这次又在新对象上创建了实例属性name,这个属性就屏蔽了原型中的的同名属性,为了解决这个,就用寄生组合继承

做题:

var a = 'oops,global'
function foo(){
  console.log(this.a);
}
function doFoo(fn){
  fn()
}
 
var obj = {
  a:2,
  foo:foo
}
doFoo(obj.foo)//'oops,global'

深浅拷贝

1.定义

浅拷贝和深拷贝都只针对于引用数据类型。
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;
但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象;
区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制;
在这里插入图片描述

2.实现深拷贝

方法一:手动实现
let obj1 = {
   a: 1,
   b: 2
}
let obj2 = {
   a: obj1.a,
   b: obj1.b
}
obj2.a = 3;
alert(obj1.a); // 1
alert(obj2.a); // 3

缺点:当对象中有对象时无法实现深拷贝

let obj1 = {
   a: {
     b: 2
   }
}
let obj2 = {
   a: obj1.a
}
obj2.a.b = 3;
console.log(obj1.a.b); // 3
console.log(obj2.a.b); // 3
方法二:递归实现
function deepCopy(obj1) {
      var obj2 = Array.isArray(obj1) ? [] : {};
      if (obj1 && typeof obj1 === "object") {
        for (var i in obj1) {
          if (obj1.hasOwnProperty(i)) {
            // 如果子属性为引用数据类型,递归复制
            if (obj1[i] && typeof obj1[i] === "object") {
              obj2[i] = deepCopy(obj1[i]);
            } else {
              // 如果是基本数据类型,只是简单的复制
              obj2[i] = obj1[i];
            }
          }
        }
      }
      return obj2;
    }
    var obj1 = {
      a: 1,
      b: 2,
      c: {
        d: 3
      }
    }
    var obj2 = deepCopy(obj1);
    obj2.a = 3;
    obj2.c.d = 4;
    alert(obj1.a); // 1
    alert(obj2.a); // 3
    alert(obj1.c.d); // 3
    alert(obj2.c.d); // 4

缺陷:当遇到两个互相引用的对象,会出现死循环的情况,为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环;

function deepCopy(obj1) {
      var obj2 = Array.isArray(obj1) ? [] : {};
      if (obj1 && typeof obj1 === "object") {
        for (var i in obj1) {
          var prop = obj1[i]; // 避免相互引用造成死循环,如obj1.a=obj
          if (prop == obj1) {
            continue;
          }
          if (obj1.hasOwnProperty(i)) {
            // 如果子属性为引用数据类型,递归复制
            if (prop && typeof prop === "object") {
              obj2[i] = (prop.constructor === Array) ? [] : {};
              arguments.callee(prop, obj2[i]); // 递归调用
            } else {
              // 如果是基本数据类型,只是简单的复制
              obj2[i] = prop;
            }
          }
        }
      }
      return obj2;
    }
    var obj1 = {
      a: 1,
      b: 2,
      c: {
        d: 3
      }
    }
    var obj2 = deepCopy(obj1);
    obj2.a = 3;
    obj2.c.d = 4;
    alert(obj1.a); // 1
    alert(obj2.a); // 3
    alert(obj1.c.d); // 3
    alert(obj2.c.d); // 4
方法三:JSON.stringify & JSON.parse

使用JSON.stringify和JSON.parse实现深拷贝:JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;

function deepCopy(obj1){
    let _obj = JSON.stringify(obj1);
    let obj2 = JSON.parse(_obj);
    return obj2;
  }
    var a = [1, [1, 2], 3, 4];
    var b = deepCopy(a);
    b[1][0] = 2;
    alert(a); // 1,1,2,3,4
    alert(b); // 1,2,2,3,4

缺陷:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON;

let obj1 = {
   fun:function(){
      alert(123);
   }
}
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun); // function
console.log(typeof obj2.fun); // undefined
方法四:函数库lodash,方法_.cloneDeep
var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false
方法五:jQuery.extend()

定义和用法
jQuery.extend() 函数用于将一个或多个对象的内容合并到目标对象。

注意:

  1. 如果只为$.extend()指定了一个参数,则意味着参数target被省略。此时,target就是jQuery对象本身。通过这种方式,我们可以为全局对象jQuery添加新的函数。
  2. 如果多个对象具有相同的属性,则后者会覆盖前者的属性值。

语法
$.extend( target [, object1 ] [, objectN ] )
指示是否深度合并

$.extend( [deep ], target, object1 [, objectN ] )

警告: 不支持第一个参数传递 false 。

参数描述
deep可选。 Boolean类型 指示是否深度合并对象,默认为false。如果该值为true,且多个对象的某个同名属性也都是对象,则该"属性对象"的属性也将进行合并。
targetObject类型 目标对象,其他对象的成员属性将被附加到该对象上。
object1可选。 Object类型 第一个被合并的对象。
objectN可选。 Object类型 第N个被合并的对象。
var $ = require('jquery');
var obj1 = {
   a: 1,
   b: {
     f: {
       g: 1
     }
   },
   c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);  // false
方法六:…展开
		//这个方法只适用于此类简单对象  严格来说 不算是深拷贝 但依然具有效果
		let obj = { a:10 }
		let objclone = {...obj}
		objclone.a = 5
		console.log(obj,objclone)  // 10   5 

slice()和concat()都并非深拷贝

循环引用

1.什么是循环引用

当对象 1 中的某个属性指向对象 2,对象 2 中的某个属性指向对象 1 就会出现循环引用,(当然不止这一种情况,不过原理是一样的)下面通过代码和内存示意图来说明一下。

function circularReference() {
  let obj1 = {
	};
	let obj2 = {
 	 b: obj1
	};
	obj1.a = obj2;
}

2.JS 中引用计数垃圾回收策略的问题

先简单讲一下 JS 中引用垃圾回收策略大体是什么样的一个原理,当一个变量被赋予一个引用类型的值时,这个引用类型的值的引用计数加 1。就像是代码中的 obj1 这个变量被赋予了 obj1 这个对象的地址,obj1 这个变量就指向了这个 obj1(右上)这个对象,obj1(右上)的引用计数就会加1.当变量 obj1的值不再是 obj1(右上)这个对象的地址时,obj1(右上)这个对象的引用计数就会减1.当这个 obj1(右上)对象的引用计数变成 0 后,垃圾收集器就会将其回收,因为此时没有变量指向你,也就没办法使用你了。

看似很合理的垃圾回收策略为什么会有问题呢?

就是上面讲到的循环引用导致的,下面来分析一下。当 obj1 这个变量执行 obj1 这个对象时,obj1 这个对象的引用计数会加 1,此时引用计数值为 1,接下来 obj2 的 b 属性又指向了 obj1 这个对象,所以此时 obj1 这个对象的引用计数为 2。同理 obj2 这个对象的引用计数也为2.

当代码执行完后,会将变量 obj1 和 obj2 赋值为 null,但是此时 obj1 和 obj2 这两个对象的引用计数都为1,并不为 0,所以并不会进行垃圾回收,但是这两个对象已经没有作用了,在函数外部也不可能使用到它们,所以这就造成了内存泄露。

3.解决办法

标记清除回收策略
大致流程是这样的,最开始的时候将所有的变量加上标记,当执行 cycularReference 函数的时候会将函数内部的变量这些标记清除,在函数执行完后再加上标记。这些被清除标记又被加上标记的变量就被视为将要删除的变量,原因是这些函数中的变量已经无法被访问到了。像上述代码中的 obj1 和 obj2 这两个变量在刚开始时有标记,进入函数后被清除标记,然后函数执行完后又被加上标记被视为将要清除的变量,因此不会出现引用计数中出现的问题,因为标记清除并不会关心引用的次数是多少。
== JSON.decycle 可以去除循环引用==

vue/react框架

双向数据绑定

ES5:Object.defineProperty

<script>
      let obj = {
      name: ""
    };
    let newObj = JSON.parse(JSON.stringify(obj));
    Object.defineProperty(obj, "name", {
      get() {
        return newObj.name;
      },
      set(val) {
        if (val === newObj.name) return; //增加判断优化性能 判断新值与旧值是否一样 一样就返回  不一样的话的再次赋值
        newObj.name = val;
        obServer();
      }
    });
    //   重新赋值的方法
    function obServer() {
      spanName.innerHTML = newObj.name; //获取obj.name的值
      inputName.value = newObj.name;
    }
    obServer(); //开始就执行一次
    setTimeout(() => {
      obj.name = "大左";
    }, 1000);
  </script>

Vue2.0不足之处
1.需要对原始数据进行克隆 不然死循环 上面有提到
2.如果我们想给对象中的数据进行get和set的拦截 就要一个个设置 对象中的属性都要单独的监听一下 如果有多个就要循环遍历了 分别来监听了

ES6:Proxy

 <script>
    let obj = {};
    obj = new Proxy(obj, {
      get(target, prop) {
        console.log("D");
        return target[prop];
      },
      set(target, prop, value) {
        console.log("Z");
        target[prop] = value;
      }
    }); //监听整个对象 不需要指定属性 相当于把对象里所有的属性都监听了 So 直接写整体的set get
  </script>

MVC和MVVM区别

本质上没有区别
这三种模式均为MV* 模式,M为模型层,V为视图层,都是希望能更好的对模型、视图与逻辑层的解耦。

MVC模型中,C为(controller)。主要处理逻辑为:View触发事件,controller响应并处理逻辑,调用Model,Model处理完成后将数据发送给View,View更新。
在这里插入图片描述

MVP模型中,P为Presenter,并以Presenter为核心,负责从model获取数据,并填充到View中。该模型使得Model和View不再有联系,且View被称为“被动视图”,暴露出setter接口。
在这里插入图片描述

MVVM模型中,VM为ViewModel,同样是以VM为核心,但是不同于MVP,MVVM采用了数据双向绑定的方案,替代了繁琐复杂的DOM操作。该模型中,View与VM保持同步,View绑定到VM的属性上,如果VM数据发生变化,通过数据绑定的方式,View会自动更新视图;VM同样也暴露出Model中的数据。
在这里插入图片描述

vue和react区别

vue 提供了一系列的api, 而react的api 很少

vue的思想是响应式的,也就是基于是数据可变的,实现了数据的双向绑定,react整体是函数式的思想,是单向数据流,推崇结合immutable来实现数据不可变

vue 采用了template, react采用了jsx (本质上都是模版)

React依赖Virtual DOM,而Vue.js使用的是DOM模板。React采用的Virtual DOM会对渲染出来的结果做脏检查。

Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作DOM。

vue和react的优劣势

vue的优点

  1. 简单:官方文档很清晰,比 Angular 简单易学。

  2. 快速:异步批处理方式更新 DOM。

  3. 组合:用解耦的、可复用的组件组合你的应用程序。

  4. 紧凑:~18kb min+gzip,且无依赖。

  5. 强大:表达式 & 无需声明依赖的可推导属性 (computed properties)。

  6. 对模块友好:可以通过 NPM、Bower 或 Duo 安装,不强迫你所有的代码都遵循 Angular 的各种规定,使用场景更加灵活。

vue的缺点:

  1. 新生儿:Vue.js是一个新的项目,没有angular那么成熟。

  2. 影响度不是很大:google了一下,有关于Vue.js多样性或者说丰富性少于其他一些有名的库。

  3. 不支持IE8。

react的优点:

  1. 速度快:在UI渲染过程中,React通过在虚拟DOM中的微操作来实现对实际DOM的局部更新。

  2. 跨浏览器兼容:虚拟DOM帮助我们解决了跨浏览器问题,它为我们提供了标准化的API,甚至在IE8中都是没问题的。

  3. 模块化:为你程序编写独立的模块化UI组件,这样当某个或某些组件出现问题是,可以方便地进行隔离。

  4. 单向数据流:Flux是一个用于在JavaScript应用中创建单向数据层的架构,它随着React视图库的开发而被Facebook概念化。

  5. 同构、纯粹的javascript:因为搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化。

  6. 兼容性好:比如使用RequireJS来加载和打包,而Browserify和Webpack适用于构建大型应用。它们使得那些艰难的任务不再让人望而生畏。

react的缺点:

陡峭的学习曲线:由于复杂的设置过程,属性,功能和结构,它需要深入的知识来构建应用程序。

跨域问题

1.什么是跨域?

跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。

所谓同源是指,域名,协议,端口均相同

请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。

浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。

解决办法:
jsonp(只支持get请求,支持老的IE浏览器)适合加载不同域名的js、css,img等静态资源;
CORS(支持所有类型的HTTP请求,但浏览器IE10以下不支持)适合做ajax各种跨域请求;
Nginx代理跨域和nodejs中间件跨域原理都相似,都是搭建一个服务器,直接在服务器端请求HTTP接口,这适合前后端分离的前端项目调后端接口。
document.domain+iframe适合主域名相同,子域名不同的跨域请求。
postMessage、websocket都是HTML5新特性,兼容性不是很好,只适用于主流浏览器和IE10+。

在这里插入图片描述

vue组件通信

  1. props和$emit
    父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件来做到的。

  2. $attrs和$listeners
    第一种方式处理父子组件之间的数据传输有一个问题:如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想传递数据给组件C怎么办呢?
    如果采用第一种方法,我们必须让组件A通过prop传递消息给组件B,组件B在通过prop传递消息给组件C;要是组件A和组件C之间有更多的组件,那采用这种方式就很复杂了。Vue2.4开始提供了 a t t r s 和 attrs和 attrslisteners来解决这个问题,能够让组件A之间传递消息给组件C。

  3. 中央事件总线
    上面两种方式处理的都是父子组件之间的数据传递,而如果两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式。新建一个Vue事件bus对象,然后通过bus. e m i t 触 发 事 件 , b u s . emit触发事件,bus. emitbus.on监听触发的事件

  4. provide和inject
    父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。

  5. v-model 父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input’,val)自动修改v-model绑定的值

  6. p a r e n t 和 parent和 parentchildren

  7. boradcast和dispatch
    vue1.0中提供了这种方式,但vue2.0中没有,但很多开源软件都自己封装了这种方式,比如min ui、element ui和iview等。
    比如如下代码,一般都作为一个mixins去使用, broadcast是向特定的父组件,触发事件,dispatch是向特定的子组件触发事件,本质上这种方式还是on和on和emit的封装,但在一些基础组件中却很实用。

  8. vuex处理组件之间的数据交互 如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。

https

HTTPS 协议是由 HTTP 加上 TLS/SSL 协议构建的可进行加密传输、身份认证的网络协议,主要通过数字证书、加密算法、非对称密钥等技术完成互联网数据传输加密,实现互联网传输安全保护。

1.设计目标主要有三个。

(1)数据保密性:保证数据内容在传输的过程中不会被第三方查看。就像快递员传递包裹一样,都进行了封装,别人无法获知里面装了什么 [4] 。
(2)数据完整性:及时发现被第三方篡改的传输内容。就像快递员虽然不知道包裹里装了什么东西,但他有可能中途掉包,数据完整性就是指如果被掉包,我们能轻松发现并拒收 [4] 。
(3)身份校验安全性:保证数据到达用户期望的目的地。就像我们邮寄包裹时,虽然是一个封装好的未掉包的包裹,但必须确定这个包裹不会送错地方,通过身份校验来确保送对了地方 [4] 。

2.HTTPS 请求交互时的过程:

客户端发起请求(Client Hello 包)
a) 三次握手,建立TCP 连接
b) 支持的协议版本(TLS/SSL)
c) 客户端生成的随机数client.random,后续用于生成“对话密钥”
d) 客户端支持的加密算法
e) sessionid,用于保持同一个会话(如果客户端与服务器费尽周折建立了一个HTTPS 链接,刚建完就断了,也太可惜)

服务端收到请求,然后响应(Server Hello)
a) 确认加密通道协议版本
b) 服务端生成的随机数server.random,后续用于生成“对话密钥”
c) 确认使用的加密算法(用于后续的握手消息进行签名防止篡改)
d) 响应服务器证书(CA 机构颁发给服务端的证书)

客户端收到证书进行验证
a) 验证证书是否是上级CA 签发的, 在验证证书的时候,浏览器会调用系统的证书管理器接口对证书路径中的所有证书一级一级的进行验证,只有路径中所有的证书都是受信的,整个验证的结果才是受信。
b) 服务端返回的证书中会包含证书的有效期,可以通过失效日期来验证证书是否过期。
c) 验证证书是否被吊销了

d) 前面我们知道CA 机构在签发证书的时候,都会使用自己的私钥对证书进行签名。

证书里的签名算法字段 sha256RSA 表示CA 机构使用sha256对证书进行摘要,然后使用RSA 算法对摘要进行私钥签名,而我们也知道RSA 算法中,使用私钥签名之后,只有公钥才能进行验签。

e) 浏览器使用内置在操作系统上的CA机构的公钥对服务器的证书进行验签。确定这个证书是不是由正规的机构颁发。验签之后得知CA 机构使用sha256 进行证书摘要,然后客户端再使用sha256 对证书内容进行一次摘要,如果得到的值和服务端返回的证书验签之后的摘要相同,表示证书没有被修改过。

f) 验证通过后,就会显示绿色的安全字样。

g) 客户端生成随机数,验证通过之后,客户端会生成一个随机数pre-master secret , 客户端根据之前的: Client.random +sever.random + pre-master 生成对称密钥然后使用证书中的公钥进行加密,同时利用前面协商好的加密算法,将握手消息取HASH 值,然后用“随机数加密“握手消息+握手消息HASH 值(签名)”然后传递给服务器端;(在这里之所以要取握手消息的HASH值,主要是把握手消息做一个签名,用于验证握手消息在传输过程中没有被篡改过。)

服务端接收随机数
a) 服务端收到客户端的加密数据以后,用自己的私钥对密文进行解密。然后得到client.random/server.random/pre-master secret. ,再用随机数密码 解密 握手消息与HASH 值,并与传过来的HASH 值做对比确认是否一致。
b) 然后用随机密码加密一段握手消息(握手消息+握手消息的HASH 值 )给客户端。

客户端接收消息
a) 客户端用随机数解密并计算握手消息的HASH,如果与服务端发来的HASH 一致,此时握手过程结束,
b) 之后所有的通信数据将由之前交互过程中生成的pre master
secret / client.random/server.random 通过算法得出sessionKey,作为后续交互过程中的对称密钥。

3.优缺点

优点
使用 HTTPS 协议可认证用户和服务器,确保数据发送到正确的客户机和服务器 [2] ;
HTTPS 协议是由 SSL+HTTP构建的可进行加密传输、身份认证的网络协议,要比 HTTP安全,可防止数据在传输过程中被窃取、改变,确保数据的完整性 [2] 。
HTTPS 是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本 [2] 。
缺点
相同网络环境下,HTTPS 协议会使页面的加载时间延长近 50%,增加 10%到 20%的耗电。此外,HTTPS 协议还会影响缓存,增加数据开销和功耗 [2] 。
HTTPS 协议的安全是有范围的,在黑客攻击、拒绝服务攻击和服务器劫持等方面几乎起不到什么作用 [2] 。
最关键的是,SSL 证书的信用链体系并不安全。特别是在某些国家可以控制 CA 根证书的情况下,中间人攻击一样可行 [2] 。
成本增加。部署 HTTPS 后,因为 HTTPS 协议的工作要增加额外的计算资源消耗,例如 SSL 协议加密算法和 SSL 交互次数将占用一定的计算资源和服务器成本。在大规模用户访问应用的场景下,服务器需要频繁地做加密和解密操作,几乎每一个字节都需要做加解密,这就产生了服务器成本。随着云计算技术的发展,数据中心部署的服务器使用成本在规模增加后逐步下降,相对于用户访问的安全提升,其投入成本已经下降到可接受程度 [4] 。

公钥私钥区别在哪

公钥是公开的,不需要保密,而私钥是由个人自己持有,并且必须妥善保管和注意保密。

CA证书为什么会有过期现象

CA机构考虑私钥可能存在破解风险,所以要不断的强制更新。久有效的证书导致CRL不断增加,会增加浏览器的请求流量压力有效期由3年(39个月)缩短为2年(825天)。EV证书因信任级别较高,最长有效期2年有效期对保护证书安全性是基于PKI技术的数字证书,结合公钥加密算法、对称加密算法、散列算法等密码技术,用于实现认证、加密、签名等安全功能。没有合理设置SSL证书有效期,会造成极大的安全威胁。自签名SSL证书为例,自签名SSL证书不受国际标准制约,可以把有效期设置为10年甚至20年。自签名证书长期未更新,仍在使用非常不安全的1024位RSA算法和SHA-1签名算法。超长的有效期和脆弱的加密算法,让黑客拥有足够的时间和算力破解证书的加密密钥,造成严重后果。 CA机构必须重新验证身份信息,确保身份认证信息是最新的,并仍然拥有该域名。

2.介绍第一个项目(介绍功能,优化,节流防抖,图片并发控制)

vue响应式的原理

vue2.x中能否通过数组下标改变数组

不会

箭头函数和普通函数的区别

1、样式不同,箭头函数是 =>,普通函数是 function;

2、箭头函数不能作为构造函数使用,也就不能使用 new 关键字;

3、箭头函数不绑定 arguments,可以考虑用剩余参数代替;

4、箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值,定义的时候就确定了;

5、call、apply、bind 并不会影响 this 的指向;

6、箭头函数没有原型属性;

7、箭头函数不能当作 Generator 函数,不能使用 yield 关键字;

改变this方法

call()和apply()

- 这两个方法都是函数对象的方法,需要通过函数对象来调用

- 当对函数调用call()和apply()都会调用函数执行

- 在调用call()和apply()可以将一个对象指定为第一个参数

此时这个对象将会成为函数执行时的this

- call()方法可以将实参在对象之后依次传递

- apply()方法需要将实参封装到一个数组中统一传递

bind()

Function.prototype.bind(obj)

将函数内的this绑定为obj, 并将函数返回,传参方式和call()一样

在定时器中this指向window,如果要改变this指向,只能用bind(),因为不会立即执行函数,

如果用call()或者apply()会立即调用函数

call()/apply()/bind()三者的区别

call()/apply()是立即调用函数

bind()是将函数返回
  • this的情况:

    1.以函数形式调用时,this永远都是window

    2.以方法的形式调用时,this是调用方法的对象

    3.以构造函数的形式调用时,this是新创建的那个对象

    4.使用call和apply调用时,this是指定的那个对象

手写bind

Function.prototype.myBind = function(context) {
	let that = this
	let arg =[].slice.call(arguments, 1)
	return function(){
		let arg2 = [].slice.call(arguments) // 此时的argument表示bind创建后的函数的参数
		return that.apply(context, arg.concat(arg2))
	}
}

ES6扩展参数简化

Function.prototype.myBind = function(context, ...arg) {
	let that = this
	return function(...arg2){
		return that.apply(context, [...arg, ...arg2])
	}
}

MDN源码

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis || window,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

判断数据类型的方法

1、使用typeof能判断出四种,分别是number,string,boolean,object,剩余的均被检测为object

2、使用instanceof,根据instanceof的定义:判断参照对象的prototype属性所指向的对象是否在被行测对象的原型链上,
这种变量判断可以检测出9种,undefined和null被检测为object,因为js中没有这种全局类型

但是这种方式判断有个弊端:

 对于number,string,boolean这三种类型,

      只有通过构造函数定义比如:

      let num =new Number(1);

      let str =  new String('abc');

      let bool = new Boolean(true);

      这样定义才能检测出.

      let num = 1;

      let str = 'abc';

      let bool = true;

      这样定义是检测不出来的

3、使用constructor检测
针对于instanceof的弊端,我们使用constructor检测,constructor是原型对象的属性指向构造函数。

这种方式解决了instanceof的弊端,可以检测出除了undefined和null的9种类型

但是这种方式仍然有个弊端,就是constructor所指向的的构造函数是可以被修改的.
4、使用Object.prototype.toString.call

 Object.prototype.toString可以取得对象的内部属性[[class]],并根据这个内部属性返回诸如"[object  Number]"的字符串.那么我们就可以通过call获取内部属性[[class]]

  例如: Object.prototype.toString.call(num)//"[object  Number]"

  在ES5以及之前这种方式是唯一访问内部属性的方法,但是ES6已经废除了[[class]]的内部属性,取而待之的是internal solt

  并且Object.prototype.toString执行步骤也发生了改变

5、使用jquery的$.type

 其实$.type是基于ES5的Object.prototype.toString.call进一步封装.可以检测出所有的变量类型.

数组拍平

a:递归实现

function fn(arr){
    let arr1 = []
     arr.forEach((val)=>{
         if(val instanceof Array){
             arr1 = arr1.concat(fn(val))
         }else{
             arr1.push(val)
         }
      })
      return arr1
 }

b:reduce实现

function fn(arr){
    return arr.reduce((prev,cur)=>{
        return prev.concat(Array.isArray(cur)?fn(cur):cur)
    },[])
}

c:flat

参数为层数(默认一层)

arr.flat(Infinity)

d:扩展运算符

function fn(arr){
    let arr1 = [];
    let bStop = true;
    arr.forEach((val)=>{
        if(Array.isArray(val)){
            arr1.push(...val);
            bStop = false
        }else{
            arr1.push(val)
        }
    })
    if(bStop){
        return arr1;
    }
    return fn(arr1)
}

e:toString

let arr1 = arr.toString().split(',').map((val)=>{
            return parseInt(val)
        })
        console.log(arr1)

f:apply

function flatten(arr){
     while(arr.some(item => Array.isArray(item))){
           arr =  [].concat.apply([],arr);
     }
      return arr;
}

4. 手写懒加载

//n用来避免重复给image赋值src
let n = 0;
//获取所有image元素
const images = document.querySelectorAll('img');
//所有image元素的长度
const num = images.length;
//懒记载实现
function lazyLoad() {
    //获取可视区域高度
    const clientHeight = document.body.clientHeight;
    //获取滚动区域高度
    const scrollTop = document.body.scrollTop;
    // 循环所有image元素, inde为n, 不用每次都从0开始,已经循环过的不需要重新循环,提高运行效率
    for (let index = n; index < num; index++) {
        //当前image
        const img = images[index];
        //获取当前image到浏览器顶部的距离,而不是到当前窗口的顶部的距离
        const offsetTop = img.offsetTop;
        //判断是否在可视区域内
        if (offsetTop < clientHeight + scrollTop) {
            //判断是否已经加载过
            if (img.getAttribute('src') === './image/loading.gif') {
            //加载真正的src
                img.src = img.getAttribute('data-src');
            }
            // n++
            n += 1;
        }
    }
}
// 先运行一次
lazyLoad();
//将懒加载函数赋值给浏览器的onscroll函数,滚动即执行函数
window.onscroll = lazyLoad;

  1. 手写vue的defineProperty的相关原理,直接说不会换成了手写节流

js分为哪三部分

ECMAScript(核心),DOM(文档对象模型),BOM(浏览器对象模型)。

ES6,ES5的区别

一、新增箭头函数

箭头函数解决的问题
简化了写法。箭头函数适用于函数体只有一行的情况;当有多行时,可用普通函数增加可读性。 少打代码,结构清晰
明确了this。传统JS的this是在运行的时候确定的,而不是在定义的时候确定的;而箭头函数的this是在定义时就确定的,不能被改变,也不能被call,apply,bind这些方法修改。 明确运行时候this指向谁,不用运行时判断this指向 注:箭头函数没有自己的this,他的this就是外层的this,指向上一个不是箭头函数的函数的this。因为js的机制,所以指向的是一个非箭头函数的函数的作用域。
箭头函数与普通函数的区别
普通function的声明在变量提升中是最高的,箭头函数没有函数提升
箭头函数没有this,函数体内部的this对象就是定义的时候所在的对象而不是使用时所在的对象
箭头函数没有arguments对象,该对象在函数体内不存在,如果要用,可以使用rest参数
箭头函数不能作为构造函数,不能被new,没有property
call和apply方法只有参数,没有作用域
不可以使用yield命令,因此箭头函数不能做Generator函数
二、块级作用域

ES6中的let命令,声明变量,用法和var差不多,但是let是为JavaScript新增了块级作用域,ES5中是没有块级作用域的,并且var有变量提升的概念,但是在let中,使用的变量一定要进行声明;const声明常量
ES6中变量的结构赋值,比如:var [a,b,c] = [0,1,2];
三、类继承

ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念,听起来和Java中的面向对象编程的语法有些像,但是二者是不一样的。

四、设置默认函数参数

ES6中可以设置默认函数参数,如function A(x,y=9){};

五、promise

promise产生背景:解决回调地狱问题,处理异步请求

promise用法:链式调用,成功和失败的回调,三个状态,pending状态改变时触发。状态一旦改变就不会再变。

六、模板字符串

七、赋值结构

ES6后class采用的继承方法

set和map的区别

集合 是以 [value, value]的形式储存元素,字典 是以 [key, value] 的形式储存

2、nodejs了解过吗,会用吗
3、webpack了解过吗?说一下config里面的东西

怎么设置元素隐藏,有什么方法,有什么区别

css隐藏元素的方法一:使用opacity属性

opacity属性的意思是检索或设置对象的不透明度当他的透明度为0的时候,视觉上它是消失了,但是他依然占据着那个位置,并对网页的布局起作用。它也将响应用户交互。添加了opacity属性的元素,它的背景和元素内容也是会跟着变化的。

.hide {
  opacity: 0;
}

css隐藏元素的方法二:使用display属性

display属性才是真正意义上的隐藏元素,当元素的display属性为none时,该元素就会就会从视觉中消失,并且连盒模型也不生成.也不会在页面占据任何位置,不但如此,就连它的子元素也会一同从盒子模型中消失。

.hide {
   display: none;
}

css隐藏元素的方法三:使用visibility属性

visibility属性类似opacity属性,该属性值为hidden的时候,元素将会隐藏,也会占据着自己的位置,并对网页的布局起作用,与 opacity 唯一不同的是它不会响应任何用户交互。此外,元素在读屏软件中也会被隐藏。

.hide {
   visibility: hidden;
}

说明:这个属性也能够实现动画效果,只要它的初始和结束状态不一样。这确保了 visibility 状态切换之间的过渡动画可以是时间平滑的。

css隐藏元素的方法四:使用position属性

position属性的意义就是把元素脱离文档流移出视觉区域,添加该属性后既不会影响布局,又能让元素保持可以操作。应用该属性后,主要就是通过控制方向(top,left,right,bottom),达到一定的值,离开当前可是页面。

.hide {

   position: absolute;

   top: -9999px;

   left: -9999px;

}

注意:得避免使用这个方法去隐藏任何可以获得焦点的元素,因为如果那么做,当用户让那个元素获得焦点时,会导致一个不可预料的焦点切换。这个方法在创建自定义复选框和单选按钮时经常被使用。

说明:给他和它的子元素添加的任何动画效果交互效果都会不起作用。

讲一下ajax,用来干什么,怎么用

12、讲一下浏览器存储吧,讲了cookie、storage,深入讲一下cookie吧,cookie里面有什么,storage怎么用
13、讲一讲前端优化吧,有什么方法吗

css选择器,权重

!important > 行内样式 > ID选择器 > (类选择器 | 属性选择器 | 伪类选择器 )> 元素选择器

2、React生命周期
3、shouldComponentUpdate 优化

var、let、const区别

变量提升
var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined
let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错

块级作用域
var不存在块级作用域
let和const存在块级作用域

重复声明
var允许重复声明变量
let和const在同一作用域不允许重复声明变量

修改声明的变量
var和let可以
const声明一个只读的常量。一旦声明,常量的值就不能改变,但对于对象和数据这种引用类型,内存地址不能修改,可以修改里面的值。

generator函数

最大特点就是可以交出函数的执行权(即暂停执行)。

function* gen(x){
  var y = yield x + 2;
  return y;
}

上面代码就是一个 Generator 函数。它不同于普通函数,是可以暂停执行的,所以函数名之前要加星号,以示区别。

整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明。Generator 函数的执行方法如下。

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器 )g 。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针 g 的 next 方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的 yield 语句,上例是执行到 x + 2 为止。

换言之,next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。

  1. vue computed and watch区别, computed和methods区别,methods可以代替computed吗
  2. vue computed支持异步吗
  3. 说所有原生js dom选择器
  4. token存入localstorage还是cookie好
  5. cookie 和 localstorage区别
  6. querySelector参数
  7. 除了ajax以外浏览器还有什么可以发请求的东西,fetch 用过吗
  8. 自己有什么优点

1
2
3
一个a数组 [{data:string}],存放365天的日期
一个b数组 [{data:string,price:string}] 存放有特价的天数和价格
要求:用0(n)的时间复杂度,实现一个数组中,包含全年的日期,同时有特价的那天有price的显示,没有特价的那天price显示’–’
面试官态度很好,还给出提示,双层循环是可以实现,但是效率太低,不能使用,你如何用一层循环实现,写一下伪代码

for ( let i =0 ; i < b.length; i++) {
let index // 用来存储当前b数组中在a数组中的位置
index = b[i][data] - a[0][data] // 因为a数组日期是递增的,所以相减获得index
a[index][price] = b[i][price] // 直接用index替换
a[i][price] = ‘–’

}
然后面试官说可以使用双指针的方式来优化一下,然后就说了一下思路,就让我下去自己实现了

  1. vue的双向绑定原理
  2. vue的生命周期
  3. vue里面 computed 和watch 的区别 以及原理
    6.了解vuex吗?
  4. css box-shadow
    在这里插入图片描述

transation transfor animation的 区别以及用法

tansition是一个过渡属性,就是一个属性从一个值过渡到另一个值,tansform变换,就是一个整体的位置(或整体大小)发生变换,animation动画,就是在一段时间内各种属性进行变化从而达到一个动画的效果。

transition

主要包含四个值,transition-property(执行变换的属性),transition-duration(执行变换的持续时间),transition-timing-function(变换的速率变化模式),transition-delay(变换延迟时间)。

(1)transition-property

这只属性是用来指定元素中哪一个属性执行过渡效果,取值有

none 没有属性需要执行过渡

all 所有属性发生变化(默认值)

indent 元素的某一个属性值

indent的取值覆盖了大部分的css取值,但是自适应的时候,宽度发生变化是不会触发的。

(2)transiton-duration

用来指定元素转换过程的持续时间,单位为s或ms,可以作用域任何元素。默认值为0

(3)transition-timing-function

transition-timing-function允许我们根据时间的推进去改变属性值的变换速率。主要有以下几个取值

ease(逐渐变慢)

linear(匀速)

ease-in(加速)

ease-out(减速)

ease-in-out(加速然后减速)

cubic-bezier(允许自定义一个时间曲线)

(4)transition-delay

用来指定一个动画开始执行的时间,也就是说当改变元素属性值后多唱时间执行transition效果。

当我们想要改变不知一个css属性而是改变多个属性的时候,只要把几个transition的声明串在一起,用逗号分隔开,然后定义各自的效果。

因为transition-duration和transition-delay都是时间值,所以一定要把duration的值放在delay的前面。

transform

1.rotate(* deg):围绕中心点2D旋转若干度,单位为deg。

如果*中的值为正数则顺时针旋转,如果是负值则逆时针旋转
2.translate(x,y)移动

translate有三种情况,translate(x,y),x轴和y轴同时移动(如果这里只设定一个值,证明x的值和y的值相同,同样的也是x轴和y轴同时移动),translateX(x),沿着x轴移动,translateY(y),沿着y轴移动。

当值为负数时,反方向移动物体,其基点默认为元素 中心点,也可以根据transform-origin进行改变基点

3.scale缩放

缩放和移动是极其相似的,它也是具有三种情况:scale(x,y)使元素水平方向和垂直方向同时缩放(也就是x轴和y轴同时缩放);scaleX(x)就只是水平方向进行缩放(x轴缩放);scaleY(y)仅缩放垂直方向(y轴)。但它们具有相同的缩放中心点和技术,其中心点就是元素的中心位置,缩放基数就是1(也就是参数是相对于1的多少倍),如果参数大于1元素就放大,反之元素就缩小。
4.skew扭曲(倾斜)

扭曲在我看来就是拉动四个角,往外拉,在水平方向拉,在垂直方向拉,水平方向和垂直方向同时拉。

先来看看只在水平方向拉
5.其他属性

(1)transform-style

设置内嵌的元素在 3D 空间如何呈现。有两个值:flat:所有子元素在 2D 平面呈现;preserve-3d:保留3D空间

(2)perspective-origin

(3)backface-visibility

该属性用于隐藏内容的背面。默认情况下,背面可见的,也就是当翻转后,背面内容仍然可见。但是当backface-visibility设置为hidden的时候,旋转后内容将会被隐藏。

取值:visible,hidden

三、animation(动画)

动画animation有两个关键

一个是animation本身的属性值,一个是keyframes的写法,keyframes比较简单,我们先来说说这个。

keyframes其实就是帧的集合,为什么这样说呢?

我们在上面看到了transition,发现它其实可以做很多动画对不对?但是有没有发现,transition它只允许在两种状态之间进行过度,没有中间的值。

而我们把一个动画看作是许多个帧的集合,我们可以去设定每个帧的呈现(每个时刻对应的css属性值),这样的动画就有更多的可变性了。

keyfram用@keyframs开头,后面接一个动画名,然后用大括号括起来,在里面写每一个关键帧的属性。

animation-name(动画名,也就是keyfram中定义的动画名)

animation-duration(动画持续时间)

animation-timing-function(动画变化的速率)

animation-delay(动画延迟播放的时间)

animation-iteration-count(动画循环的次数,infinite是无限次)

animation-direction(动画的方向)

animation-play-state动画的播放状态

positon 定位

1.static

positon定位的默认值,没有定位

2.relative

生成相对定位的元素,相对于其正常位置进行定位,一般在子元素设置absoute定位时,给父元素设置relative

元素的位置通过top、right、bottom、left 控制,其值的定位起点都是是父元素左上角(这点和absoute、fixed不一样)

3.absoute

生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位

元素的位置通过top、right、bottom、left 控制,top、left的定位起点是包含块左上角,right、bottom的定位起点是包含块右下角

4.fixed (ie6不兼容)

生成绝对定位的元素,相对于浏览器窗口进行定位,和absoute的区别是fixed不会跟随屏幕滚动(常见的各种贴屏广告)

元素的位置通过top、right、bottom、left 控制,top、left的定位起点是包含块左上角,right、bottom的定位起点是包含块右下角
1.inherit

规定应该从父元素继承 position 属性的值

inherit 关键字可用于任何 HTML 元素上的任何 CSS 属性

兼容:ie7及以下版本不支持此属性

2.initial

设置positon的值为默认值(static)

兼容:ie不支持此属性

问:有了static为什么还会存在此属性,不是多此一举?

答:initial 关键字可用于任何 HTML 元素上的任何 CSS 属性,不是postion特有的

3.unset

设置positon的值为不设置:

如果该属性的默认属性是 继承属性(例如字体相关的默认属性基本都是继承),该值等同于 inherit
如果该属性的默认属性 不是继承属性(例如pisition的默认属性为static),该值等同于 initial

兼容:ie不支持此属性

4.sticky

css3新属性,它的表现就像position:relative和position:fixed的合体:

1.在目标区域在屏幕中可见时,它的行为就像position:relative;

数组中统计重复元素的个数

图片常见的格式

JPEG
TIFF
PNG
GIF
PSD
RAW
EPS
SVG
PDF
BMP

图片压缩算法

vue3.0和2.0有哪些区别?
手写一下2.0的双向绑定,讲一下。
3.0的proxy。(知道是啥但不会写)
手写数组扁平化。
手写图片懒加载,讲一下原理。
还有一个JSON.stringify()(不会)

让用JS写了一个数组去重。
问了一下HTTP状态码。
了解布局方式吗。
问了一下怎么实现跨域。

1.html的解析过程

2.css重绘和重排

3.盒模型

4.选择器、权重,*号作用和缺点

5.伪类伪元素(忘记了,直接说反了)

6.清除浮动

7.BFC

8.flex

9.float与absolute区别(不记得了)

10.闭包

11.ES6

12.箭头函数

13.call、apply、bind

http与https

http请求方式和区别

http状态码

跨域

git(场景题,不会----);

本地修改,仓库被人提交poll

数组去重(挖坑,Set)

Set原理,所有都能去重吗[{},{},{}]

vue

computed,data,vuex作用(状态管理、组件通信),通信方式

介绍数据结构
null和undefined的区别
是对象吗null和undefined
react hooks原理
useState原理
不想git merge产生commit记录该怎么
github actions CI做了什么
ts高级用法
浏览器怎么渲染的
讲一讲eventloop
常见的宏任务微任务
算法题 a的b次方

3、git相关的工作流了解吗
4、node.js 和 axios 浏览器兼容 浏览器用哪个api进行请求
5、按需加载、路由懒加载
6、前后端用什么报文格式进行交互
8、js写千分符
9、项目登录验证部分

1.localstorage 存满了怎么办
2.localstorage 遇到浏览器隐身模式怎么处理
3.webpack
4.Vue3和Vue2的区别
5.proxy代理了解吗
6.promise 有什么缺点
7.promise如果遇到两个接口,接口A在接口B有效的情况下就不调用,怎么解决
8.不同的浏览器兼容flex布局
9.二维数组转化为一维数组,一维数组转化为二维数组
10.typeScript 清楚吗

实习经历的亮点或者项目难点,工作职责
有没有用到动画、布局技巧、兼容性问题等等
实现动画效果的方式
用animation实现动画,实现一个元素向上淡出,这样消失的效果,怎样实现
用transition怎样设置
了解transform属性吗
从top:50%到top:-50%和translate平移有区别吗?效果一样的吗?它们对别的CSS属性有没有影响
手撕代码:用JS实现一个队列
清空队列除了把队列赋值为空数组,还有别的方法吗?
代码中this的指向分别指向什么?
JS对象的语法是怎么写的?
去哪儿网根目录下面的文件的作用
package.json的作用是什么,里面有什么内容?
.gitignore里面内容是什么,它的作用是什么?
eslint的两个文件.eslintignore和.eslintrc.js的作用是什么?
去哪儿网项目写得比较好的地方
数据联动具体是怎么实现的
state.js用到localStorage,这个localStorage的作用是什么?

如何实现图片懒加载?怎么判断图片到了可视区域

https://zhuanlan.zhihu.com/p/55311726

单页面应用怎么实现的路由跳转,原理是什么

https://blog.csdn.net/weixin_44135121/article/details/103578650

两个页面之间如何实现数据传递

https://www.cnblogs.com/1020182600HENG/p/7517928.html

js获取DOM节点的方法

一、通过元素类型的方法来操作:
document.getElementById();//id名,
document.getElementsByTagName();//标签名
document.getElementsByClassName();//类名
document.getElementsByName();//name属性值,一般不用
document.querySelector();//css选择符模式,返回与该模式匹配的第一个元素,结果为一个元素;如果没找到匹配的元素,则返回null
document.querySelectorAll()//css选择符模式,返回与该模式匹配的所有元素,结果为一个类数组
注意:

前缀为document,意思是在document节点下调用这些方法,当然也可以在其他的元素节点下调用

二、根据关系树来选择(遍历节点树):
【先简单介绍一下节点:

DOM(文档对象模型)可以将任何HTML、XML文档描绘成一个多层次的节点树。所有的页面都表现为以一个特定节点为根节点的树形结构。html文档中根节点为document节点。

所有节点都有nodeType属性,代表节点的不同类型,通过nodeType属性可以来判断节点的类型。经常使用的节点主要有以下几种类型:

Element类型(元素节点):nodeType值为 1
Text类型(文本节点):nodeType值为 3
Comment类型(注释节点):nodeType值为 8
所有的节点都有 hasChildNodes()方法 判断有无子节点 有一个或多个子节点时返回true】

通过一些属性可以来遍历节点树:

parentNode//获取所选节点的父节点,最顶层的节点为#document
childNodes //获取所选节点的子节点们
firstChild //获取所选节点的第一个子节点
lastChild //获取所选节点的最后一个子节点
nextSibling //获取所选节点的后一个兄弟节点 列表中最后一个节点的nextSibling属性值为null
previousSibling //获取所选节点的前一兄弟节点 列表中第一个节点的previousSibling属性值为null

三、基于元素节点树的遍历(遍历元素节点树):
parentElement //返回当前元素的父元素节点(IE9以下不兼容)
children // 返回当前元素的元素子节点
firstElementChild //返回的是第一个元素子节点(IE9以下不兼容)
lastElementChild //返回的是最后一个元素子节点(IE9以下不兼容)
nextElementSibling //返回的是后一个兄弟元素节点(IE9以下不兼容)
previousElementSibling //返回的是前一个兄弟元素节点(IE9以下不兼容)

怎么获取所有节点

常见的http请求头和响应头

https://blog.csdn.net/qq_30553235/article/details/79282113
https://blog.csdn.net/vincent_wen0766/article/details/109216167

http状态码

类别	原因短语

1XX Informational(信息性状态码) 接受的请求正在处理
2XX Success(成功状态码) 请求正常处理完毕
3XX Redirection(重定向状态码) 需要进行附加操作以完成请求
4XX Client Error(客户端错误状态码) 服务器无法处理请求
5XX Server Error(服务器错误状态码) 服务器处理请求出错

2XX——表明请求被正常处理了

1、200 OK:请求已正常处理。

2、204 No Content:请求处理成功,但没有任何资源可以返回给客户端,一般在只需要从客户端往服务器发送信息,而对客户端不需要发送新信息内容的情况下使用。

3、206 Partial Content:是对资源某一部分的请求,该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。响应报文中包含由Content-Range指定范围的实体内容。

3XX——表明浏览器需要执行某些特殊的处理以正确处理请求

4、301 Moved Permanently:资源的uri已更新,你也更新下你的书签引用吧。永久性重定向,请求的资源已经被分配了新的URI,以后应使用资源现在所指的URI。

5、302 Found:资源的URI已临时定位到其他位置了,姑且算你已经知道了这个情况了。临时性重定向。和301相似,但302代表的资源不是永久性移动,只是临时性性质的。换句话说,已移动的资源对应的URI将来还有可能发生改变。

6、303 See Other:资源的URI已更新,你是否能临时按新的URI访问。该状态码表示由于请求对应的资源存在着另一个URL,应使用GET方法定向获取请求的资源。303状态码和302状态码有着相同的功能,但303状态码明确表示客户端应当采用GET方法获取资源,这点与302状态码有区别。

当301,302,303响应状态码返回时,几乎所有的浏览器都会把POST改成GET,并删除请求报文内的主体,之后请求会自动再次发送。

7、304 Not Modified:资源已找到,但未符合条件请求。该状态码表示客户端发送附带条件的请求时(采用GET方法的请求报文中包含If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since中任一首部)服务端允许请求访问资源,但因发生请求未满足条件的情况后,直接返回304.。

8、307 Temporary Redirect:临时重定向。与302有相同的含义。

4XX——表明客户端是发生错误的原因所在。

9、400 Bad Request:服务器端无法理解客户端发送的请求,请求报文中可能存在语法错误。

10、401 Unauthorized:该状态码表示发送的请求需要有通过HTTP认证(BASIC认证,DIGEST认证)的认证信息。

11、403 Forbidden:不允许访问那个资源。该状态码表明对请求资源的访问被服务器拒绝了。(权限,未授权IP等)

12、404 Not Found:服务器上没有请求的资源。路径错误等。

5XX——服务器本身发生错误

13、500 Internal Server Error:貌似内部资源出故障了。该状态码表明服务器端在执行请求时发生了错误。也有可能是web应用存在bug或某些临时故障。

14、503 Service Unavailable:抱歉,我现在正在忙着。该状态码表明服务器暂时处于超负载或正在停机维护,现在无法处理请求。

xss攻击和csrf攻击有关的吧

XSS攻击有哪几种类型?

常见的 XSS 攻击有三种:反射型XSS攻击、DOM-based 型XXS攻击以及存储型XSS攻击。

1.反射型XSS攻击

反射型 XSS 一般是攻击者通过特定手法(如电子邮件),诱使用户去访问一个包含恶意代码的 URL,当受害者点击这些专门设计的链接的时候,恶意代码会直接在受害者主机上的浏览器执行。反射型XSS通常出现在网站的搜索栏、用户登录口等地方,常用来窃取客户端 Cookies 或进行钓鱼欺骗。

2.存储型XSS攻击

也叫持久型XSS,主要将XSS代码提交存储在服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码。当目标用户访问该页面获取数据时,XSS代码会从服务器解析之后加载出来,返回到浏览器做正常的HTML和JS解析执行,XSS攻击就发生了。存储型 XSS 一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。

3.DOM-based 型XSS攻击

基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击。DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞。
如何防御XSS攻击?

  1. 对输入内容的特定字符进行编码,例如表示 html标记的 < > 等符号。

  2. 对重要的 cookie设置 httpOnly, 防止客户端通过document.cookie读取 cookie,此 HTTP头由服务端设置。

  3. 将不可信的值输出 URL参数之前,进行 URLEncode操作,而对于从 URL参数中获取值一定要进行格式检测(比如你需要的时URL,就判读是否满足URL格式)。

  4. 不要使用 Eval来解析并运行不确定的数据或代码,对于 JSON解析请使用 JSON.parse() 方法。

  5. 后端接口也应该要做到关键字符过滤的问题。
    CSRF是跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击一样,存在巨大的危害性。
    你可以这样来理解:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。
    目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并验证;在 HTTP 头中自定义属性并验证。
    https://zhuanlan.zhihu.com/p/114750961

数组的方法有哪些

https://www.cnblogs.com/obel/p/7016414.html

携程二面
vue响应式原理
es6箭头函数和普通函数的区别
浏览器从输入url地址到显示页面的过程发生了什么
js继承以及es6的class
浏览器的事件循环机制
模块化以及npm
跨域问题
你写项目中登录是怎么实现的

页面优化

git的指令
Vue使用了哪些, Vue的生命周期(那个阶段可以操作DOM)
Vue的组件通信(父子和祖先子孙)
webpack的配置有哪些

外部文件和内联样式哪个优先级更高

优先级顺序为:内联样式 > 内部样式 > 外部样式

盒子模型的构造

内容(content)、内边距(padding)、边框(border)、外边距(margin

border画一个三角形

div {
    width: 0;
    height: 0;
    border: 40px solid;
    border-color: transparent transparent red;
}

在这里插入图片描述

<div id="blue"><div>

#blue {
    position:relative;
    width: 0;
    height: 0;
    border-width: 0 40px 40px;
    border-style: solid;
    border-color: transparent transparent blue;
}
#blue:after {
    content: "";
    position: absolute;
    top: 1px;
    left: -38px;
    border-width: 0 38px 38px;
    border-style: solid;
    border-color: transparent transparent yellow;
}

在这里插入图片描述

#blue:after {
    content: "";
    position: absolute;
    top: 2px;
    left: -38px;
    border-width: 0 38px 38px;
    border-style: solid;
    border-color: transparent transparent #fff;
}

在这里插入图片描述

捕获和冒泡事件的顺序

如果一个父元素同时有一个设置了捕获的事件和普通的事件, 子元素有一个普通的事件, 事件触发的顺序是怎么样的

垂直居中和水平居中

https://blog.csdn.net/weixin_37580235/article/details/82317240

transform的属性

transform: none|transform-functions;

描述测试

none 定义不进行转换。 测试
matrix(n,n,n,n,n,n) 定义 2D 转换,使用六个值的矩阵。 测试
matrix3d(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n) 定义 3D 转换,使用 16 个值的 4x4 矩阵。
translate(x,y) 定义 2D 转换。 测试
translate3d(x,y,z) 定义 3D 转换。
translateX(x) 定义转换,只是用 X 轴的值。 测试
translateY(y) 定义转换,只是用 Y 轴的值。 测试
translateZ(z) 定义 3D 转换,只是用 Z 轴的值。
scale(x,y) 定义 2D 缩放转换。 测试
scale3d(x,y,z) 定义 3D 缩放转换。
scaleX(x) 通过设置 X 轴的值来定义缩放转换。 测试
scaleY(y) 通过设置 Y 轴的值来定义缩放转换。 测试
scaleZ(z) 通过设置 Z 轴的值来定义 3D 缩放转换。
rotate(angle) 定义 2D 旋转,在参数中规定角度。 测试
rotate3d(x,y,z,angle) 定义 3D 旋转。
rotateX(angle) 定义沿着 X 轴的 3D 旋转。 测试
rotateY(angle) 定义沿着 Y 轴的 3D 旋转。 测试
rotateZ(angle) 定义沿着 Z 轴的 3D 旋转。 测试
skew(x-angle,y-angle) 定义沿着 X 和 Y 轴的 2D 倾斜转换。 测试
skewX(angle) 定义沿着 X 轴的 2D 倾斜转换。 测试
skewY(angle) 定义沿着 Y 轴的 2D 倾斜转换。 测试
perspective(n) 为 3D 转换元素定义透视视图。 测试

animation

在这里插入图片描述

设置渐变的属性

1、使用linear-gradient设置线性渐变,语法“linear-gradient(角度,颜色,颜色)”;
2、使用radial-gradient设置径向渐变,语法“radial-gradient(位置,颜色,颜色) ”。

async和await的使用, 以及怎么捕获这种异步处理方式的错误

方法一:try-catch
缺点:但是如果我们有一堆请求,每一个await都需要对应一个trycatch,那就多了很多垃圾代码。或许我们可以用一个trycatch将所有的await包起来,但是这样就很不方便对每一个错误进行对应的处理,还得想办法区分每一个错误。

方法二. then()
因为返回的是一个Promise,那我们首先想到的就是.then()和.catch(),于是很快就能写出以下代码:

window.onload = async () => {
  let res = await getData(3).then(r=>r).catch(err=>err);
  console.log(res) //getdata error
}

方法三:
上面那种方法是有一定问题的,如果getData()返回是resolve,res则是我们想要的结果,但是如果getData()返回是reject,res则是err,这样错误和正确的结果混在一起了,显然是不行的。

window.onload = async () => {
  let res = await getData(3)
  .then((res) => [null, res])
  .catch((err) => [err, null])
  console.log(res) // ["getdata error",null]
}

这种方式有的类似error first的风格。这样可以将错误和正确返回值进行区分了。但是这种方式会让每一次使用await都需要写很长一段冗余的代码,因此考虑提出来封装成一个工具函数:

function awaitWraper(promise) {
  return promise.then((res) => [null, res])
  .catch((err) => [err, null])
}
window.onload = async () => {
  let res = await awaitWraper(getData(3))
  console.log(res) // ["getdata error",null]
}

for…in, for…of, forEach和map(forEach, map循环能否终端)

for…in

for…in以原始插入顺序访问对象的可枚举属性,包括从原型继承而来的可枚举属性。

let obj = {
    a:123,
    b:"abc"
}
for(let pro in obj){
    console.log(pro+':' + obj[pro])
}
//a:123
//b:abc

for…in用于遍历数组时,可以将数组看作对象,数组下标看作属性名。但用for…in遍历数组时不一定会按照数组的索引顺序。


let arr = [123,'abc']
for(let pro in arr){
    console.log(pro+':' + arr[pro])
}
//0:123
//1:abc

for…of

for…of语句在可迭代对象(Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,为每个不同属性的值执行语句。

let arr = [123,'abc']
for(let item of arr){
    console.log(item)
}
//123
//abc

使用for…in循环时,获得的是数组的下标;使用for…of循环时,获得的是数组的元素值。
for…of遍历Map时,可以获得整个键值对对象:

let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);

for (let entry of iterable) {
  console.log(entry);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]

也可以只获得键值:

let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);

for (let [key] of iterable) {
  console.log(key);
}
//a
//b
//c

或者分别获得键与值:

let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);

for (let [key,value] of iterable) {
  console.log(key+':'+value);
}
//a:1
//b:2
//c:3

forEach

forEach方法对数组/Map/Set中的每个元素执行一次提供的函数。该函数接受三个参数:

正在处理的当前元素,对于Map元素,代表其值;
正在处理的当前元素的索引,对于Map元素,代表其键,对于Set而言,索引与值一样。
forEach()方法正在操作的数组对象。

let arr = [1,2,3,4]
arr.forEach(function(value,index,currentArr){
    currentArr[index]=value + 1
})
console.log(arr)//[ 2, 3, 4, 5 ]

map

map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。该函数接受的三个参数为:

当前元素
当前索引
当前被调用的数组
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]

二面

怎么学前端
项目
cookie, sessionStorage和localStorage
cookie的理解
出入url到页面显示的一个过程
页面优化的方式(不太了解, 随便说了下防抖与节流, 图片压缩)
CDN
服务器渲染
闭包
算法题
数字反转
数组去重(数组内含数组)
二面 (4-13 46分钟)
先吐槽一下携程面试,太吵了,完全听不清面试官说的什么,感觉像是在一个特别大的房间都在面试,面试体验极其不好

自我介绍

项目中哪些组件是通用性的

业务上通用组件有哪些

用了vue的哪些功能

vue的生命周期

vue的虚拟dom解决什么问题

为什么要用虚拟dom

js的异步处理机制

场景题:做一个倒计时,每一秒更新前端界面时间,可能与倒计时会有延迟,简单说就是本身时间到了但还没结束倒计时

项目中使用的是es6吗,讲一下es6的特性

和传统的js有什么区别

let,const和var的区别

类用原生js怎么实现

定义一个对象,所有属性都是…(这里确实没听清,问了两遍都没听清,面试官那边好吵)

场景题:

一个页面里面内嵌一个页面,怎么让页面高度一致,就是怎么实现不出现两个滚动条

注册页面,用户输入一半页面刷新了,怎么保持输入内容不丢失

图片传输过程中失效,展示一张默认图片给用户怎么实现,面试官就是想问img标签无法展示出图片,要怎么展示默认图片

跨域怎么解决

缓存机制的场景题:服务端资源已经更改,怎么强制客户端请求资源时刷新缓存

做题:

// 对某商品列表进行默认排序,排序规则
// 1.库存为0的置底,优先级最高
// 2.isTop为true置顶,多个为true时按4,5规则排序
// 3.isRecommend为true置顶,优先级低于isTop的产品,多个为true时按4,5规则排序
// 4.按价格升序
// 5.价格相同时按上架时间降序
 
const list = [
    {
        name: "商品1",
        "price": 20,
        "isTop": true,
        "isRecommend": true,
        "createTime": "2020/09/10 00:00:00",
        "inventory": 9
    },
    {
        "name": "商品3",
        "price": 30,
        "isTop": false,
        "isRecommend": true,
        "createTime": "2020/09/08 00:00:00",
        "inventory": 2
    },
    {
        "name": "商品2",
        "price": 40,
        "isTop": true,
        "isRecommend": false,
        "createTime": "2020/09/09 00:00:00",
        "inventory": 0
    },
    {
        "name": "商品4",
        "price": 50,
        "isTop": false,
        "isRecommend": true,
        "createTime": "2020/09/10 00:00:00",
        "inventory": 12
    }
]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nydia~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值