这是一个入门前端不久的小白对前端一些基本知识的总结
(部分借鉴了来自各个社区大佬的总结)

Html

重绘与回流

回流: 当我们删除或修改元素高度时,页面会重新布局,DOM树发生变化,引起渲染树重新渲染,这个过程叫做回流(回流一定造成重绘)
重绘: 当修改元素的颜色,渲染树会根据新规则重新渲染,这个过程叫做重绘(重绘不一定造成回流)

如何减少回流

  1. 对DOM进行多次添加删除操作时,使用documentfragment对象(在该对象中对DOM进行操作,完成后append到文档中,即可只进行一次回流)
  2. 使用定位脱离文档流改变位置
  3. 避免逐项更改样式,将样式列表定义为class并一次性更改class属性
  4. 避免循环读取offsetLeft等属性,在循环之前把它们缓存起来。

Css

1. css布局,实现顶部高固定,左侧导航宽固定,右侧自适应

掘金讲解

2. 三大定位,相对定位放在固定定位产生什么影响?

fixed、relative、absolute

3. 伪类和伪元素

伪类:向某些选择器设置特殊效果,用于选择器选择不到的元素
伪元素:向某些选择器添加特殊效果

4. 纯css画三角形(基本思路就是border撑开容器)

5. BFC

BFC(Block formatting context)直译为”块级格式化上下文”。它是一个独立的渲染区域,只有Block-level box参与(在下面有解释), 它规定了内部的Block-level Box如 何布局,并且与这个区域外部毫不相干。
我们常说的文档流其实分为定位流、浮动流和普通流三种。而普通流其实就是指BFC中的FC。

触发条件或者说哪些元素会生成BFC:

  满足下列条件之一就可触发BFC
  1. 根元素,即HTML元素
  2. float的值不为none
  3. overflow的值不为visible
  4. display的值为inline-block、table-cell、table-caption(后两个目前没怎么用)
  5. position的值为absolute或fixed

BFC布局规则:

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

BFC有哪些作用:

1.自适应两栏布局
2.可以阻止元素被浮动元素覆盖
3.可以包含浮动元素——清除内部浮动
4.分属于不同的BFC时可以阻止margin重叠

6. 清除浮动的方式

  1. claer:both
  2. 父元素BFC(上面已经提到BFC不会影响到外面的元素)

7. 水平垂直居中的实现方式

方法一、定位 + transform

.parent{
position: relative;
height: 500px;
width: 500px;
border: 1px solid red;
}
.child{
position: absolute;
left: 50%;
top: 50%;
height: 80px;
width: 80px;
border:1px solid black;
transform: translate(-50%, -50%);
}

方法二、margin + transform

.parent{
height: 500px;
width: 500px;
border: 1px solid red;
}
.child{
height: 80px;
width: 80px;
background-color: #515A6E;
margin: 50% 0 0 50%;
transform: translate(-50%, -50%);
}

方法三、flex

.parent{
height: 500px;
width: 500px;
border: 1px solid red;
display: flex;
align-items: center;
justify-content: center;
}
.child{
height: 80px;
width: 80px;
background-color: #abcdef;
}

方法四、table-cell

.parent{
height: 500px;
width: 500px;
border: 1px solid red;
display: table-cell;
text-align: center;
vertical-align: middle;
}
.child{
display: inline-block;
height: 80px;
width: 80px;
background-color: #515A6E;
}

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

块级元素

独占一行,在默认情况下,其宽度自动填满其父元素的宽度
块级元素可以设置width、height属性
块级元素即使设置了宽度也是独占一行,块级元素可以设置margin、padding属性

行内元素

行内元素不会独占一行,相邻的行内元素会排列在同一行里,直到行排不下,就自动换行,其宽度随内容而变化
行内元素的width、height属性则无效
水平方向的padding、margin会产生边距效果,竖直方向的padding、margin不会产生边距效果
对于img我们可以给他包裹一个父级盒子给其设置大小

行内置换元素

img、input、textarea、select、object属于行内置换元素, 具有块级元素的特征(除宽度外)

Javascript

1.js找出数组中出现最多的元素和次数

function findMost (arr) {
if (!arr.length) return;
if (arr.length === 1) return 1;
let res = {};
let maxName, maxNum = 0
// 遍历数组
arr.forEach((item) => {
res[item] ? res[item] += 1 : res[item] = 1
if (res[item] > maxNum) {
maxName = item
maxNum = res[item]
}
})
return ‘出现次数最多的元素为:’ + maxName + ‘, 出现次数为:’ + maxNum;
}

2. 100阶台阶一次走1步或2步有多少种走法

function test (num) {
let num1 = 1, num2 = 2, res = 0
if(num===1)
return 1;
if(num===2)
return 2;
for (let i=2; i<num; i++) {
res = num1 + num2;
num1 = num2;
num2 = res;
}
return res;
}

3. 原生js实现抽奖转盘

4. 闭包及使用场景

闭包
  1. 外部函数能够访问内部函数的变量
  2. 闭包所在的函数变量不能被垃圾回收机制回收
  3. 常驻内存,占用内存使用量
使用场景
  1. 给对象设置私有变量并且利用特权方法去访问私有属性
  2. 采用函数引用方式的setTimeout调用
  3. 封装相关功能集

5. 原型、原型链

原型对象也是普通的对象,是对象一个自带隐式的__proto__属性,原型也有可能有自己的原型,如果一个原型对象的原型不为null的话,我们就称之为原型链。
原型链是由一些用来继承和共享属性的对象组成的(有限的)对象链。

6. 类的创建与继承

创建:new 一个Function,在prototype中添加属性和方法
继承:

  1. 原型链继承,基于原型链,是父类和子类的实例,无法实现多继承
  2. 构造函数继承:复制父类的实例给子类,只继承父类实例的属性与方法,不继承原型上的,可实现多继承
  3. 组合继承(原型链继承+构造函数继承)

ES6中可以用class 定义类
class demo {
constructor(name,age){
this.name=name;
this.age=age;
}
show(){
console.log(this.name);
}
}
但是只能用new demo(‘demo1’,18)创建实例对象

7. new做了什么事

创建一个空对象
将该对象的原型指向创建该对象的构造函数的prototype上
例如
function Cat(){}
var demoCat = new Cat();
demoCat.__proto__ == Cat.prototype //ture

8.创建对象的方式

  1. 工厂模式
  2. 构造函数模式
  3. 原型模式
  4. 混合构造函数与原型模式

9. 深浅拷贝

JavaScript有几种基本类型:undefined null boolean number string symbol
(null受争议,因为typeof null值为object,但我先归为基本类型)
其他的都为引用类型
基本类型存放在栈内存。还含有一种复杂的数据类型(也叫引用类型)存放在堆内存,就是对象(Object,Array)。 堆内存用于存放由new创建的对象,栈内存存放一些基本的类型的变量和对象的引用变量。

深拷贝
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
const isType = function(type){
return function(obj){
return Object.prototype.toString.call(obj)=='[object '+type+']';
}
};
let isArray =isType('Array');//判断是否为Array类型的函数
let isBoolean = isType('Boolean');//判断是否为Boolean类型的函数
let isFunction = isType('Function');//判断是否为Function类型的函数
let isNumber = isType('Number');//判断是否为Number类型的函数
let isObject = isType('Object');//判断是否为对象类型的函数
let isString = isType('String');//判断是否为String类型的函数
let isSymbol = isType('Symbol');//判断是否为Symbol类型的函数
//上面的函数封装了各种类型检查,需要用的时候很方便,而且很准确
function deepClone(source){
if(isBoolean(source) || isNumber(source) || isString(source) || isSymbol(source)){
return source;
}else if(isArray(source)){
var _arr =[];
for(var i=0;i<source.length ;i++){
_arr.push(deepClone(source[i]));
}
return _arr;
}else if(isObject(source)){
var _obj ={};
for(var k in source){
console.log(source[k]);
_obj[k]=deepClone(source[k]);
}
return _obj;
}
}

10. 数组操作

Array操作方法
  1. unshift() 向数组开头添加一个或多个元素,并返回新的长度 改变原数组
  2. shift() 删除数组第一个元素,并返回删除的元素本身 如果数组为空,则不修改素组,返回undefined 改变原数组
  3. push() 向数组末尾添加一个或多个元素,并返回新长度 改变原数组
  4. pop() 删除数组最后一个元素,并返回删除元素本身 如果数组为空,则不修改数组,并返回undefined 改变原素组
  5. concat()合并两个或多个数组,返回合并后的数组 不改变原数组
  6. splice()删除数组指定位置长度的元素,或添加一个、多个元素,返回删除元素 添加元素可选 改变原数组
  7. slice(start,end)截取数组指定开始和结束下边的元素,并返回结果元素数组 end可选 不改变原数组
  8. sort()对数组进行排序,返回排序后的数组 改变原数组 参数必须是函数例如比较数字大小
    var arr=[1,10,9,4,11,100]
    arr.sort((a,b)=>a-b);// [1, 4, 9, 10, 11, 100]
  9. reverse() 反转数组,返回反转后的数组 改变原数组
  10. join()以指定字符分割数组,返回分割后的字符串 若参数不填,则默认以’,’为分割符 不改变原数组
Array的遍历方法
  1. forEach()
    array.forEach(function(currentValue, index, arr), thisValue)
    遍历数组每一个元素,并在回调函数中处理 不改变原数组
  2. map()
    array.map(function(currentValue, index, arr), thisValue)
    遍历数组每一项,并返回操作后的数组 不改变原数组 thisValue绑定回调函数的this上下文
  3. filter()
    array.filter(function(currentValue,index,arr), thisValue)
    遍历数组每一项,并在回调中处理,返回符合条件的新数组 不改变原数组
  4. every()
    遍历数组每一项,通过回调判断是否符合条件 若有一项不符合,则返回false,不继续执行;若全部符合则返回true 不改变原数组
如何判断是否为Array
  1. isArray()
  2. instanceof
    判断被检测对象是非为构造函数的实例
    原理:左侧被检测对象的原型链上是否包含右侧构造函数的prototype属性
  3. constructor
    constructor 属性返回对象的构造函数, 数组对象的构造函数为Array
不同对象的constructor

console.log([].constructor) // ƒ Array() { [native code] }
console.log({}.constructor) // ƒ Object() { [native code] }
console.log(f.constructor) // ƒ Function() { [native code] }
console.log((123).constructor) // ƒ Number() { [native code] }
console.log(‘123’.constructor) // ƒ String() { [native code] }
console.log(true.constructor) // ƒ Boolean() { [native code] }

11.快速排序(二分排序)

//会改变原数组,如果不想改变原数组可以使用深克隆
function quickSort(arr){
if(arr.length<=1){
return arr;
}
let pivotIndex = Math.floor(arr.length / 2) ;
let pivot = arr.splice(pivotIndex, 1)[0];
let left = [];
  let right = [];
arr.forEach((value,index)=>{
if(arr[index]>pivot){
right.push(arr[index])
}else{
 left.push(arr[index]);
}
})
return quickSort(left).concat([pivot], quickSort(right));
}

  1. 函数的防抖与节流
    防抖: 任务频繁触发情况下,只有两次任务间隔超过指定时间,才会执行。若还未超过却又一次触发,则时间重新开始计算
    节流: 每隔一段时间,只执行一次函数。
防抖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function debounce(fn, delay) {
var timer; // 维护一个 timer
return function () {
var _this = this; // 取debounce执行作用域的this
var args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(_this, args); // 用apply指向调用debounce的对象,相当于_this.fn(args);
}, delay);
};
}

function testDebounce( content) {
console.log(content);
}
var testDebounceFn = debounce(testDebounce, 1000); // 防抖函数
document.onmousemove = function () {
testDebounceFn('debounce'); // 给防抖函数传参
}
节流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function throttle(fn, delay) {
var timer;
return function () {
var _this = this;
var args = arguments;
if (timer) {
return;
}
timer = setTimeout(function () {
fn.apply(_this, args);
timer = null; // 在delay后执行完fn之后清空timer,此时timer为假,throttle触发可以进入计时器
}, delay)
}
}
function testThrottle(content) {
console.log(content);
}
var testThrottleFn = throttle(testThrottle, 3000); // 节流函数
document.onmousemove = function () {
testThrottleFn('throttle'); // 给节流函数传参
}

ES6

Promise问题

promise promise是异步编程的解决方案,比起传统的异步解决方案“回调函数”、“事件”更合理强大,已被ES6纳入规范
具有三种状态:pending 过渡态、fulfilled 完成态、rejected 失败态,状态一旦确定不可修改

Promise优缺点

优点

解决回调地域
可读性高,便于维护

缺点

无法停止,一旦创建Promise立即执行
必须指定回调,否则内部抛出异常
当处于pending状态时,无法确定此时进行到哪一阶段(刚开始还是即将完成)

Promise方法

Promise.prototype.then()

Promise 实例添加状态改变时的回调函数,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
then方法返回的是一个新的Promise实例(不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

Promise.prototype.catch()

.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

Promise.prototype.finally()

无论Promise最后状态如何,都会执行finally内的函数

promise.all()

多个Promise同时执行,若全部成功,则以数组形式返回执行结果;若有一个是rejected,则只返回rejected的结果

Promise.race()

多个Promise同时执行,返回最先结束Promise执行任务的结果,无论成功或失败

Promise.resolve()

返回一个新的 Promise 实例,该实例的状态为resolved

Promise.reject()

返回一个新的 Promise 实例,该实例的状态为rejected

手写Promise对象 (搜索promise即可看到)