# 2020.4

# 2020.4.6

☺️ 😆 😆

# 变量提升

变量声明的提升是以变量所处的第一层词法作用域为“单位”的,即全局作用域中声明的变量会提升至全局最顶层,函数内声明的变量只会提升至该函数作用域最顶层。

var a;
console.log(a); // undefined
a = "a";
var foo = () => {
  var a; // 全局变量会被局部作用域中的同名变量覆盖
  console.log(a); // undefined
  a = "a1";
}
foo();

1
2
3
4
5
6
7
8
9
10

结论

ES6新增了let和const关键字,使得js也有了“块”级作用域,而且使用let和const 声明的变量和函数是不存在提升现象的,比较有利于我们养成良好的编程习惯。

# 函数提升

console.log(foo1); // [Function: foo1]
foo1(); // foo1
console.log(foo2); // undefined
foo2(); // TypeError: foo2 is not a function
function foo1 () {
	console.log("foo1");
};
var foo2 = function () {
	console.log("foo2");
}
// 这里可能会有人有疑问? 为foo2会报错,不同样也是声明?
// foo2在这里是一个函数表达式且不会被提升

1
2
3
4
5
6
7
8
9
10
11
12
13

结论

函数提升只会提升函数声明,而不会提升函数表达式。

# 2020.4.8

# 1、数组的长度问题

let jing = [1, 2];
jing[9] = 520;
console.log(jing.length) //10
1
2
3

数组的长度等于数组中最大索引值 + 1。

# 2、数据类型转换




# node vue mongodb js

# 2020.4.9

# 1、process.cwd() 与 __dirname 的区别

  • process.cwd()是代表打开终端的目录
  • __dirname 是代表当前运行文件的目录

image.png

# 2020.4.10

# 1、Vim常用命令

常用命令是ESC,然后:wq(保存并退出),:q!(不保存并强制退出),i进入vim模式。另外还有其它的,我可能都不会用到。。。按ESC键 跳到命令模式,然后:

  • w 保存文件但不退出vi
  • :w file 将修改另外保存到file中,不退出vi
  • :w! 强制保存,不推出vi
  • :wq 保存文件并退出vi
  • :wq! 强制保存文件,并退出vi
  • q: 不保存文件,退出vi
  • :q! 不保存文件,强制退出vi
  • :e! 放弃所有修改,从上次保存文件开始再编辑

原文链接:http://caibaojian.com/vim.html
来源:前端开发博客

# 2、git使用场景

写完代码后,我们一般这样
git add . //添加所有文件
git commit -m "本功能全部完成"

# 执行完commit后,想撤回commit,怎么办?

# git reset --soft HEAD^

这样就成功的撤销了你的commit
注意,仅仅是撤回commit操作,您写的代码仍然保留。

HEAD^的意思是上一个版本,也可以写成HEAD~1
如果你进行了2次commit,想都撤回,可以使用HEAD~2

# git commit --amend

如果仅仅只是commit -m 的注释信息写错了,可以这样
git commit --amend
此时会进入默认vim编辑器,修改注释完毕后保存就好了。

# --soft、--mixed、--hard几个参数的区别

# --mixed:

意思是:不删除工作空间改动代码,撤销commit,并且撤销git add . 操作
这个为默认参数,git reset --mixed HEAD^ 和 git reset HEAD^ 效果是一样的。

# --soft(安全的)

不删除工作空间改动代码,撤销commit,不撤销git add .

# --hard

删除工作空间改动代码,撤销commit,撤销git add . 
注意完成这个操作后,就恢复到了上一次的commit状态。

# 2020.4.11

# 1、map() 和 reduce() 函数

// // 让 2s 输出 ‘hello world’ 完成 test。
function test(callback) {
  setTimeout(() => {
    callback('jing')
  }, 2000)
}

test(function (str) {
  console.log(str)
})

const arr1 = [1, 2, 3, 4, 5]
// arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
let sum = arr1.reduce((acc, cur) => {
  // 0 + 10 => 10
  // 10 + 20 => 30
  // => 30
  console.log(acc, cur)
  return acc + cur
}, 0)
// 回调函数第一次执行时,accumulator 和currentValue的取值有两种情况:
// 如果调用reduce()时提供了initialValue
// accumulator取值为initialValue,currentValue取数组中的第一个值;
// 如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。
console.log(sum);


const arr = [{ age: 10 }, { age: 20 }]

// Array.prototype.myMap = function (cb) {
//   let t = [];
//   for (let i = 0; i < this.length; i++) {
//     t.push(cb(this[i]));
//     console.log(this[i])
//   }
//   console.log(t, '------')
//   return t;
// }
Array.prototype.myMap = function (cb) {
  return this.reduce((acc, current) => {
    // [] {age: 20} 
    let res = cb(current);
    return acc.concat(res);
  }, []);
}
// // 先定义一个 
// // 在返回
// // 写成 reduce

const newArr = arr.myMap(e => {
  return {
    ...e,
    age: e.age * 2
  }
});
console.log(newArr);
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

# 2、npm 包 rimraf 的使用

  • rimraf.sync(路径,路径下某个文件夹的名字)    同步删除某个文件夹

  • rimraf.sync(path.join(process.cwd(),'文件夹名')) 同步删除某个文件夹


# 2020.4.12

# 1、leetcode 151

# api 法

var reverseWords = function (s) {
  return s.split(' ').filter(item => item).reverse().join(' ')
};
1
2
3

# 双指针法

/**
 * 
 * @param {String} s 
 */

var reverseWords = function (s) {
  let str = s.trim();		//去除两端空格
  let i = j = str.length - 1;
  let jing = [];
  while (i >= 0) {
    while (str[i] != ' ' && i >= 0) {
      i--;
    }
    jing.push(str.substring(i + 1, j + 1))	//切割子串
    while (str[i] == ' ') {
      i = i - 1;
    }
    j = i;
  }
  return jing.join(' ')  //数组变字符串
};


console.log(reverseWords("  hello   world!  "))	//world! hello
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 注意

  • substring()方法返回一个字符串在开始索引到结束索引之间的一个子集, 或从开始索引直到字符串的末尾的一个子集。
  • str.substring(indexStart[, indexEnd])
  • 参数

indexStart——需要截取的第一个字符的索引,该索引位置的字符作为返回的字符串的首字母。
indexEnd——可选。一个 0 到字符串长度之间的整数,以该数字为索引的字符不包含在截取的字符串内

# 2020.4.13

# 1、react 入门学习

  • react 中只能通过setState来修改state中的数据——immutable


image.png

  • TodoList 代码优化


image.png

image.png

image.png

# 2、一些重点

  • react 采用声明式开发——可以减少大量dom代码
  • 可以与其他框架并存
  • 组件化
  • 单向数据流——父组件可以向子组件传数据,但是子组件不能改变这个数据
  • react 是一个视图层框架——大型项目需要其他数据层框架的支持
  • react 采用函数式编程——更容易自动化测试

# 2020.4.14

# 手写一个深拷贝

/**
 * 手写一个深拷贝
 */
function deepClone(obj = {}) {
  // 如果obj不是对象和数组 或者是null 直接返回
  if (typeof obj !== 'object' || obj == null) {
    return obj;
  }

  let result;   //初始化返回结果

  // 判断是对象还是数组
  if (obj instanceof Array) {
    result = [];
  } else {
    result = {}
  }

  for (let key in obj) {
    // 判断是否是自己的属性和方法
    if (obj.hasOwnProperty(key)) {
      // 递归调用
      result[key] = deepClone(obj[key])
    }
  }
  return result;  //返回结果
}

const obj1 = {
  age: 20,
  name: 'xxx',
  address: {
    city: 'beijing',
    jing: {
      love: 'hao',
      age: 20,
      address: '永新县'
    }
  },
  arr: ['a', 'b', 'c'],
}

const obj2 = deepClone(obj1)
obj2.arr[0] = 'a1'          //改变属性值
obj2.address.city = 'shanghai'  //改变属性值
console.log(obj1.address.city)  //测试
console.log(obj1.arr[0])		 //测试
console.log(obj2.address.jing)
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

# 2020.4.15

# 1、LeetCode 445两数之和2

/*
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
function ListNode(val) {
  this.val = val;
  this.next = null;
}
var a1 = new ListNode(5);
var a2 = new ListNode(0);
var a3 = new ListNode(2);
a1.next = a2;
a2.next = a3;

var a4 = new ListNode(2);
var a5 = new ListNode(9);
var a6 = new ListNode(8);
a4.next = a5;
a5.next = a6;

var addTwoNumbers = function (l1, l2) {
  let arr1 = [], arr2 = []; //定义两个数组存放链表数据
  while (l1) {
    arr1.push(l1.val)
    l1 = l1.next;
  }
  while (l2) {
    arr2.push(l2.val);
    l2 = l2.next;
  }

  let jing = 0;
  let dummy;
  while (arr1.length > 0 || arr2.length > 0 || jing !== 0) {
    let sum1 = arr1.length > 0 ? arr1.pop() : 0;
    let sum2 = arr2.length > 0 ? arr2.pop() : 0;
    let sum = sum1 + sum2 + jing;
    jing = sum / 10 | 0;		//按位或操作符 也常用来取整
    let newNode = new ListNode(sum % 10);
    newNode.next = dummy;
    dummy = newNode;
  }
  return dummy;
}
// addTwoNumbers(a1, a4)
console.log(addTwoNumbers(a1, a4))
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

# 注意:

# 2、react基础学习

  • PropTypes 和 defaultProps 用于类型检测
  • state 和 props 改变都会重新执行一次render() 函数
  • 父组件的 render() 函数执行一次 所有的子组件都会执行一次 render()函数

# 3、react 虚拟 DOM

  • 1、state 数据
  • 2、JSX 模板
  • 3、数据 + 模板 生成虚拟DOM (虚拟DOM就是一个JS对象,用它来描述真实的DOM)
//虚拟DOM如下
['div',{id:'jing'},['span',{},'hello world']]
1
2
  • 4、用虚拟DOM的结构生成真实的DOM 来显示
//真实DOM如下
<div id="jing">
  <span>hello world</span>
</div>
1
2
3
4
  • 5、state 发生了变化
hello world——hello jing
1
  • 6、数据 + 模板 生成新的虚拟DOM (极大的提升了性能)
//虚拟DOM如下
['div',{id:'jing'},['span',{},'hello jing']]
1
2
  • 7、比较原始虚拟DOM 和 新的虚拟DOM的差异,发现差异是span 标签中的内容**(极大的提升了性能)**
  • 8、最后只要操作DOM改变 span 中的内容就可以了

# 2020.4.16

# 1、纯函数的概念

  • 没有任何副作用
  • 易于测试
  • 易于缓存

# 2、手写实现 lodash.js 中的 _.memoize()

<script src="https://cdn.bootcss.com/lodash.js/4.17.15/lodash.min.js"></script>
<script>
  // 纯函数
  // FP:数学家
  // f(x) = x ^ 2 + b
  // 没有任何副作用,同样的输入一定会有同样的输出
  const add = (a, b) => a + b;
  // add(1, 2) => 3 结果缓存起来
  // add(1, 2) => 3 下一次输入 1,2 ,得到结果 3,3 其实没必要计算
  // add(1, 2) => 3  
  let c = 100
  const add1 = (a, b) => a + b + c;
  // add1(10, 20) => 130  
  // c = 200
  // add1(10, 20) => 230 ?

  const add3 = (a, b) => {
    // 副作用 会对外部环境造成影响
    c = 300;
    console.log(c);
    return a + b;
  }
  // fs.readFile('./index.html', 'utf8', (err, res) => {
  //   console.log(res);
  // })
  // Math.random();  Date.now()
  // 业务代码:不是全部用 FP,可能会用到 FP 其中几个思想
  // node

  // 优点:
  // 易于测试
  // expect(add(10, 20)).equal(30)
  // 构造外部变量 c = 10, 准备外部 index.html
  // expect(add1(10, 20)).equal(40)
  // 易于缓存
  function min(a, b) {
    console.log('re cal');
    return a - b;
  }
  console.log(min(5, 4))
  console.log(min(5, 4))
  console.log(min(5, 4))
  const mMin = _.memoize(min);
  // add 有缓存功能
  console.log(mMin(10, 8))
  console.log(mMin(10, 8))
  console.log(mMin(10, 8))
  // 缓存在 map
  let map = {};
  function minMemoize(a, b) {
    let key = `${a}${b}`;
    if (map[key] !== undefined) return map[key]
    console.log('re cal');
    let res = a - b;
    // 缓存一下, 依据什么东西??
    map[key] = res;
    return res;
  }
  function memoize(fun) {
    let map = {};
    // 带有缓存
    return function (...args) {
      // fun 需要的 args
      let key = JSON.stringify(args);
      if (map[key] !== undefined) return map[key];
      // 真正的计算
      let res = fun(...args)
      // 缓存一下
      map[key] = res;
      // 
      return res;
    }
  }
  const mMin1 = memoize(min);
  console.log(mMin1(12, 12))
  console.log(mMin1(12, 12))
  console.log(mMin1(12, 12))
</script>
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

# 2020.4.17

# 1、react 基础学习

# 1、react.createElement()

  • 接受三个参数 react.createElement('div', {}, 'jingjinghaohao')
  • 参数用途参照虚拟DOM

# 2、react 中 ref 的使用(获取DOM)

image.png

# 3、setState()是一个异步方法

  • 错误

image.png

  • 正确

image.png

# 4、react生命周期函数

image.png

# 2020.4.18

# 1、react生命周期函数的应用

  • 前面讲到在 react 中,父组件的 render() 函数执行的时候,子组件中的render() 函数也会重新执行。为了减少不必要的页面重新渲染,可以在子组件中的 shouldComponentUpdate() 生命周期函数中返回false。
  • componentDidMount()中做获取数据操作最好

# 2、pureComponent(纯组件)

  • React15.3中新加了一个 PureComponent 类,顾名思义, pure 是纯的意思,PureComponent 也就是纯组件,取代其前身 PureRenderMixin ,PureComponent 是优化 React 应用程序最重要的方法之一,易于实施,只要把继承类从 Component 换成 PureComponent 即可,可以减少不必要的 render 操作的次数,从而提高性能,而且可以少写 shouldComponentUpdate 函数,节省了点代码。

# 2020.4.19

# 1、leetcode 11题 盛最多水的容器

/**
 * @param {Array} height 
 */

var maxArea = function (height) {
  let jing = 0, max = 0;
  let i = 0, j = height.length - 1;
  while (j > i) {
    if (height[j] > height[i]) {
      jing = height[i] * (j - i);
      i++;
    } else {
      jing = height[j] * (j - i);
      j--;
    }
    max = Math.max(max, jing)   //每次更新最大值
  }
  return max;
}
console.log(maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7]))
// 49
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 2、redux入门学习

# createStore()—创建 store 的API

import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

export default store;
1
2
3
4
5

# store.getState()—获取store数据

this.state = store.getState();
1

# store.dispatch()—派发 action 给 reducer

handleInputChange(e) {
    const action = {
      type: 'change_input_value',		//type用于reducer中判断
      value: e.target.value
    }
    store.dispatch(action);
  }
1
2
3
4
5
6
7

# store.subscribe()—订阅store改变

 store.subscribe(this.handleStoreChange);
1

只要store里面的数据发生改变,**store.subscribe()**这个函数的回调函数就会执行

# 2020.4.20

# 1、react中的 UI组件和容器组件

# 2、无状态组件

  • 当一个普通组件中只有一个 render() 函数的时候,可以用无状态组件替换。
  • 无状态组件就是一个函数
  • UI组件一般可以用无状态组件替换

# 3、无状态组件的优势

  • 无状态组件性能更好
  • 因为无状态组件没有继承自 component,没有各种生命周期函数。

# 4、redux的中间件

  • 中间件是redux的中间件,不是react的中间件
  • 中间件两边是 action 和 store

# 2020.4.21

# 1、typescript 基础类型的认识

  • number
  • string
  • boolean
  • null
  • undefined
let isDone: boolean = false

let age: number = 20
let binaryNumber: number = 0b1111

let firstName: string = 'viking'
let message: string = `Hello, ${firstName}, age is ${age}`

let u: undefined = undefined
let n: null = null

// null 和 undefined 是所有类型的子类型
let num: number = undefined

// any类型
let notSure: any = 4
notSure = 'maybe it is a string'
notSure = true

notSure.myName
notSure.getName()

// 联合类型
let numberOrString: number | string = 234
numberOrString = 'abc'

// 数组类型
let arrOfNumbers: number[] = [1, 2, 3, 4]
arrOfNumbers.push(5)

// 元组
let user: [string, number] = ['viking', 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
30
31
32

# 2、cookie session


HTTP 协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session 和 Cookie 的主要目的就是为了弥补 HTTP 的无状态特性

# Session 的缺点

Session 机制有个缺点,比如 A 服务器存储了 Session,就是做了负载均衡后,假如一段时间内 A 的访问量激增,会转发到 B 进行访问,但是 B 服务器并没有存储 A 的 Session,会导致 Session 的失效。

  • 会话 Cookie 中缺少 HttpOnly 属性会导致攻击者可以通过程序(JS脚本、Applet等)获取到用户的 Cookie 信息,造成用户 Cookie 信息泄露,增加攻击者的跨站脚本攻击威胁。
  • 如果在 Cookie 中没有设置 HttpOnly 属性为 true,可能导致 Cookie 被窃取。

# 禁用 Cookies,如何使用 Session ?

  • 如果禁用了 Cookies,服务器仍会将 sessionIdcookie 的方式发送给浏览器,但是,浏览器不再保存这个cookie (即 sessionId ) 了。
  • 如果想要继续使用 session,需要采用 url 重写的方式来实现

cookie也是支持跨域的,http请求的时候设置 withCredentials=true 即可;JWT 本身没有什么支不支持跨域的概念,就看你把它放哪儿了;cookieJWT 原理还是有区别的,如果使用 cookie 的话,实际上cookie里面存的是 sessionID,后端就需要有个地方存储 sessionID和登录信息的映射,如果使用JWT的话,不需要这个地方来存映射,其实就是用计算时间换存储空间。

# 2020.4.24

# 3、react hooks 初识

import React, { useState, useEffect } from 'react';
import useMousePosition from '../hooks/useMousePosition';   //自定义 Hooks
const LikeButton: React.FC = () => {
  const [like, setlike] = useState(0)   //useState
  const [on, setOn] = useState(true)
  const positions = useMousePosition()
  useEffect(() => {
    document.title = `点击了${like}`
  })
  return (
    <>
      <button onClick={() => { setlike(like + 1) }}>
        {like}
      </button>
      <button onClick={() => { setOn(!on) }}>
        {on ? 'on' : 'off'}
      </button>
      <h1>X: {positions.x}, Y : {positions.y}</h1>
    </>
  )
}

export default LikeButton
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 2020.4.25

# js web API

# 1、DOM 的本质

# 2、DOM 节点操作

  • property:修改对象属性,不会体现到 html 结构中
  • attribute:修改 html 属性,会改变 html 结构
  • 两者都有可能引起 DOM 重新渲染 

# 3、DOM 结构操作

# 4、性能优化

  • DOM 查询做缓存
// 不缓存 DOM 查询结果
for (let i = 0; i < document.getElementsByTagName('p').length; i++) {
	// 每次循环 都会计算 length 频繁进行 DOM 查询
}
// 缓存 DOM 查询结果
const pList = document.getElementsByTagName('p')
const len = pList.length
for (let i = 0; i < len; i++) {
	// 缓存 len 只进行了一次 DOM 查询
}
1
2
3
4
5
6
7
8
9
10
  • 把频繁的操作改为一次性操作 document.createDocumentFragment()
const list = document.getElementById('list')

// 创建一个文档片段,此时还没有插入到 DOM 结构中
const frag = document.createDocumentFragment()

for (let i = 0; i < 20; i++) {
	const li = document.createElement('li')
	li.innerHTML = `List item ${i}`

	// 先插入文档片段中
	frag.appendChild(li)
}

// 都完成之后,再统一插入到 DOM 结构中
list.appendChild(frag)

console.log(list)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2020.4.28

  • 如果对象有两个具有相同名称的键,则将替前面的键。它仍将处于第一个位置,但具有最后指定的值。
const obj = { a: "one", b: "two", a: "three" };
console.log(obj);
//{ a: "three", b: "two" }
1
2
3
  • 所有对象键(不包括Symbols)都会被存储为字符串,即使你没有给定字符串类型的键。 这就是为什么obj.hasOwnProperty('1')也返回true
  • 上面的说法不适用于Set。 在我们的Set中没有“1”set.has('1')返回false。 它有数字类型1set.has(1)返回true
const obj = { 1: "a", 2: "b", 3: "c" };
const set = new Set([1, 2, 3, 4, 5]);

obj.hasOwnProperty("1");
obj.hasOwnProperty(1);
set.has("1");
set.has(1);

//true true false true
1
2
3
4
5
6
7
8
9
  • 使用var关键字,您可以用相同的名称声明多个变量。然后变量将保存最新的值。

  • 您不能使用letconst来实现这一点,因为它们是块作用域的。

  • 关闭选项卡后,将删除存储在sessionStorage中的数据。

  • 如果使用localStorage,数据将永远存在,除非例如调用localStorage.clear()

# 2020.4.29

# html 块级元素和行内元素

https://www.cnblogs.com/yanqiu/p/8987126.html?tdsourcetag=s_pctim_aiomsg

# querySelector 和 getElementBy、、、系列的区别

  • 前者查出来的是一个NodeList
  • 后者是一个HTMLCollection


mark
mark

# 2020.4.30

# 我的婧婧过生日,希望她身体健康、快快乐乐。