面试题

发表于 2021-12-21
预计阅读时间 50 分钟
12k
浏览

判断数据类型的方法🌟🌟🌟🌟🌟

  1. typeof(缺点typeof ull的值为Object,无法分辨是null还是Object)
  2. instanceof(只能判断对象是否存在于目标对象的原型链上)
  3. constructor
  4. Object.prototype.toString.call()
    一种最好的基本类型检测方式 Object.prototype.toString.call() ;它可以区分 null 、 string 、
    boolean 、 number 、 undefined 、 array 、 function 、 object 、 date 、 math 数据类型。
    缺点:不能细分为谁谁的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// -----------------------------------------typeof
typeof undefined // 'undefined'
typeof '10' // 'String'
typeof 10 // 'Number'
typeof false // 'Boolean'
typeof Symbol() // 'Symbol'
typeof Function // ‘function'
typeof null // ‘Object’
typeof [] // 'Object'
typeof {} // 'Object'


// -----------------------------------------instanceof
function Foo() { }
var f1 = new Foo();
var d = new Number(1)


console.log(f1 instanceof Foo);// true
console.log(d instanceof Number); //true
console.log(123 instanceof Number); //false -->不能判断字面量的基本数据类型


// -----------------------------------------constructor
var d = new Number(1)
var e = 1
function fn() {
console.log("ming");
}
var date = new Date();
var arr = [1, 2, 3];
var reg = /[hbc]at/gi;

console.log(e.constructor);//ƒ Number() { [native code] }
console.log(e.constructor.name);//Number
console.log(fn.constructor.name) // Function
console.log(date.constructor.name)// Date
console.log(arr.constructor.name) // Array
console.log(reg.constructor.name) // RegExp




//-----------------------------------------Object.prototype.toString.call()
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call(123)); // "[object Number]"
console.log(Object.prototype.toString.call("abc")); // "[object String]"
console.log(Object.prototype.toString.call(true)); // "[object Boolean]"


function fn() {
console.log("ming");
}
var date = new Date();
var arr = [1, 2, 3];
var reg = /[hbc]at/gi;
console.log(Object.prototype.toString.call(fn));// "[object Function]"
console.log(Object.prototype.toString.call(date));// "[object Date]"
console.log(Object.prototype.toString.call(arr)); // "[object Array]"
console.log(Object.prototype.toString.call(reg));// "[object RegExp]"

instanceof原理🌟🌟🌟🌟🌟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function myInstance(L, R) {//L代表instanceof左边,R代表右边
var RP = R.prototype
var LP = L.__proto__
while (true) {
if(LP == null) {
return false
}
if(LP == RP) {
return true
}
LP = LP.__proto__
}
}
console.log(myInstance({},Object));

为什么typeof null是Object🌟🌟🌟🌟

因为在JavaScript中,不同的对象都是使用二进制存储的,如果二进制前三位都是0的话,系统会判断为是Object类型,而null的二进制全是0,自然也就判断为Object

这个bug是初版本的JavaScript中留下的,扩展一下其他五种标识位:

000 对象
1 整型
010 双精度类型
100字符串
110布尔类型

手写call、apply、bind🌟🌟🌟🌟🌟

1.call和apply实现思路主要是:
判断是否是函数调用,若非函数调用抛异常
通过新对象(context)来调用函数
给context创建一个fn设置为需要调用的函数
结束调用完之后删除fn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 手写call
Function.prototype.myCall = function(context) {
if(typeof this != 'function') {
throw new Error('Not a function')
}
// 不穿参数默认window
context = context || window
// 保存this
context.fn = this;
// 保存参数
let args = Array.from(arguments).slice(1)//Array.from把伪数组转出数组

// 调用函数
let result = context.fn(...args);
delete context.fn
return result
}
// 手写apply
Function.prototype.myApply = function(context) {
if(typeof this != 'function') {
throw new Error('Not a function')
}
let result
// 不穿参数默认window
context = context || window
// 保存this
context.fn = this;
// 是否传参数
if(arguments[1]) {
result = context.fn(...arguments[1])
}else{
result = context.fn()
}
delete context.fn
return result
}

2.bind实现思路
判断是否是函数调用,若非函数调用抛异常
返回函数
判断函数的调用方式,是否是被new出来的
new出来的话返回空对象,但是实例的__proto__指向_this的prototype
完成函数柯里化
Array.prototype.slice.call()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Function.prototype.myBind = function(context) {
if(typeof this != 'function') {
throw new Error('Not a function')
}
// 保存调用bind函数
const _this = this;
// 保存参数
const args = Array.prototype.slice.call(arguments,1)
//返回一个函数
return function F() {
// 判断是不是new出来的
if(this instanceof F){
// 如果是new出来的
// 返回一个空对象,且使创建出来的实例的__proto__指向_this的prototype,且完成函数柯里化
return new _this(...args,...arguments)
}else{
// 如果不是new出来的改变this指向,且完成函数柯里化
return _this.apply(context,args.concat(...arguments))
}
}
}

字面量创建对象和new创建对象有什么区别,new内部都实现了什么,手写一个new🌟🌟🌟🌟🌟

字面量:
字面量创建对象更简单,方便阅读
不需要作用域解析,速度更快
new内部:
创建一个新对象
使新对象的__proto__指向原函数的prototype
改变this指向(指向新的obj)并执行该函数,执行结果保存起来作为result
判断执行函数的结果是不是null或Undefined,如果是则返回之前的新对象,如果不是则返回result

1
2
3
4
5
6
7
8
9
10
11
12
//手写new
function myNew(fn,...args) {
// 创建一个控对象
let obj = {};
// 使空对象的隐式原型指向原函数的显式原型
obj.__proto__ = fn.prototype
// this指向obj
let result = fn.apply(obj,args)
// 返回
return result instanceof Object ? result : obj

}

字面量new出来的对象和 Object.create(null)创建出来的对象有什么区别🌟🌟🌟

  • 字面量和new出来的对象会继承Object的属性和方法,他们的隐式原型会指向Object的显示原型
  • Object.create(null)创建出来的对象原型为null,作为原型链的顶端,没有继承Object的属性和方法。

什么是作用域什么是作用域链🌟🌟🌟🌟

  • 规定变量和函数可使用的范围称为作用域
  • 每个函数都有一个作用域,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域到集合称为作用域链

什么是执行栈,什么是执行上下文🌟🌟🌟🌟

执行上下文分为:

  • 全局执行上下文
    创建一个全局的window对象,并规定this执行window,执行js的时候就压入栈底,关闭浏览器的时候才能弹出
  • 函数执行上下文
    每次函数调用时,都会新创建一个函数执行上下文
    执行上下文分为创建阶段和执行阶段
    创建阶段:函数环境会创建变量对象:arguments对象(并赋值)、函数声明(并赋值)、变量声明(不赋值),函数表达式声明(不赋值);会确定this指向;会确定作用域
    执行阶段:变量赋值、函数表达式赋值,使变量对象编程活跃对象
  • eval执行上下文

执行栈:

  • 栈的特点先进后出
  • 当进入一个执行环境,就会创建它的执行上下文,然后进行压栈(进栈),当程序执行完成时,它的执行上下文就会被销毁,进行弹栈(出栈)
  • 栈底永远是全局环境的执行上下文,栈顶永远是正在执行函数的执行上下文
  • 只有浏览器关闭的时候全局执行上下文才会弹出

什么是闭包?闭包的作用?闭包的应用?🌟🌟🌟🌟🌟

函数执行,形成私有的执行上下文,使内部私有变量不受外界干扰,起到保护和保存的作用
关于闭包说法不一 https://zhuanlan.zhihu.com/p/22486908

作用

  • 保护
    避免变量冲突
  • 保存
    解决循环绑定引发的索引问题
  • 变量不会被销毁
    可以使用函数内部的函数,使变量不会被垃圾机制回收

应用

  • 设计模式中的单例模式
  • for循环中保留i的操作
  • 防抖和节流
  • 函数柯里化

缺点
会出现内存泄漏

原型和原型链、如何理解🌟🌟🌟🌟🌟

帮助理解 https://www.jianshu.com/p/dee9f8b14771

原型:
原型分为隐式原型和显式原型,每个对象都有一个隐式原型,它指向自己的构造函数的显式原型

原型链:
多个__proto__组成的集合成为原型链

  • 所有实例的__proto__都指向他们构造函数的prototype
  • 所有的prototype都是对象,自然它的__proto__指向的是Object()的prototype
  • 所有的构造函数的隐式原型指向的都是Function()的显示原型
  • Object的隐式原型是null

js的继承方式有哪些、及优点🌟🌟🌟🌟🌟

原型继承、组合继承、寄生组合继承、es6extend

原型继承

1
2
3
4
5
6
7
8
9
10
11
12
// 把父类的实例作为子类的原型
// 缺点:子类的实例共享了父类构造函数的引用属性 不能传参
var person = {
friends: ["a", "b", "c", "d"]
}

var p1 = Object.create(person)

p1.friends.push("aaa")//缺点:子类的实例共享了父类构造函数的引用属性

console.log(p1);
console.log(person);//缺点:子类的实例共享了父类构造函数的引用属性

组合继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 在子函数中运行父函数,但是要利用call把this改变一下,
// 再在子函数的prototype里面new Father() ,使Father的原型中的方法也得到继承,最后改变Son的原型中的constructor
// 缺点:调用了两次父类的构造函数,造成了不必要的消耗,父类方法可以复用
// 优点可传参,不共享父类引用属性
function Father(name) {
this.name = name
this.hobby = ["篮球", "足球", "乒乓球"]
}

Father.prototype.getName = function () {
console.log(this.name);
}

function Son(name, age) {
Father.call(this, name)
this.age = age
}

Son.prototype = new Father()
Son.prototype.constructor = Son


var s = new Son("ming", 20)

console.log(s);

寄生组合继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Father(name) {
this.name = name
this.hobby = ["篮球", "足球", "乒乓球"]
}

Father.prototype.getName = function () {
console.log(this.name);
}

function Son(name, age) {
Father.call(this, name)
this.age = age
}

Son.prototype = Object.create(Father.prototype)
Son.prototype.constructor = Son

var s2 = new Son("ming", 18)
console.log(s2);

extend继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}

class ColorPoint extends Point {
constructor(x, y, color) {
// this.color = color; // ReferenceError
super(x, y);
this.color = color; // 正确
}
}
let cp = new ColorPoint(25, 8, 'green');
console.log(cp.x);

内存泄漏、垃圾回收机制🌟🌟🌟🌟🌟

什么是内存泄漏:
内存泄漏是指不在用的内存没有释放出来,导致该段内存无法被使用就是内存泄漏
为什么会导致内存泄漏:
内存泄漏指我们无法在通过js访问某个对象,而垃圾回收机制认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该块内存无法被释放,积少成多,系统会越来越卡以至于崩溃
垃圾回收机制都有哪写策略:

  • 标记清除法
    垃圾回收机制获取根并标记他们,然后访问并标记所有来自它的引用,然后再访问这些对象并标记他们的引用。。。如此递进结束后若发现有没有标记的(不可达的)进行删除,进入执行环境的不能删除
  • 引用计数法
    1.当声明一个变量并给该变量赋值一个引用类型的值时候,该值的计数+1,当该值赋值给另一个变量的时候,该计数+1,当该值被其他值取代的时候,该计数-1,当计数变为0的时候,说明无法访问该值了,垃圾回收机制清除该对象
    2.缺点: 当两个对象循环引用的时候,引用计数无计可施。如果循环引用多次执行的话,会造成崩溃等问题。所以后来被标记清除法取代。

深拷贝浅拷贝🌟🌟🌟🌟🌟

帮助理解 https://www.cnblogs.com/dengyao-blogs/p/11466598.html
浅拷贝:
子对象复制父对象,父子对象发生关联,两者属性值指向同一内存空间。简单来讲,就是改变其中一个对象,另一个对象也会跟着改变。

1
2
3
4
let a = [1,2,3];
b = a
a[0] = 4
console.log(a,b) //[4, 2, 3] [4, 2, 3]

深拷贝:
拷贝对象各个层级的属性。简单的讲,就是复制出来的每个对象都有属于自己的内存空间,不会互相干扰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//借用JSON对象的 parse 和 stringify !!
let c = [1,2,3];
d = JSON.parse(JSON.stringify(c))
c[0] = 4;
console.log(c,d); //[4, 2, 3]  [1, 2, 3]


//封装深拷贝函数
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if(obj && typeof obj === "object") {
for(key in obj) {
if(obj.hasOwnProperty(key)) {
// 判断 obj 是否是对象,如果是,递归复制
if(obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
}else{
// 如果不是
objClone[key] = obj[key];
}
}
}
}
return objClone
}

let a = [1,2,3,4],
b = deepClone(a);
a[0] = 5;
console.log(a,b)

hasOwnProperty() 方法详解 https://blog.csdn.net/a791226606/article/details/110679991

为什么js是单线程🌟🌟🌟🌟🌟

因为js里面有可视的DOM,如果是多线程的话,这个线程正在删除DOM节点,另一个线程正在编辑DOM节点,导致浏览器不知道该听谁的。

如何实现异步编程

回调函数

什么是generator

generators 是可以控制 iterator(迭代器)的函数。并在任何时候都可以暂停和恢复。

1
2
3
4
5
6
7
8
9
10
11
12
13
function * generatorForLoop(num) {
for (let i = 0; i < num; i += 1) {
yield console.log(i);
}
}

const genForLoop = generatorForLoop(5);

genForLoop.next(); // 首先 console.log —— 0
genForLoop.next(); // 1
genForLoop.next(); // 2
genForLoop.next(); // 3
genForLoop.next(); // 4

帮助理解 https://zhuanlan.zhihu.com/p/45599048

Generator是怎么样使用的以及各个阶段的变化如何?🌟🌟🌟

  • 首先生成器是一个函数,用来返回迭代器
  • 调用生成器后不会立即执行,而是通过返回的迭代器来控制这个生成器一步一步执行的
  • 通过调用迭代器的next方法来请求一个一个的值,返回的对象有两个属性,一个是value,也就是值,一个是done,是个布尔值类型,done为true说明生成器函数执行完毕,没有可返回的值
  • done为true后,继续调用迭代器的next方法,返回值value为undefined
    状态变化
  • 每当执行到yield属性的时候,都会返回一个对象
  • 这时候生成器处于一个非阻塞的挂起状态
  • 调用迭代器的next方法的时候,生成器又从挂起状态变成执行状态,继续上一次执行位置开始执行
  • 直到遇到下一次yield依次循环
  • 直到代码没有yield了,就会返回一个done为true,value为undefined的对象

说说 Promise 的原理?你是如何理解 Promise 的?🌟🌟🌟🌟🌟

实现原理

说到底,Promise 也还是使用回调函数,只不过是把回调封装在了内部,使用上一直通过 then 方法的链式调用,使得多层的回调嵌套看起来变成了同一层的,书写上以及理解上会更直观和简洁一些。
帮助理解 https://blog.csdn.net/qq_37860963/article/details/81539118

宏任务和微任务有哪些🌟🌟🌟🌟🌟

宏任务:
script,setTimeOut,setInterval,setImmediate
微任务:
Promise.then,process.nextTick,Object.observe,MutationObserve

  • Promise是同步任务

宏任务和微任务是怎么执行的🌟🌟🌟🌟🌟

  • 执行宏任务中的script
  • 进行script后,所有的同步任务主线程执行
  • 所有的宏任务放入宏任务执行队列
  • 所有的微任务放入微任务执行队列
  • 先清空微任务队列
  • 再取一个宏任务执行,再清空微任务队列
  • 依次循环
    例题1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
console.log('4');
new Promise(function(resolve){
console.log('5');
resolve();
}).then(function(){
console.log('6')
});
setTimeout(function(){
console.log('7')
});
function bar(){
console.log('8')
foo()
}
function foo(){
console.log('9')
}
console.log('10')
bar()
//2 4 5 10 8 9 3 6 1 7

解析

  • 首先js浏览器执行js代码从上到下顺序,遇到setTimeout,把setTimeout放到宏任务执行队列
  • new Promise属于主线程任务直接执行 2
  • promise.then下面属于微任务,把then放到微任务的执行队列
  • console.log(‘4’);属于主线程任务直接执行 4
  • 又遇到new Promise属于主线程直接执行 5,下面的then放到微任务执行队列
  • 又遇到setTimeout,把setTimeout放到宏任务执行队列
  • console.log(‘10’)属于主线程任务直接执行 10
  • 遇到bar()函数调用,执行bar函数打印 8 在bar函数中调用foo()执行打印 9
  • 主线程的任务都执行完之后,去执行微任务队列中的任务执行 3, 6
  • 微任务队列中的任务执行完之后,执行宏任务队列中的任务执行1, 7
    例题2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    setTimeout(() => {
console.log('1');
new Promise(function (resolve, reject) {
console.log('2');
setTimeout(() => {
console.log('3');
}, 0);
resolve();
}).then(function () {
console.log('4')
})
}, 0);
console.log('5');
setTimeout(() => {
console.log('6');
}, 0);
new Promise(function (resolve, reject) {
console.log('7');
// reject();
resolve();
}).then(function () {
console.log('8')
}).catch(function () {
console.log('9')
})
console.log('10');
// 5 7 10 8 1 2 4 6 3

变量和函数怎么进行提升的?优先级是怎么样的?🌟🌟🌟🌟

  • 对所有函数声明进行了提升(除函数表达式和箭头函数),引用类型的赋值
    开辟堆空间
    存储内容
    将地址赋给变量
  • 对变量声明进行提升,只声明不赋值,值为undefined

var let const 有什么区别🌟🌟🌟🌟🌟

  • var
    var声明的变量可以进行变量提升,let const没有
    var可以重复声明
    var在飞函数作用域中定义是挂载到window上的
  • let
    let声明的变量在才局部起作用
    let防止变量污染
    不可再声明
  • const
    具有let的所有特征
    不可被改变
    如果const声明的是对象的话,是可以修改对象里面的值的

箭头函数和普通函数的区别?箭头函数可以当做构造函数 new 吗?🌟🌟🌟🌟🌟

  • 箭头函数是普通函数的简写,但是他不具备很多普通函数的特征
  • 1.this指向问题,箭头函数的this指向它定义时所在的对象,而不是调用时所在的对象
  • 2.不会进行函数提升
  • 3.没有arguments对象,不能使用arguments,如果要获取参数的话要使用rest运算符
  • 4.没有yield属性,不能作为生成器gengenerator使用
  • 5.不能new
    没有自己的this,不能调用call和apply
    没有prototype,new关键字内部需要把新对象的__proto__指向函数的prototype

说说你对代理的理解🌟🌟🌟

代理有几种定义方式:

  • 字面量定义,对象里面的get和set
  • 类定义,类里面的get和set
  • Proxy对象,里面传两个对象,第一个对象是目标对象target,第二个对象是专门放get和set的handler对象。Proxy和上面两个的区别在于Proxy专门对对象的属性进行get和set
    代理的实际应用有:
  • Vue的双向绑定vue2用的是Object.definProperty,vue3用的是proxy
  • 校验值
  • 计算属性值(get的时候加以修饰)
    Object.definProperty 帮助理解https://blog.csdn.net/weixin_46726346/article/details/115913752

为什么要使用模块化🌟🌟🌟

  • 防止命名冲突
  • 更好分离按需加载
  • 更好的复用性
  • 更高的维护性

exports和module.exports有什么区别?🌟🌟🌟

  • 导出方式不一样
    exports.xxx = ‘xxx’
    module.export = {}
  • exports是module.exports的引用,两个指向的是用一个地址,而require能看到的只有module.exports

js模块包格式有哪些?🌟🌟🌟

帮助理解 https://www.cnblogs.com/qdwz/p/10718536.html

  • common.js
    同步运行,不适合前端
  • AMD
    异步运行
    异步模块定义,主要采用异步的方式加载模块,模块的加载不影响后面代码的执行。所有依赖这个模块的语句都写在一个回调函数中,模块加载完毕,再执行回调函数
  • CMD
    异步运行
    seajs 规范

ES6和commonjs的区别🌟🌟

  • commonjs模块输出的是值的拷贝,而ES6输出的值是值的引用
  • commonjs是在运行时加载,是一个对象,ES6是在编译时加载,是一个代码块
  • commonjs的this指向当前模块,ES6的this指向undefined

跨域的方式都有哪些?他们的特点是什么 🌟🌟🌟🌟🌟

https://blog.csdn.net/ligang2585116/article/details/73072868?locationNum=13&fps=1

HTTP的结构🌟🌟🌟🌟

  • 请求行 请求头 空行 请求体
    请求行包括 http版本号,url,请求方式
    响应行包括版本号,状态码,原因

说说你知道的状态码🌟🌟🌟🌟🌟

  • 2开头的表示成功
    一般见到的就是200
  • 3开头的表示重定向
    301永久重定向
    302临时重定向
    304表示可以在缓存中取数据(协商缓存)
  • 4开头表示客户端错误
    403跨域
    404请求资源不存在
  • 5开头表示服务端错误
    500

网络OSI七层模型都有哪些?TCP是哪一层的🌟🌟🌟🌟

应用层
表示层
会话层
传输层
网络层
数据链路层
物理层

TCP属于传输层

http1.0和http1.1,还有http2有什么区别?🌟🌟🌟🌟

  • http0.9只能进行get请求
  • http1.0添加了POST,HEAD,OPTION,PUT,DELETE等
  • http1.1增加了长连接keep-alive,增加了host域,而且节约带宽
  • http2

https和http有什么区别,https的实现原理?🌟🌟🌟🌟🌟

  • http无状态无连接,而且是明文传输,不安全
  • https传输内容加密,身份验证,保证数据完整性
  • https实现原理⭐⭐⭐⭐⭐
    首先客户端向服务端发起一个随机值,以及一个加密算法
    服务端收到后返回一个协商好的加密算法,以及另一个随机值
    服务端在发送一个公钥CA
    客户端收到以后先验证CA是否有效,如果无效则报错弹窗,有过有效则进行下一步操作
    客户端使用之前的两个随机值和一个预主密钥组成一个会话密钥,在通过服务端传来的公钥加密把会话密钥发送给服务端
    服务端收到后使用私钥解密,得到两个随机值和预主密钥,然后组装成会话密钥
    客户端在向服务端发起一条信息,这条信息使用会话秘钥加密,用来验证服务端时候能收到加密的信息
    服务端收到信息后返回一个会话秘钥加密的信息
    都收到以后SSL层连接建立成功

localStorage、SessionStorage、cookie、session 之间有什么区别🌟🌟🌟🌟🌟

  • localStorage
    生命周期:关闭浏览器后数据依然保留,除非手动清除,否则一直在
    作用域:相同浏览器的不同标签在同源情况下可以共享localStorage
  • SessionStorage
    生命周期:关闭浏览器或者标签后即失效
    作用域:只在当前标签可用,当前标签的iframe中且同源可以共享
  • cookie
    是保存在客户端的,一般由后端设置值,可以设置过期时间
    储存大小只有4K
    一般用来保存用户的信息的
    在http下cookie是明文传输的,较不安全

cookie属性有:
http-only:不能被客户端更改访问,防止XSS攻击(保证cookie安全性的操作)
Secure:只允许在https下传输
Max-age: cookie生成后失效的秒数
expire: cookie的最长有效时间,若不设置则cookie生命期与会话期相同

  • session
    session是保存在服务端的
    session的运行依赖sessionId,而sessionId又保存在cookie中,所以如果禁用的cookie,session也是不能用的,不过硬要用也可以,可以把sessionId保存在URL中
    session一般用来跟踪用户的状态
    session 的安全性更高,保存在服务端,不过一般为使服务端性能更加,会考虑部分信息保存在cookie中

怎么使用cookie保存用户信息🌟🌟🌟

document.cookie(“名字 = 数据;expire=时间”)
https://bbs.csdn.net/topics/250052356

怎么删除cookie🌟🌟🌟

目前没有提供删除的操作,但是可以把它的Max-age设置为0,也就是立马失效,也就是删除了

get和post的区别🌟🌟🌟🌟🌟

  • 冪等/不冪等(可缓存/不可缓存)
    get请求是冪等的,所以get请求的数据是可以缓存的
    而post请求是不冪等的,查询查询对数据是有副作用的,是不可缓存的
  • 传参
    get传参,参数是在url中的
    准确的说get传参也可以放到body中,只不过不推荐使用
    post传参,参数是在请求体中
    准确的说post传参也可以放到url中,只不过不推荐使用
  • 安全性
    get较不安全
    post较为安全
    准确的说两者都不安全,都是明文传输的,在路过公网的时候都会被访问到,不管是url还是header还是body,都会被访问到,要想做到安全,就需要使用https
  • 参数长度
    get参数长度有限,是较小的
    准确来说,get在url传参的时候是很小的
    post传参长度不受限制
  • 发送数据
    post传参发送两个请求包,一个是请求头,一个是请求体,请求头发送后服务器进行验证,要是验证通过的话就会给客户端发送一个100-continue的状态码,然后就会发送请求体
  • 字符编码
    get在url上传输的时候只允许ASCII编码

在浏览器输入url后发生了什么🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟

https://www.jianshu.com/p/7eea6fbc5fcd
1.浏览器的地址栏输入URL并按下回车。
2.浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
3.DNS解析URL对应的IP。
4.根据IP建立TCP连接(三次握手)。
5.HTTP发起请求。
6.服务器处理请求,浏览器接收HTTP响应。
7.渲染页面,构建DOM树。
8.关闭TCP连接(四次挥手)。

什么是xss?什么是csrf?🌟🌟🌟🌟🌟

  • xss脚本注入
    不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。
    防御
    编码:对用户输入的数据进行HTML Entity 编码。把字符转换成 转义字符。Encode的作用是将$var等一些字符进行转化,使得浏览器在最终输出结果上是一样的。
    过滤:移除用户输入的和事件相关的属性。
  • csrf跨域请求伪造
    在未退出A网站的前提下访问B,B使用A的cookie去访问服务器
    防御:token,每次用户提交表单时需要带上token(伪造者访问不到),如果token不合法,则服务器拒绝请求

什么是回流 什么是重绘?🌟🌟🌟🌟🌟

渲染树(render树)是什么 https://zhuanlan.zhihu.com/p/121807893
什么是回流 什么是重绘和区别https://www.jianshu.com/p/e081f9aa03fb

  • 回流
    render树中一部分或全部元素需要改变尺寸、布局、或着需要隐藏而需要重新构建,这个过程叫做回流
    回流必将引起重绘
  • 重绘
    render树中一部分元素改变,而不影响布局的,只影响外观的,比如颜色。该过程叫做重绘
  • 区别
    回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流
    当页面布局和几何属性改变时就需要回流
    比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变

事件冒泡和事件捕捉有什么区别🌟🌟🌟🌟🌟

  • 事件冒泡
    在addEventListener中的第三属性设置为false(默认)
    从下至上(儿子至祖宗)执行
  • 事件捕获
    在addEventListener中的第三属性设置为true
    从上至下(祖宗到儿子)执行

什么是防抖?什么是节流?手写一个🌟🌟🌟🌟🌟

帮助理解防抖 https://blog.csdn.net/woshidamimi0/article/details/99705915

  • 防抖
    n秒后在执行该事件,若在n秒内被重复触发,则重新计时
  • 节流
    n秒内只运行一次,若在n秒内重复触发,只有一次生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//防抖函数
function debounce(func,delay){
let timeout;
return function() {
let arg = arguments;
if(timeout) clearTimeout(timeout)
timeout = setTimeout (() => {
func(arg)
},delay)
}
}
//立即执行防抖函数
function debounce2(func,delay) {
let timer;
return function() {
let args = arguments;
if(timer) clearTimeout(timer)
let callNow = !timer
timer = setTimeout(() => {
timer = null;
},delay)
if(allNow) { fn(args)}
}
}
//节流 时间戳
function throttle(fn, wait) {
let previous = 0
return function () {
let now = Date.now()
let _this = this
let args = arguments
if (now - previous > wait) {
fn.apply(_this, arguments)
previous = now
}
}
}
//节流 定时器
function throttle2(fn, wait) {
let timer
return function () {
let _this = this
let args = arguments
if (!timer) {
timer = setTimeout(() => {
timer = null
fn.apply(_this, arguments)
}, wait);
}
}
}

函数柯里化原理🌟🌟🌟🌟🌟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function add() {
var args = Array.prototype.slice.call(arguments)

var adder = function () {
args.push(...arguments)
return adder
}

adder.toString = function () {
return args.reduce((prev, curr) => {
return prev + curr
}, 0)
}

return adder
}

let a = add(1, 2, 3)
let b = add(1)(2)(3)
console.log(a)
console.log(b)
console.log(add(1, 2)(3));
console.log(Function.toString)

什么是requestAnimationFrame?🌟🌟🌟🌟

帮助理解 https://www.jianshu.com/p/fa5512dfb4f5
requestAnimationFrame请求数据帧可以用做动画执行
可以自己决定什么时机调用该回调函数
能保证每次频幕刷新的时候只被执行一次
页面被隐藏或者最小化的时候暂停执行,返回窗口继续执行,有效节省CPU

js常见的设计模式🌟🌟🌟🌟🌟

单例模式、工厂模式、构造函数模式、发布订阅者模式、迭代器模式、代理模式、观察者模式
工厂模式和构造函数模式的异同 https://blog.csdn.net/liumuye88888/article/details/115561044
观察者模式和订阅者模式理解 https://zhuanlan.zhihu.com/p/351750593

  • 单例模式
    保证一个类仅有一个实例,并提供一个访问它的全局访问点。

帮助理解单例模式 https://zhuanlan.zhihu.com/p/34754447 知乎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//惰性单例
let timeTool = (function() {
let _instance = null;

function init() {
//私有变量
let now = new Date();
//公用属性和方法
this.name = '处理时间工具库',
this.getISODate = function() {
return now.toISOString();
}
this.getUTCDate = function() {
return now.toUTCString();
}
}

return function() {
if(!_instance) {
_instance = new init();
}
return _instance;
}
})()
let instance1 = timeTool();
let instance2 = timeTool();
console.log(instance1 === instance2); //true

上面的timeTool实际是一个函数,——instance作为实例对象最开始赋值为null,init函数是其构造函数,用于实例化对象,立即执行函数返回的是匿名函数用于判断实例是否创建,只有当调用timeTool()时进行实例化,这就是惰性单例的应用,不在js加载时就进行实例化创建,而是在需要的时候再进行单例的创建。如果再次调用,那么返回的永远是第一次实例化后的实例对象

  • 工厂模式
    代替new创建一个对象,且这个对象像工厂制作一样,批量制作属性相同的实例对象(指向不同)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function createFactory(name,age,gender){
var obj = new Object()
obj.name = name
obj.age = age
obj.gender = gender
obj.sayName = function(){
alert(this.name)
}
return obj

}
var obj2 = createFactory("小明",18,"男")
var obj3 = createFactory("小红",12,"女")
var obj4 = createFactory("张三",16,"男")
console.log(obj2);
console.log(obj3);
console.log(obj4);

JS性能优化的方式🌟🌟🌟🌟🌟

避免使用全局变量
图片优化(雪碧图)
懒加载
垃圾回收
闭包中的对象清除
防抖节流
分批加载(setInterval,加载10000个节点)
事件委托
少用with
requestAnimationFrame的使用
script标签中的defer和async
CDN

vue数据劫持

帮助理解 https://yuchengkai.cn/docs/frontend/framework.html#mvvm
vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调

阐述一下你所理解的MVVM响应式原理🌟🌟🌟🌟🌟

Vue 采用数据劫持结合发布者-订阅者模式的方式通过 Object.defineProperty() 劫持并监听各个属性的 setter 和 getter,在数据变动时,发布消息给依赖收集器,通知观察者,调用相应的回调函数,进而更新视图
MVVM 作为入口函数,整合 Observer、Compile、Watcher 三者,通过 Observer 监听数据变化,通过 Compile 解析编译模版指令,通过 Watcher 连接 Observer 和 Compile ,达到数据更新时视图更新,视图交互更新时数据更新的双向绑定效果

订阅器(dep):通知订阅者更新视图,存放多个订阅器
订阅者(watcher):更新视图
observer:劫持属性
compile:解析指令
原文链接:https://www.cnblogs.com/veinyin/p/12640058.html

vue生命周期🌟🌟🌟🌟🌟

vue生命周期

  • beforeCreate
    创建之前还没有data和model
  • created
    创建完成,此时data和method可以使用了
    在created之后beforeMount之前如果没有el选项的话那么此时生命周期结束,停止编译,如果有则继续
  • beforeMount
    在渲染之前
  • mounted
    页面已经渲染完成,并且vm实例已经添加完成$el,已经替换掉哪些dom元素(双括号中的变量),这个时候可以操作DOM了(但是是获取不了元素的高度等属性的,如果想要获取,需要使用nextTick()(仅在整个视图都被渲染之后才会运行的代码))
  • beforeUpdate
    data改变后,对应的组件重新渲染之前
  • updated
    data改变后,对应的组件渲染完成
  • activated
    被 keep-alive 缓存的组件激活时调用。
    该钩子在服务器端渲染期间不被调用。
  • beforeDestory
    在实例销毁之前,此实例仍然可以使用
  • destoryed
    实例销毁后

vue-router的模式🌟🌟🌟🌟🌟

Vue-router 中hash模式和history模式的区别 https://zhuanlan.zhihu.com/p/337073166

  • hash模式
    监听hashchange事件实现前端路由,利用url中的hash来模拟一个hash,以保证url改变时,页面不会重新加载。
  • history模式
    利用pushstate和replacestate来将url替换但不刷新,但是有一个致命点就是,一旦刷新的话,就会可能404,因为没有当前的真正路径,要想解决这一问题需要后端配合,将不存在的路径重定向到入口文件。

什么是diff算法🌟🌟🌟🌟🌟

diff算法是指新旧虚拟dom节点进行对比,并返回一个patch对象,用来存储两个节点不同的地方,最后利用patch记录的消息局部更新dom
diff

虚拟dom的优缺点🌟🌟🌟🌟🌟

  • 缺点
    首次渲染大量dom时,由于多了一层虚拟dom的计算,会比innerHtml插入慢
  • 优点
    减少dom操作,减少了回流与重绘
    能跨平台渲染:因为虚拟DOM本质只是一个JS对象,所以虚拟DOM不仅可以改变DOM,还可以变成小程序,iOS应用,安卓应用等
    保证了性能的下限,虽然性能不是最佳,但是它具备局部更新的能力,所以大部分的时候还是比正常的dom性能好很多

Vue的Key的作用🌟🌟🌟🌟🌟

key主要是用于虚拟dom算法中,每个虚拟节点有一个唯一标识key,通过对比新旧节点的key来判断节点是否改变,用key就可以大大提高渲染效率,这个key类似于缓存中的etag。
key

Vue组件之间的通信方式🌟🌟🌟🌟🌟

组件通信大全 https://www.cnblogs.com/fundebug/p/10884896.html

  • 父传子
    子组件通过props接受父组件传递的值
  • 子传父
    子组件$emit(‘事件名’,值),父组件v-on/@接收
  • 任意组件通信,新建一个空的全局Vue对象,利用emit发送,emit发送,on接收
1
2
3
Vue.prototype.Event=new Vue();
Event.$emit(事件名,数据);
Event.$on(事件名,data => {});

vuex也可以传值

Vue-router有哪几种钩子函数

参考 https://www.jianshu.com/p/ddcb7ba28c5e
https://www.zhihu.com/search?type=content&q=路由钩子函数

  • 全局钩子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
```afterEach进```入之后触发
beforeResolve
每个守卫方法接收三个参数:
①to: Route: 即将要进入的目标路由对象(to是一个对象,是将要进入的路由对象,可以用to.path调用路由对象中的属性)
②from: Route: 当前导航正要离开的路由
③next: Function: 这是一个必须需要调用的方法,执行效果依赖 next 方法的调用参数
- 单个路由里面的钩子
```beforeEnter```写在路由配置中,只有访问到这个路径,才能触发钩子函数
- 组件路由
1.`beforeRouteEnter`
在渲染该组件的对应路由被 confirm 前调用
不!能!获取组件实例 `this`
因为当钩子执行前,组件实例还没被创建
2.`beforeRouteUpdate`
在当前路由改变,但是该组件被复用时调用
举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
可以访问组件实例 `this`
3.`beforeRouteLeave`
导航离开该组件的对应路由时调用
可以访问组件实例 `this`
## webpack常用的几个对象及解释🌟🌟🌟🌟
- entry(嗯锤) 入口文件
- output(奥t铺t)输出文件
一般配合node的path模块使用
```javaScript
// 入口文件
entry:"./src/index.js",
output:{
// 输出文件名称
filename:"bundle.js",
// 输出的路径(绝对路径)
path:path.resolve(__dirname,"dist") //利用node模块的path 绝对路径
},
// 设置模式
mode:"development"
  • mode设计模式
  • module(loader)
    里面有一个rules数组对某种格式的文件进行转换处理(转换规则)
    use数组解析顺序是从下到上逆序执行的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module:{
// 对某种格式的文件进行转换处理(转换规则)
rules:[
{
// 用到正则表达式
test:/\.css$/, //后缀名为css格式的文件
use:[
// use数组解析顺序是从下到上逆序执行的
// 先用css-loader 再用style-loader
// 将js的样式内容插入到style标签里
"style-loader",
// 将css文件转换为js
"css-loader"
]
}
]
}

// -----------------------------------------------------vue的
module.exports={
module:{
rules:[
{
test: /\.vue$/,
use:["vue-loader"]
}
]
}
}
  • plugin(铺类根)插件配置
1
2
3
4
5
6
7
const uglifyJsPlugin = reqiure('uglifyjs-webpack-plugin')

module.exports={
plugin:[
new uglifyJsPlugin() //丑化
]
}
  • devServer(热更新)
1
2
3
4
5
6
7
8
9
10
11
12
devServer:{
// 项目构建路径
contentBase:path.resolve(__dirname,"dist"),
// 启动gzip亚索
compress:true,
// 设置端口号
port:2020,
// 自动打开浏览器:否
open:false,
//页面实时刷新(实时监听)
inline:true
}
  • resolve(配置路径规则)
    alias 别名
1
2
3
4
5
6
7
8
9
10
11
module.exports= {
resolve:{
//如果导入的时候不想写后缀名可以在resolve中定义extensions
extensions:['.js','.css','.vue']
//alias:别名
alias:{
//导入以vue结尾的文件时,会去寻找vue.esm.js文件
'vue$':"vue/dist/vue.esm.js"
}
}
}
  • babel(ES6转ES5)
    下载插件babel-loader,在module(loader)中配置

loader和plugin的区别是什么?🌟🌟🌟

  • loader
    loader是用来解析非js文件的,因为Webpack原生只能解析js文件,如果想把那些文件一并打包的话,就需要用到loader,loader使webpack具有了解析非js文件的能力
  • plugin
    用来给webpack扩展功能的,可以加载许多插件

flext布局🌟🌟🌟🌟🌟

阮一峰 http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html

grid布局🌟🌟🌟🌟

https://www.cnblogs.com/cythia/p/10894598.html

first-of-type和first-child有什么区别🌟🌟🌟🌟

https://www.cnblogs.com/2050/p/3569509.html

  • first-of-type
    匹配的是从第一个子元素开始数,匹配到的那个的第一个元素
  • first-child
    必须是第一个元素

doctype标签和meta标签🌟🌟🌟🌟🌟

  • doctype
    告诉浏览器以什么样的文档规范解析文档
  • meta
    最简单的理解就是设置这个页面的编码格式,不然会出现中英文乱码

script标签中defer和async都表示了什么🌟🌟🌟🌟🌟

帮助理解 https://www.cnblogs.com/jiasm/p/7683930.html
众所周知script会阻塞页面的加载,如果我们要是引用外部js,假如这个外部js请求很久的话就难免出现空白页问题,好在官方为我们提供了defer和async

  • defer
1
2
<script src="d.js" defer></script>
<script src="e.js" defer></script>

不会阻止页面解析,并行下载对应的js文件
下载完之后不会执行
等所有其他脚本加载完之后,在DOMContentLoaded事件之前执行对应d.js、e.js

  • async
1
2
<script src="b.js" async></script>
<script src="c.js" async></script>

不会阻止DOM解析,并下载对应的js文件
下载完之后立即执行

  • 补充,DOMContentLoaded事件
    是等HTML文档完全加载完和解析完之后运行的事件
    在load事件之前
    不用等样式表、图像等完成加载

什么是BFC🌟🌟🌟🌟🌟

帮助理解 https://zhuanlan.zhihu.com/p/184905483

  • BFC是一个独立渲染区域,它丝毫不会影响到外部元素
  • BFC特性
    同一个BFC下的margin会重叠
    计算BFC高度时会算上浮动元素
    BFC不会影响到外部元素
    BFC内部元素是垂直排列的
    BFC区域不会与float元素重叠
  • 如何创建BFC
    position设为absolute或者fixed
    float不为none
    overflow设置为hidden
    display设置为inline-block或者inline-table或flex

如何清除浮动🌟🌟🌟🌟🌟

  • 1.额外标签clear:both
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.fahter{
width: 400px;
border: 1px solid deeppink;
}
.big{
width: 200px;
height: 200px;
background: darkorange;
float: left;
}
.small{
width: 120px;
height: 120px;
background: darkmagenta;
float: left;
}

.clear{
clear:both;
}
</style>
</head>
<body>
<div class="fahter">
<div class="big">big</div>
<div class="small">small</div>
<div class="clear">额外标签法</div>
</div>
</body>

  • 2.利用BFC
    overflow:hidden || auto
1
2
3
4
5
.fahter{
width: 400px;
border: 1px solid deeppink;
overflow: hidden;
}
  • 3.给前面的父元素设置高度,不推荐使用
  • 4.使用after(推荐)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  <style>
.clearfix:after{/*伪元素是行内元素 正常浏览器清除浮动方法*/
content: "";
display: block;
height: 0;
clear:both;
visibility: hidden;
}
.clearfix{
*zoom: 1;/*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行*/
}
/*ie6-7不支持伪元素:after,使用zoom:1触发hasLayout.*/
</style>
<body>
<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>
<!--<div class="clear">额外标签法</div>-->
</div>

  • 5.使用before和after双伪元素清除浮动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    .clearfix:after,.clearfix:before{
content: "";
display: table;
}
.clearfix:after{
clear: both;
}
.clearfix{
*zoom: 1;
}

<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>
</div>
<div class="footer"></div>

垂直水平居中的几种方法🌟🌟🌟🌟🌟

  • 1.第一种方法
    子元素绝对定位,上下左右为0,margin:auto;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!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>
*{
margin: 0;
padding: 0;
}
body,
html{
width: 100%;
height: 100%;
}
#demo{
width: 200px;
height: 200px;
border: 1px solid red;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<div id="demo"></div>
</body>
</html>
  • 2.弹性布局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!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>
*{
margin: 0;
padding: 0;
}
body,
html{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#demo{
width: 200px;
height: 200px;
border: 1px solid red;
}
</style>
</head>
<body>
<div id="demo"></div>
</body>
</html>
  • 3.定位 + margin-top + margin-left
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!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>
*{
margin: 0;
padding: 0;
}
body,
html{
width: 100%;
height: 100%;
}
#demo{
width: 200px;
height: 200px;
border: 1px solid red;
position: relative;
}
#demoSon{
width: 20px;
height: 20px;
position: absolute;
border: 1px solid blue;
top: 50%;
left: 50%;
margin-top: -10px;
margin-left: -10px;


}
</style>
</head>
<body>
<div id="demo">
<div id="demoSon"></div>
</div>
</body>
</html>

设置父元素的position为相对定位,子元素绝对定位,并在 top 和 left 方向上移动父元素50%的距离。
但这个时候,是子元素的上边框和左边框距离父元素150px,整体向右下角偏了一些,所以还需要再用 margin 调整至中心位置,数值分别是高度和宽度的一半。

  • 4.display: table-cell 无兼容性问题(文字居中)
1
2
3
4
5
6
7
8
9
.box {
position: relative;
width: 300px;
height: 300px;
border: 1px solid red;
display: table-cell;
text-align: center;
vertical-align: middle;
}

只需要设置父元素即可,text-align: center; 并在竖直方向上令内容居中(middle),早期属性,不存在兼容问题。

什么是DOM事件流?什么是事件委托🌟🌟🌟🌟🌟

  • DOM时间流分成三个阶段
    捕获阶段
    目标阶段
    冒泡阶段
    在addeventListener()的第三个参数(useCapture)设为true,就会在捕获阶段运行,默认是false冒泡
  • 事件委托
    利用冒泡原理(子向父一层层穿透),把事件绑定到父元素中,以实现事件委托

算法

排序算法动画网站 https://math.hws.edu/eck/js/sorting/xSortLab.html

  • 1.冒泡排序🌟🌟🌟🌟🌟
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function bubbleSort(arr){
for(var i = 0;i<arr.length;i++){
for(var j = 0;j<arr.length;j++){
if(arr[j] > arr[j+1]){
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
// [arr[j],arr[j+1]] = [arr[j+1],arr[j]] es6结构赋值
}
}
}
}
var Arr = [2,3,99,22,44,1,34,13,55];
bubbleSort(Arr);
  • 2.快速排序🌟🌟🌟🌟🌟
    从数组中选择一个元素作为基准点
    排序数组,所有比基准值小的元素摆放在左边,而大于基准值的摆放在右边。每次分割结束以后基准值会插入到中间去。
    最后利用递归,将摆放在左边的数组和右边的数组在进行一次上述的1和2操作。

帮助理解 https://segmentfault.com/a/1190000017814119

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function quickSort(arr){
if(arr.lenght <= 1){
return arr
}
var middle = Math.floor(arr.length/2);
var middleData = arr.splice(middle,1)[0];//基准值
var left = [];
var right = [];
for(var i=0;i<arr.length;i++){
if(arr[i]<middleData){
left.push(arr[i])
}else{
right.push(arr[i])
}
}
return quickSort(left).concat([moddleData],quickSort(right))
}

var Arr = [1,3,0,13,11,44,33,22,10];
var newArr = quickSort(Arr)
  • 3.插入排序🌟🌟🌟🌟
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function insertSort(arr){
// 默认从索引1开始
for(var i = 1;i<arr.length;i++){
if(arr[i] < arr[i -1]){
var el = arr[i];
arr[i] = arr[i - 1];
var j = j -1
while(j >=0 && arr[j] > el){
arr[j + 1] = arr[j]
j--
}
arr[j+1] = el
}
}
};

var Arr = [9,8,7,6];
insertSort(Arr)
  • 4.是否回文🌟🌟🌟🌟🌟
1
2
3
4
5
function isHuiWen(str){
return str == str.split('').reverse().join('')
}
var str = 'nun';
isHuiWen(str)
  • 5.斐波那契数列🌟🌟🌟🌟🌟
1
2
3
4
5
6
7
8
9
10
// num1前一项
// num2当前项
function fb(n, num1 = 1, num2 = 1) {
if(n == 0) return 0
if (n <= 2) {
return num2
} else {
return fb(n - 1, num2, num1 + num2)
}
}

git 🌟🌟🌟🌟🌟

https://blog.csdn.net/qq_36095679/article/details/91804051


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !