2020
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();结论
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在这里是一个函数表达式且不会被提升结论
函数提升只会提升函数声明,而不会提升函数表达式。
4.8
1、数组的长度问题
let jing = [1, 2];
jing[9] = 520;
console.log(jing.length); //10数组的长度等于数组中最大索引值 + 1。
2、数据类型转换
node vue mongodb js
4.9
1、process.cwd() 与 __dirname 的区别
- process.cwd()是代表打开终端的目录
- __dirname 是代表当前运行文件的目录

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^的意思是上一个版本,也可以写成 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);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(' ');
};双指针法
/**
*
* @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注意
substring()方法返回一个字符串在开始索引到结束索引之间的一个子集, 或从开始索引直到字符串的末尾的一个子集。str.substring(indexStart[, indexEnd])- 参数
indexStart——需要截取的第一个字符的索引,该索引位置的字符作为返回的字符串的首字母。indexEnd——可选。一个 0 到字符串长度之间的整数,以该数字为索引的字符不包含在截取的字符串内
4.13
1、react 入门学习
- react 中只能通过
setState来修改state中的数据——immutable

- TodoList 代码优化



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);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));注意:
- | 的作用 https://juejin.im/post/5ddf8d326fb9a0717b5fce3c
- 链表头插法
- 数组模拟栈
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']];- 4、用虚拟 DOM 的结构生成真实的 DOM 来显示
//真实DOM如下
<div id="jing">
<span>hello world</span>
</div>- 5、state 发生了变化
hello world——hello jing- 6、数据 + 模板 生成新的虚拟 DOM (极大的提升了性能)
//虚拟DOM如下
['div', { id: 'jing' }, ['span', {}, 'hello jing']];- 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>4.17
1、react 基础学习
1、react.createElement()
- 接受三个参数 react.createElement('div', {}, 'jingjinghaohao')
- 参数用途参照虚拟 DOM
2、react 中 ref 的使用(获取 DOM)
3、setState()是一个异步方法
- 错误

- 正确
4、react 生命周期函数

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]));
// 492、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;store.getState()—获取 store 数据
this.state = store.getState();store.dispatch()—派发 action 给 reducer
handleInputChange(e) {
const action = {
type: 'change_input_value', //type用于reducer中判断
value: e.target.value
}
store.dispatch(action);
}store.subscribe()—订阅 store 改变
store.subscribe(this.handleStoreChange);只要 store 里面的数据发生改变,**store.subscribe()**这个函数的回调函数就会执行
4.20
1、react 中的 UI 组件和容器组件
2、无状态组件
3、无状态组件的优势
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];2、cookie session
HTTP 协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session 和 Cookie 的主要目的就是为了弥补 HTTP 的无状态特性。
Session 的缺点
Session 机制有个缺点,比如 A 服务器存储了 Session,就是做了负载均衡后,假如一段时间内 A 的访问量激增,会转发到 B 进行访问,但是 B 服务器并没有存储 A 的 Session,会导致 Session 的失效。
Cookie 中的 HttpOnly 标记
- 会话
Cookie中缺少HttpOnly属性会导致攻击者可以通过程序(JS 脚本、Applet 等)获取到用户的Cookie信息,造成用户 Cookie 信息泄露,增加攻击者的跨站脚本攻击威胁。 - 如果在
Cookie中没有设置HttpOnly属性为true,可能导致Cookie被窃取。
禁用 Cookies,如何使用 Session ?
- 如果禁用了
Cookies,服务器仍会将sessionId以cookie的方式发送给浏览器,但是,浏览器不再保存这个 cookie (即 sessionId ) 了。 - 如果想要继续使用 session,需要采用 url 重写的方式来实现
cookie 跨域
cookie也是支持跨域的,http请求的时候设置 withCredentials=true 即可;JWT 本身没有什么支不支持跨域的概念,就看你把它放哪儿了;cookie 和 JWT 原理还是有区别的,如果使用 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;4.25
js web API
1、DOM 的本质
2、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 查询
}- 把频繁的操作改为一次性操作
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);4.28
- 如果对象有两个具有相同名称的键,则将替前面的键。它仍将处于第一个位置,但具有最后指定的值。
const obj = { a: 'one', b: 'two', a: 'three' };
console.log(obj);
//{ a: "three", b: "two" }- 所有对象键(不包括
Symbols)都会被存储为字符串,即使你没有给定字符串类型的键。 这就是为什么obj.hasOwnProperty('1')也返回true。 - 上面的说法不适用于
Set。 在我们的Set中没有“1”:set.has('1')返回false。 它有数字类型1,set.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- 使用
var关键字,您可以用相同的名称声明多个变量。然后变量将保存最新的值。 - 您不能使用
let或const来实现这一点,因为它们是块作用域的。
- 关闭选项卡后,将删除存储在
sessionStorage中的数据。 - 如果使用
localStorage,数据将永远存在,除非例如调用localStorage.clear()。
4.29
html 块级元素和行内元素
https://www.cnblogs.com/yanqiu/p/8987126.html?tdsourcetag=s_pctim_aiomsg
querySelector 和 getElementBy、、、系列的区别
- 前者查出来的是一个 NodeList
- 后者是一个 HTMLCollection
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
注意一一对应如下图!!
2、插槽 slot
- 默认插槽
- 作用域插槽
- 具名插槽
3、动态组件
- 通过 :is

- 当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。
- 这时可以用 keep-alive 把频繁切换、想缓存的组件缓存下来,在用 keep-alive 包裹的组件之间切换的时候,上一个组件不会被销毁,进入的组件也不会重新渲染。
4、异步组件
- 不在统一的引入组件,而在需要用的时候用 import() 函数导入
- 这样可以使得一个大组件加载的更快,性能优化的意思。
就是用图片中下面这种加载组件的方式代替上面的那个。起到 异步加载 的效果

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));5.10
初识 Redis
set jing meinv //添加 key-value数据
get jing //meinv 通过 key 获取 value
del jing // 删除一个数据
setex key time value //设置过期时间
keys * //查出所有已经设置的 keyredis-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、采用 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;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;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');
}, []);useLayoutEffect 与 useEffect 的区别
useLayoutEffect比useEffect先执行。useLayoutEffect是在页面更新时DOM树渲染成HTML之前执行的useEffect是在页面更新时DOM树渲染成 HTML 之后执行的- 所以很少用
useLayoutEffect,因为如果useLayoutEffect执行时间过长,页面渲染就会等待,造成页面卡顿,降低用户体验。
5.18
Redux reducer
1、是一个纯粹的方法,不应该有任何副作用
- 就是不能在 reducer 中依赖外部变量。
2、有任何数据更新应该返回新的对象
redux中使用reducer返回state的时候必须返回一个新对象!!!- 因为在
js中判断一个对象是否相等比较的是它们在栈中对堆的引用地址。 - 如果不返回一个新对象,在
React中DOM对比节点是否相同的时候就算你修改了某个状态,但是返回的还是原来的对象,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;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('');
};解法二(双指针)
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;
};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 的所有元素。
- 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} %'
}
}
];10.26
a 标签上的 href 用来向后端传递参数时只能写死,不能用变量。
给 DOM 元素添加属性
this.linkAdom.setAttribute('href', `kim://thread?id=${this.groupId}&type=4`);11.02
监听浏览器切换 Tab 页的 API visibilitychange
document.addEventListener('visibilitychange', function () {
// 浏览器切换事件
if (document.visibilityState === 'hidden') {
// 离开当前tab标签
console.log('离开当前tab标签'); // 这里可以做一些我们想做的操作,比如说清除页面的轮刷定时器。
} else {
// 回到当前tab标签
console.log('回到当前tab标签'); // 这里可以做一些我们想做的操作,比如重新开启页面的轮刷定时器。
}
});11.6
some 位运算
求除以 2 的余数可以用位与运算符:n & 1,如下所示:
0101
&
0001
0001 5 & 1 = 10110
&
0001
0000 6 & 1 = 0上面位运算的应用
- 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] 是唯一一个有 0 个 1 的数。
[1,2,4,8] 都有 1 个 1 。
[3,5,6] 有 2 个 1 。
[7] 有 3 个 1 。
按照 1 的个数排序得到的结果数组为 [0,1,2,4,8,3,5,6,7]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-integers-by-the-number-of-1-bits
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。/**
* @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;
});
};看到的字符串 API
String.fromCharCode()
- 静态 String.fromCharCode() 方法返回由指定的 UTF-16 代码单元序列创建的字符串。
String.fromCharCode(97); // aJavaScript 中的字符集
为什么 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 引擎会搜集所有的变量声明,并且提前让声明生效。至于剩下的语句,则需要等到执行阶段、等到执行到具体的某一句的时候才会生效。这就是变量提升背后的机制。
russ
