Skip to content

Latest commit

 

History

History
448 lines (417 loc) · 13.5 KB

待整理.md

File metadata and controls

448 lines (417 loc) · 13.5 KB

CSS

  • 左右居中 text-align:center / margin: 0 auto / position: absotion; margin-top: -100px;
  • 垂直居中 display: table-cell;vertical-align: middle / position: absotion; margin-top: -100px; / position: absolute;top: 0;bottom: 0;margin: auto;
  • flex居中 display: flex;align-items:center;justify-content:center;

CSS盒模型

  • 一个盒子中主要的属性就5个:width、height、padding、border、margin
  • 两种类型
    • IE盒模型,IE6之后就废弃了,width、height部分包含padding和border,对应box-sizing: border-box
    • 标准盒模型,width、height部分不包含padding和border,对应box-sizing: content-box

简单类型 复杂类型

简单类型:number string  boolean symbol

复杂类型:object(null undefined array function)

简单类型是存储于栈中

复杂类型则是在栈中存储一个地址,然后将数据存储在堆中。然后通过栈中的地址去找到堆中的数据,他们之间存在一个引用(虚拟的)

存储数据分配了内存,那么就有必要再这些数据没用的时候进行回收,JS里面有GC 垃圾回收机制。GC垃圾回收 :如果一个对象没有被引用,它就是垃圾,将会被回收。

js原型链的理解

Alt text

fn = new Foo()
fn.constructor = Foo
fn._proto_ = Foo.prototype
Foo.prototype.constructor = Object
Foo.prototype._proto_ = Object.prototype
Object.prototype._proto_ = null
// 所以fn通过_proto_指向Foo.prototype
// Foo.prototype通过_proto_只想Object.prototype
// 因为Object.prototype有toString方法,所以fn也有toString方法
// 这个就是js继承的实现基础

js类型转换

关系操作符(<, >, <=, >=)

// 如果两个操作值都是数值,则进行数值比较
console.log(12 > 11) // true
// 如果两个操作值都是数值字符串或者数字,则都转换为数值进行比较
console.log('12' > 11) // true
// 如果两个操作值都是非数字字符串,则比较字符串对应的字符编码值
console.log('a' > 'b') // false
// 如果只有一个操作值是非数字字符串,另一个操作值是数值,非数字字符串转为NaN
// NaN是非常特殊的值,它不和任何类型的值相等,包括它自己,同时它与任何类型的值比较大小时都返回false。
console.log('a' > 11) // false
// 如果一个操作值是布尔值,则将其转换为数值,true是1 false是0
console.log(true < 2) // true

关系操作符=

// 如果一个操作值为布尔值,则在比较之前先将其转换为数值
console.log(true == 1) // true
// 如果一个操作值为数值字符串,另一个操作值为数值,则通过Number()将字符串转换为数值
console.log('5' == 5) // true
// 如果一个操作值为非数值字符串,另一个操作值为数值,则通过Number()将非数字字符串转换NaN 永远是false
console.log('a' == 6) // false
// 如果一个操作值是对象,另一个不是,则调用对象的toString()方法,得到的结果按照前面的规则进行比较
// 如果两个操作值都是对象,则比较它们是不是指向同一个对象
const a = [1, 2, 3]
const b = '1,2,3'
const c = [1, 2, 3]
console.log(a == b) // a通过toString转换为1,2,3 所以为true
console.log(a == c) // 两者都是对象 不指向同一个对象 所以为false
// [1]通过toString转换为'1' '1'转化后为数字1 所以为true
console.log([1] == 1) // true
console.log([0] == false) // true
// 如果两者皆为null或undefined则返回true,否则返回false
console.log(null == undefined) // true
console.log(null == null) // true
console.log(null == 0) // false
// []通过toString转换为''然后转换为0 ![]转换为false然后转化为0 所以true
console.log([] == ![])

总结

  • 两边都是对象 看是否指向同一个对象 否则为false
  • 一边是对象 对象toString变为字符串再比较
  • 两边都是非数字字符串 转为ascii码
  • 一边是非数字字符串 另一边是数字 非数字字符串转为NaN 永远false
  • 数组字符串 布尔值 都转为数字 然后比较

作用域

块级作用域

js中没有块级作用域的概念

// 花括号内并不是块级作用域 外部都能访问到变量
if (true) {
  var a = 1
}
console.log(a) // 1
for (var b = 0; b < 10; b++) {
}
console.log(b) // 10

如果要创建块级作用域有如下方法

  • 用let替代var
// es6后 let定义的变量 有了块级作用域的概念 只能再花括号内访问到
if (true) {
  let a = 1
}
console.log(a) // erro undefined
for (let b = 0; b < 10; b++) {
}
console.log(b) // erro undefined
// i不是块级作用域里的
let i; 
for(i = 0; i < 5; i++){
    setTimeout(function(){
        console.log(i);  //  5 5 5 5 5
    },1000)
}
// i是块级作用域里的
for(let i = 0; i < 5; i++){
    setTimeout(function(){
        console.log(i);  //  0 1 2 3 4
    },1000)
}
  • 用匿名函数自我执行
// a和b只能再函数内部访问 创建了函数内的块级作用域
(() => {
  var a = 0
  let b = 1
})()
console.log(a, b) // erro a and b undefined

var,let,const的区别

  • let能创造块级作用域 var不能 如上
  • let不能变量提升 var可以
// 报错 因为let不能变量提升
function fn1(){
  console.log(a);
  let a = 1;
}
fn1();
// undefined 因为变量提升了 fn2相当于下面fn3 
function fn2(){
  console.log(a);
  var a = 1;
}
fn2();
// 输出undefined
function fn3(){
  var a;
  console.log(a);
  a = 1;
}
fn3();
  • conts 用来声明常量,所谓常量就是物理指针不可以更改的变量
const a ; //报错,一旦声明变量,应该立即赋值
const b = 2; // 没问题
b = 3 //报错,因为定义常量之后不能成重新赋值
// 对于对象类型来说 不能改变指针指向
const c = []
c = [1, 2] // 报错 两者指针指向不同地址
c[0] = 1 // 没问题 指针指向同一地址

作用域示例

var a = 1
function f() {
  // var有变量提升 相当于在函数开始阶段var a; 然后再原var a = 2的地方a = 2, 所以这里是undefined
  console.log(1, a)
  // b是全局 或者const b let b都没有变量提升 所以这里 error b is not defined
  console.log(2, b)
  var a = 2
  b = 3
  let c = 4
  // 2 沿作用域链往上找 内部就找到了a=2
  console.log(3, a)
}
// 函数还没执行 全部变量b还没定义 error
console.log(4, b)
f()
// 函数已经执行 找到全局变量b
console.log(5, b)
// 不能访问函数内部变量a 只能访问到全局作用域var a = 1 所以是1
console.log(6, a)
// c是函数内的局部作用域 访问不了 error
console.log(7, c)

闭包

函数内部可以通过链式作用域读取到外部全局变量,但是在函数外部无法读取函数内部的局部变量,为了从外部读取函数内部的局部变量,所以有了闭包。 闭包可以理解成"函数内return函数",他是将函数内部和函数外部连接起来的桥梁,通过这个return函数能够读取外层函数的局部变量。

function f1(){
  var n = 1
  return function f2(){
    // 闭包的this指向是window
    console.log(n, this)
  }
}
// 通过f2从外部访问到了f1内部的局部变量n
f1()() // 1

闭包示例1

function box(){
  var age = 100;
  return function(){
    age++;
    return age;
  }
}
var aaa = box();  		//只执行一次box 得到执行结果box()后赋值给aaa 之后再也不执行了 这样age就不会再重置为100   
alert(aaa());     		//101 box执行结果box()就是function(){age++; return age;} 然后这个匿名函数自我执行
alert(aaa());     		//102
alert(aaa());     		//103
var aaa = box();  		//如果再执行box 那age又被重置为100了 所以下面是101
alert(aaa());     		//101
aaa = null;       		//运用闭包后 aaa让age常驻了内存 所以记得不用后 清空aaa 等待垃圾回收

闭包示例2

// 输出16 16 16,因为等console的时候 i已经是3+1=4了
function count1() {
  var arr = [];
  for (var i=1; i<=3; i++) {
    arr.push(() => {
      return i * i
    });
  }
  return arr;
}
var a1 = count1()
console.log(a1[0]())
console.log(a1[1]())
console.log(a1[2]())
// 闭包改写 创建i局部作用域 输出1 4 9
function count2() {
  var arr = [];
  for (var i=1; i<=3; i++) {
    // 原函数匿名函数自我执行并传入i包装一层
    arr.push(((n) => {
      return () => {
        return n * n
      }
    })(i));
  }
  return arr;
}
var a2 = count2()
console.log(a2[0]())
console.log(a2[1]())
console.log(a2[2]())

如何理解参数只是引用?

var s3 = 10;
//参数xx是引用 s3是普通类型存在栈内层中 xx重新赋值为13 原来的s3还是10 所以弹出10
function fn3(xx){
  xx += 3;
}
fn3(s3);
console.log(s3);

var s4 = [1,2,3];
//参数xx是引用 把对象类型数组s4传进来后 xx数组和原s4数组指向同一片堆内存 xx变成[1,2,3,4] 所以s4也变成[1,2,3,4]
function fn4(xx){
  xx.push(4);
}
fn4(s4);
console.log(s4);

递归

function sum(num){
  console.log(arguments)
  if(num <= 1){
    // 必须要有结束条件
    return 1;
  }else{
    // 函数内部调用自身函数
    return num * arguments.callee(num - 1);
  }  
}
console.log(sum(4));     //4*3*2*1 = 24

任务队列和事件循环

  • js只有一个主线程,同时还有任务队列和事件循环
  • 任务队列分为微任务(promise nextTick等)和宏任务(异步,setTimeout,事件等),任务添加到任务队列的时机如下:
    • onclick 由浏览器内核的 DOM Binding 模块来处理,当事件触发的时候,回调函数会立即添加到任务队列中。
    • setTimeout 会由浏览器内核的 timer 模块来进行延时处理,当时间到达的时候,才会将回调函数添加到任务队列中。
    • ajax 则会由浏览器内核的 network 模块来处理,在网络请求完成返回之后,才将回调添加到任务队列中。
    • promise的then等执行到了就添加到任务队列中
  • 事件循环:JS会创建一个类似于while (true)的循环,每执行一次循环都查看是否有待处理事件,如果有则取出相关事件及回调函数放入执行栈中由主线程执行
  • 如果任务队列里同时有这微任务和宏任务优先执行微任务,
// setTimieout推宏任务到队列, promise then推微任务到队列
// 先执行主线程执行栈(同步代码) 输出2 3 6
// 然后执行队列里的任务,先执行微任务4 5,然后执行宏任务1
// 2 3 6 4 5 1
setTimeout(() => {
  console.log(1)
}, 0)

console.log(2)

new Promise((reslove) => {
  console.log(3)
  reslove()
}).then(() => {
  console.log(4)
})

new Promise((reslove) => {
  reslove()
}).then(() => {
  console.log(5)
})

console.log(6)

常用算法

// 判断回文
function f1 (str) {
    return str === str.split('').reverse().join('');
}
console.log(f1('abdba'));

// 数组去重
function f2 (arr) {
    var obj = {};
    var resArr = [];
    for (var i = 0; i<arr.length; i++) {
        if (!obj[arr[i]]) {
            obj[arr[i]] = true;
            resArr.push(arr[i]);
        }
    }
    return resArr;
}
console.log(f2([1, 2, 2, 3, 3, 3, 4, 5, 6, 6]));

// 返回字符串中字符出现次数最多的那字符
function f3 (str) {
    var obj = {};
    for (var i = 0; i < str.length; i++) {
        if (obj[str.charAt(i)]) {
            obj[str.charAt(i)] += 1;
        } else {
            obj[str.charAt(i)] = 1;
        }
    }
    var maxChar = '';
    var maxValue = 1;
    for (var k in obj) {
        if (obj[k] > maxValue) {
            maxChar = k;
            maxValue = obj[k];
        }
    }
    return maxChar;
}
console.log(f3('sfdddsdfsdfdsf'));

// 冒泡排序
function f4 (arr) {
    var temp = '';
    for (var i = 0; i < arr.length - 1; i++) {
        for (var j = i + 1; j < arr.length; j++) {
            if (arr[i] > arr[j]) {
                temp = arr [i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}
console.log(f4([7, 8, 4, 3, 11, 12, 6]));

// 快速排序
function f5 (arr) {
    if (arr.length <= 1) {
        return arr;
    }
    var q = arr[0];
    var leftArr = [];
    var rightArr = [];
    for (var i = 1; i<arr.length; i++) {
        if (arr[i] < q) {
            leftArr.push(arr[i]);
        } else {
            rightArr.push(arr[i]);
        }
    }
    return [].concat(f5(leftArr), q, f5(rightArr));
}
console.log(f5([117, 8, 4, 3, 11, 12, 6]));

// 二分查找 找出有序数字指定数的index
function f6 (arr, key) {
    var low = 0;
    var high = arr.length;
    while (low <= high) {
        var mid = parseInt((low + high) / 2);
        if (arr[mid] === key) {
            return mid;
        } else if (arr[mid] < key) {
            low = mid + 1;
        } else if (arr[mid] > key) {
            high = mid - 1;
        } else {
            return -1;
        }
    }
}
console.log(f6([1, 3, 5, 7, 8, 12, 16], 12))

scrpit error

主要是跨域引起的

RESTful APIs

  • cs架构,前后端分离,用HTTP通信
  • 无状态不存储会话,每次请求带token(JWT)
  • URL中只用名词不用动词,用POST(增加)、DELETE(删)、PUT(改)、GET(查)代替动词
  • 用HTTP状态码返回当次执行结果
  • 用HTTP header content-type指定json等格式
  • 为集合提供分页排序筛选
  • 提供版本号

todo

  • vue底层原理 250行mvvm系统
  • react底层原理 state 更新机制
  • 脚手架:升级到webpack5、加入ts选项、store及router分模块
  • 组件库
  • 性能及错误监控系统
  • 性能优化、ssr