# 3.29

# js 数据类型

# 原始数据类型

  • boolean
  • string
  • number
  • null
  • undefined
  • bigint (提案中)
  • symbol (ES6 引入表示独一无二的值)

# 引用数据类型(对象 Object)

  • 普通对象 Object
  • 数组对象 Array
  • 正则对象 RegExp
  • 日期对象 Date
  • 数学函数 Math
  • 函数对象 Function

# 3.30

# null 是对象吗?为什么?

结论: null 不是对象。 解释: 虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑 使用低位存储变量的类型信息, 000 开头代表是对象 然而 null 表示为全零,所以将它错误的判断为 object 。

# call 和 apply 的区别

# call 与 apply 的唯一区别

传给 fun 的参数写法不同:

apply 是第 2 个参数,这个参数是一个数组:传给 fun 参数都写在数组中。 call 从第 2~n 的参数都是传给 fun 的。

# 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

结论

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

结论

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

# 4.8

# 1、数组的长度问题

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

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

# 2、数据类型转换




# node vue mongodb js

# 4.9

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

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

image.png

# 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 (opens new window)
来源:前端开发博客 (opens new window)

# 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 状态。

# 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

# 2、npm 包 rimraf 的使用

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

# 4.12

# 1、leetcode 151

# api 法

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

# 双指针法

/**
 *
 * @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

# 注意

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

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

# 4.13

# 1、react 入门学习

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


image.png

  • TodoList 代码优化


image.png

image.png

image.png

# 2、一些重点

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

# 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

# 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
48

# 注意:

# 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 中的内容就可以了

# 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

# 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

# 4.18

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

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

# 2、pureComponent(纯组件)

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

# 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
22
23

# 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()**这个函数的回调函数就会执行

# 4.20

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

# 2、无状态组件

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

# 3、无状态组件的优势

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

# 4、redux 的中间件

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

# 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的话,不需要这个地方来存映射,其实就是用计算时间换存储空间。

# 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
24
25
26
27
28
29
30
31
32
33

# 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

# 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()

# 4.29

# html 块级元素和行内元素

https://www.cnblogs.com/yanqiu/p/8987126.html?tdsourcetag=s_pctim_aiomsg (opens new window)

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

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


mark
mark

# 4.30

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

# 5.2

# 1、div 垂直居中

# 方法一

  • magin:auto;
  • position:absolute;
  • left: 0;  bottom: 0;   right: 0;  top: 0;

# 方法二

  • left:50%;    top:50%;
  • transform: translate(-50%, -50%)
  • position: absolute;

# 方法三

  • left:50%;    top:50%;
  • magin-top: -height/2;
  • magin-left: -width/2;

# 2、antd 初使用

  • Icon 的使用方式——版本 3 和版本 4 不一样了

# 5.3

# Vue  "复"习

# 1、自定义 v-model

  • props
  • mode

注意一一对应如下图!!
image.png

# 2、插槽 slot

  • 默认插槽
  • 作用域插槽
  • 具名插槽

# 3、动态组件

  • 通过 :is

image.png

  • 当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。
  • 这时可以用 keep-alive 把频繁切换、想缓存的组件缓存下来,在用 keep-alive 包裹的组件之间切换的时候,上一个组件不会被销毁,进入的组件也不会重新渲染。

# 4、异步组件

  • 不在统一的引入组件,而在需要用的时候用 import() 函数导入
  • 这样可以使得一个大组件加载的更快,性能优化的意思。


就是用图片中下面这种加载组件的方式代替上面的那个。起到 异步加载 的效果

image.png

# 4、vue 抽取不同组件相同逻辑

  • mixins (就是一段数据代码)

  • mixins 不是完美的解决方案

  • vue 3.0 Composition API 旨在解决这个问题

# 5.5

# leetcode 384

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

function shuffle(arr) {
	let len = arr.length;
	for (let i = 0; i < len; i++) {
		let randomIndex = Math.floor(Math.random() * (len - i)); //向下取整
		[arr[len - i - 1], arr[randomIndex]] = [arr[randomIndex], arr[len - i - 1]];
	}
	return arr;
}
console.log(shuffle(arr));
1
2
3
4
5
6
7
8
9
10
11

# 5.10

# 初识 Redis







 



set jing meinv    //添加 key-value数据

get jing  //meinv 通过 key 获取 value

del jing         // 删除一个数据

setex key time value  //设置过期时间

keys *  //查出所有已经设置的 key
1
2
3
4
5
6
7
8
9

redis-server.exe redis.windows.conf 手动启动 Redis redis-cli -p 6378 指定端口启动 auth 密码 验证密码

# live-server --cors 设置允许跨域访问

# 5.12

# next 中的页面跳转

import Link from 'next/link';
import Router from 'next/router';
import { Button } from 'antd';
export default () => {
	function goToB() {
		Router.push('/test/b');
	}
	return (
		<>
			<Link href='/a'>
				<Button>Index</Button>
			</Link>
			<Button onClick={goToB}>test B</Button>
		</>
	);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 1、采用 Link 标签
  • 2、采用 Router.push('路径')

Link 标签其实底层也是用 Router.push('路径')来实现页面跳转的

# 5.15

# next 中 _app.js 作用

  • 固定 Layout
  • 保持一些公用的状态
  • 给页面传入一些自定义数据(重写一个 MyApp)
  • 自定义错误处理
import App from 'next/app';

import 'antd/dist/antd.css';
import Layout from '../compontents/Layout';
class Myapp extends App {
	static async getInitialProps({ Component, ctx }) {
		console.log('app init');
		let pageProps;
		if (Component.getInitialProps) {
			pageProps = await Component.getInitialProps(ctx);
		}
		return {
			pageProps,
		};
	}

	render() {
		const { Component, pageProps } = this.props;
		console.log(Component);
		return (
			<Layout>
				<Component {...pageProps} />
			</Layout>
		);
	}
}

export default Myapp;
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

# 5.17

# State Hooks 基础

  • 实现一个计时器
import React, { useEffect, useState, useReducer } from 'react';

//类组件
class MyCount extends React.Component {
	state = {
		count: 0,
	};

	componentDidMount() {
		this.interval = setInterval(() => {
			this.setState({
				count: this.state.count + 1,
			});
		}, 1000);
	}

	// 在 componentWillUnmount 生命周期函数里面清除定时器之类的
	// 不然会造成内存泄漏
	componentWillUnmount() {
		if (this.interval) {
			clearInterval(this.interval);
		}
	}

	render() {
		return <span>{this.state.count}</span>;
	}
}

// useReducer
// 定义一个 'Reducer' 函数,它接受两个参数,第一个为 state,第二个为 action。 然后根据传进来的 action 操作 state
function countReducer(state, action) {
	switch (action.type) {
		case 'add':
			return state + 1;
		case 'minus':
			return state - 1;
		default:
			return state;
	}
}

// 函数组件 hooks
function MyCountFunc() {
	// const [count, setCount] = useState(0)  //useState实现

	// useReducer 接受一个操作state的函数和 state 的初始值
	const [count, dispatchCount] = useReducer(countReducer, 0); //useReducer实现

	useEffect(() => {
		const interval = setInterval(() => {
			// setCount(count => count + 1)    //useState
			dispatchCount({ type: 'minus' });
		}, 1000);
		return () => clearInterval(interval);
	}, []);
	return <span>{count}</span>;
}

export default MyCountFunc;
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

# Effect Hooks 基础

# useEffect

  • 页面初始化useEffect会执行 return 前面的代码。
  • 然后页面的任何数据更新了,组件就会重新渲染,就是先把原来的组件取消挂载,然后挂载重新渲染的组件。
  • 如下代码,初始化打印 effect invoked,更新页面先打印effect deteched,然后打印effect invoked
  • useEffect里面第二个参数是个数组 [],这个数组里面放useEffect用到的外面的数据(依赖)。然后useEffect就会根据数组里面的数据来判断要不要重新执行。只有写在里面的数据发生改变的时候,useEffect才会重新执行。如果是个空数组,不管啥数据改变了,useEffect都会执行。
useEffect(() => {
	console.log('Effect invoked');
	return () => console.log('Effect deteched');
}, []);
1
2
3
4

# useLayoutEffect 与 useEffect 的区别

  • useLayoutEffectuseEffect 先执行。
  • useLayoutEffect 是在页面更新时DOM树渲染成HTML之前执行的
  • useEffect 是在页面更新时DOM树渲染成 HTML 之后执行的
  • 所以很少用 useLayoutEffect,因为如果useLayoutEffect执行时间过长,页面渲染就会等待,造成页面卡顿,降低用户体验。

# 5.18

# Redux reducer

# 1、是一个纯粹的方法,不应该有任何副作用

  • 就是不能在 reducer 中依赖外部变量。

# 2、有任何数据更新应该返回新的对象

  • redux 中使用 reducer 返回 state 的时候必须返回一个新对象!!!
  • 因为在 js 中判断一个对象是否相等比较的是它们在栈中对堆的引用地址。
  • 如果不返回一个新对象,在 ReactDOM 对比节点是否相同的时候就算你修改了某个状态,但是返回的还是原来的对象, React 就会认为此 DOM 没变,就会产生错误。

# 3、可以用 combineReducers 合并不同的 reducer

# 实例代码

import { createStore, combineReducers } from 'redux';

const countInitialState = {
	count: 0,
};

const userInitialState = {
	username: 'jing',
};

const ADD = 'ADD';

function countReducer(state = countInitialState, action) {
	// console.log(state, action)
	switch (action.type) {
		case ADD:
			return { count: state.count + 1 };
		default:
			return state;
	}
}

const UPDATE_USERNAME = 'UPDATE_USERNAME';
function userReducer(state = userInitialState, action) {
	switch (action.type) {
		case UPDATE_USERNAME:
			return {
				...state,
				username: action.username,
			};
		default:
			return state;
	}
}

// 使用 combineReducers 合并 reducer
const allReducers = combineReducers({
	count: countReducer,
	username: userReducer,
});

// createStore中 传入合并后的 reducer
const store = createStore(allReducers, {
	count: countInitialState,
	username: userInitialState,
});

// console.log(store.getState())
// store.dispatch({ type: 'ADD' })

store.subscribe(() => {
	console.log(store.getState());
});

// 通过 dispatch 一个 action 来操作数据
store.dispatch({ type: ADD });
store.dispatch({ type: UPDATE_USERNAME, username: 'jingjing20' });

// console.log(store.getState())

export default store;
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

# 5.19

# OAuth 认证方式如何保证安全性

  • 一次性 code 获取 —— https://github.com/login/oauth/authorize?client_id=204d73ec49b937657d99
  • client_id + client_secret
  • redirect_uri —— 用户注册 OAuth github App 的时候自己填的 callback 地址。

https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/

# 5.20

# 今天放假

# 咋代码也不能停!

# LeetCode 125 验证回文串

  • 题目描述 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: "A man, a plan, a canal: Panama" 输出: true 示例 2:

输入: "race a car" 输出: false

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/valid-palindrome

# 解法一(API 大法)

var isPalindrome = function(s) {
	let newStr = s
		.replace(/[^0-9a-zA-Z]/g, '')
		.toLowerCase()
		.split('');
	return newStr.join('') == newStr.reverse().join('');
};
1
2
3
4
5
6
7

# 解法二(双指针)

var isPalindrome = function(s) {
	let newStr = s.replace(/[^0-9a-zA-Z]/g, '').toLowerCase();
	let l = 0,
		r = newStr.length - 1;

	while (l < r) {
		if (newStr[l] != newStr[r]) {
			return false;
		}
		l++;
		r--;
	}
	return true;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 5.21

# 进程与线程

# 概念

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中实际运作单位。

# 区别

# 内存使用方面的区别

  • 进程拥有操作系统分配给它的单独的内存,默认情况一个进程的分配的内存无法给另一个进程共享。
  • 一个进程里面的所有线程可以共享进程内存。

# 通信机制方面的区别

  • 进程之间很难互通,可以通过 IPC (进程间通信)。
  • 线程之间共用一块内存,通信方便快捷。

# 量级方面的区别

  • 线程相对于进程创建起来更轻、更快,使用的资源更少。

# nodemon 使用

"start": "nodemon --watch main.js --exec \"electron .\""

  • --watch 监听哪个文件
  • --exec 执行哪个文件
  • 用 \ 转义""

# 5.22

# window.onload 和 DOMContentLoaded 的区别

DOM 完整的解析过程:

  • 1、解析 HTML 结构
  • 2、加载外部脚本和样式表文件
  • 3、解析并执行脚本代码(js)
  • 4、DOM 树构建完成
  • 5、加载图片等外部文件
  • 6、页面加载完毕

# 在第 4 步的时候 DOMContentLoaded 事件会被触发。

# 在第 6 步的时候 load 事件会被触发。

# 5.23

# Infinity

  • Infinity 是一个 JavaScript 标准内置对象

  • 全局属性 Infinity 是一个数值,表示无穷大。

  • Infinity 是全局对象(global object)的一个属性,即它是一个全局变量。

  • Infinity 的初始值是 Number.POSITIVE_INFINITY。Infinity(正无穷大)大于任何值。该值和数学意义上的无穷大很像,例如任何正值乘以 Infinity 为 Infinity, 任何数值除以 Infinity 为 0(注意,这里说的是正数乘以 Infinity 为 Infinity!Infinity * 0 为 NaN。

# slice(begin, end)

begin

  • 提取起始处的索引(从 0 开始),从该索引开始提取原数组元素。
  • 如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取。
  • slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
  • 如果省略 begin,则 slice 从索引 0 开始。
  • 如果 begin 大于原数组的长度,则会返回空数组。

end

  • 提取终止处的索引(从 0 开始),在该索引处结束提取原数组元素。
  • slice 会提取原数组中索引从 begin 到 end 的所有元素(包含 begin,但不包含 end)
  • slice(1,4) 会提取原数组中从第二个元素开始一直到第四个元素的所有元素 (索引为 1, 2, 3 的元素)。
  • 如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。
  • 如果 end 被省略,则 slice 会一直提取到原数组末尾。
  • 如果 end 大于数组的长度,slice 也会一直提取到原数组末尾。

# 9.1

# webpack 中的 HappyPack

  • 由于运行在 Node.js 之上的 Webpack 是单线程模型的,所以 Webpack 需要处理的事情需要一件一件的做,不能多件事一起做。 我们需要 Webpack 能同一时间处理多个任务,发挥多核 CPU 电脑的威力,HappyPack 就能让 Webpack 做到这点,它把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。

# 图片转 base64 格式

  • 一般小于 8kb 是明显可以的
  • 超过 16kb 一般是不建议的
  • 超过 32kb 一般是明显不应该的

# 10.22

# echarts 纵轴展示 0% ~100%

optionCopy.yAxis = [
	{
		type: 'value',
		min: 0,
		max: 100,
		interval: 25,
		axisLabel: {
			fontSize: 10,
			formatter: '{value} %',
		},
	},
];
1
2
3
4
5
6
7
8
9
10
11
12

# 10.26

# a 标签上的 href 用来向后端传递参数时只能写死,不能用变量。

# 给 DOM 元素添加属性

this.linkAdom.setAttribute('href', `kim://thread?id=${this.groupId}&type=4`);
1

# 11.02

# 监听浏览器切换 Tab 页的 API visibilitychange

document.addEventListener('visibilitychange', function() {
	// 浏览器切换事件
	if (document.visibilityState === 'hidden') {
		// 离开当前tab标签
		console.log('离开当前tab标签'); // 这里可以做一些我们想做的操作,比如说清除页面的轮刷定时器。
	} else {
		// 回到当前tab标签
		console.log('回到当前tab标签'); // 这里可以做一些我们想做的操作,比如重新开启页面的轮刷定时器。
	}
});
1
2
3
4
5
6
7
8
9
10

# 11.6

# some 位运算

求除以 2 的余数可以用位与运算符:n & 1,如下所示:

0101
    &
0001
0001  5 & 1 = 1
1
2
3
4
0110
    &
0001
0000  6 & 1 = 0
1
2
3
4

# 上面位运算的应用

  • LeetCode 1356. 根据数字二进制下 1 的数目排序
给你一个整数数组 arr 。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。
如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值大小升序排列。
请你返回排序后的数组。

示例 1:

输入:arr = [0,1,2,3,4,5,6,7,8]
输出:[0,1,2,4,8,3,5,6,7]
解释:[0] 是唯一一个有 01 的数。
[1,2,4,8] 都有 11[3,5,6]21[7]31 。
按照 1 的个数排序得到的结果数组为 [0,1,2,4,8,3,5,6,7]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-integers-by-the-number-of-1-bits
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * @param {number[]} arr
 * @return {number[]}
 */
var sortByBits = function(arr) {
	const sortJing = (n) => {
		let count = 0;
		while (n != 0) {
			count += n & 1;
			n = n >> 1;
		}
		return count;
	};
	return arr.sort((a, b) => {
		return sortJing(a) - sortJing(b) || a - b;
	});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 看到的字符串 API

String.fromCharCode()

  • 静态 String.fromCharCode() 方法返回由指定的 UTF-16 代码单元序列创建的字符串。
String.fromCharCode(97); // a
1

# JavaScript 中的字符集

  • 为什么 JavaScript 不选择更高级的 UTF-16,而用了已经被淘汰的 UCS-2 呢?

  • 答案很简单:非不想也,是不能也。因为在 JavaScript 语言出现的时候,还没有 UTF-16 编码。

  • 1995 年 5 月,Brendan Eich 用了 10 天设计了 JavaScript 语言;10 月,第一个解释引擎问世;次年 11 月,Netscape 正式向 ECMA 提交语言标准。对比 UTF-16 的发布时间(1996 年 7 月),就会明白 Netscape 公司那时没有其他选择,只有 UCS-2 一种编码方法可用!

# JavaScript 字符函数的局限

  • 由于 JavaScript 只能处理 UCS-2 编码,造成所有字符在这门语言中都是 2 个字节,如果是 4 个字节的字符,会当作两个双字节的字符处理。JavaScript 的字符函数都受到这一点的影响,无法返回正确结果。 现在 ES6 基本解决了这个问题。

来源 http://www.ruanyifeng.com/blog/2014/12/unicode.html

# JS 变量提升的本质

  • 变量提升只是一种表象
  • 透过表象背后隐藏的其实是 JavaScript 的一些重要特性
  • JavaScript 是一种动态语言 它有 编译、执行两个阶段
  • JS 和其他语言一样,都要经历编译和执行阶段。正是在这个短暂的编译阶段里,JS 引擎会搜集所有的变量声明,并且提前让声明生效。至于剩下的语句,则需要等到执行阶段、等到执行到具体的某一句的时候才会生效。这就是变量提升背后的机制。