Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

工作笔记

1.npm的使用

出现这种情况:

1.删除C:\Users\用户\下的.npmrc文件

img

2.命令行输入npm cache clean –force (强制清除缓存)

执行成功后会出现npm WARN using –force I sure hope you know what you are doing

这一问题就解决啦

img

  1. npm config ls (查看配置列表)
  2. npm ls 查看安装的模块
  3. npm init (在项目中引导创建一个package.json文件)
  4. npm i 和 npm install 的区别?(二者有着细微的不同)

① 用npm i 安装的模块无法用npm uninstall 删除 ,用 npm uninstall i 才能卸载掉

② npm i 会帮助检测当前node版本最匹配的npm包版本号,并匹配出来相互依赖的npm包

应该提升的版本号

③ npm install 安装报错时肯定会出现npm-debug.log文件npm i 不一定

npm i module_name -S => npm install module_name –save (写入到dependencies对象)

npm i module_name -D => npm install module_name –save–dev(写入到devDependencies对象)

npm i module_name -g (全局安装)

-S 就是 –save的简写

-D 就是 –save–dev的简写(这样安装的包的名称及版本号就会存在package.json的)

-save会将包的名称及版本号放在dependencies里面

devDependencies里面的插件用于开发环境,不用于生产环境

dependencies里面的插件是需要发布到生产环境的

  1. yarn的安装

① npm install -g yarn (使用npm安装)

② yarn –version (查看版本)

③yarn config set registry https://registry.npm.taobao.org -g (设置为淘宝源)

④npm config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g ( 配置node-sass 的二进制包镜像地址)

2.Object.assign()的用法是什么?

  • 1.对象的合并
  • 2.合并具有相同属性的对象
  • 3.继承属性和不可枚举属性是不能拷贝
  • 4.原始属性会被包装为对象
  • 5.异常会打断后续的拷贝任务

1.对象的拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Object.assign(target, ...sources)  // 【target:目标对象】,【source: 源对象(源对象可以有多个)】
Object.assign() //方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

// 举例:
const object1 = {
a: 1,
b: 2,
c: 3
};
const object2 = Object.assign({c: 4, d: 5}, object1);
console.log(object1) // { a: 1, b: 2, c: 3 }
console.log(object2) // { c: 3, d: 5, a: 1, b: 2 }
// 注意:
// 1.如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性
//2.Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty()

2.Object.assign+JSON.stringify+Json.parse实现对象的深拷贝

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
针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。

let obj1 = { a: 0 , b: { c: 0}};

let obj2 = Object.assign({}, obj1);

console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}


obj1.a = 1;

console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}

console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}



obj2.a = 2;

console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}

console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}



obj2.b.c = 3;

console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}

console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}

最后一次赋值的时候,b是值是对象的引用,只要修改任意一个,其他的也会受影响

// Deep Clone (深拷贝)

obj1 = { a: 0 , b: { c: 0}};

let obj3 = JSON.parse(JSON.stringify(obj1));

obj1.a = 4;

obj1.b.c = 4;

console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}

3.对象的合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const o1 = { a: 1 };

const o2 = { b: 2 };

const o3 = { c: 3 };



const obj = Object.assign(o1, o2, o3);

console.log(obj); // { a: 1, b: 2, c: 3 }

console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。

其实就是对象的拷贝,o1就是目标对象,后面的是源对象,后面的属性等会拷贝到目标对象

4.合并具有相同属性的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const o1 = { a: 1, b: 1, c: 1 };

const o2 = { b: 2, c: 2 };

const o3 = { c: 3 };



const obj = Object.assign({}, o1, o2, o3);

console.log(obj); // { a: 1, b: 2, c: 3 }

1.属性被后续参数中具有相同属性的其他对象覆盖。

2.目标对象的属性与源对象的属性相同,源的会覆盖目标的属性

5.继承属性和不可枚举属性是不能拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
const obj = Object.create({foo: 1}, { // foo 是个继承属性。
bar: {
value: 2 // bar 是个不可枚举属性。
},
baz: {
value: 3,
enumerable: true // baz 是个自身可枚举属性。
}
});
创建对象时,如果没有设置enumerable的值,默认为false(不可枚举属性),设置为true,则为可枚举属性
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }

6.原始数据会被包装为对象

1
2
3
4
5
6
7
8
9
10
11
12
13
const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo")

const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);

// 原始类型会被包装,null 和 undefined 会被忽略。

// 注意,只有字符串的包装对象才可能有自身可枚举属性。

console.log(obj); // { "0": "a", "1": "b", "2": "c" }

7.异常会打断后续拷贝任务

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
const target = Object.defineProperty({}, "foo", {

value: 1,

writable: false

}); // target 的 foo 属性是个只读属性。



Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4});

// TypeError: "foo" is read-only

// 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。

console.log(target.bar); // 2,说明第一个源对象拷贝成功了。

console.log(target.foo2); // 3,说明第二个源对象的第一个属性也拷贝成功了。

console.log(target.foo); // 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。

console.log(target.foo3); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。

console.log(target.baz); // undefined,第三个源对象更是不会被拷贝到的。

3.可选链

JavaScript中如果一个值为null,undefined,直接访问会报Uncaught TypeError: Cannot read properties of undefined 异常错误

而在真实的项目中会出现这种情况,有这个值就读这个值,没有这个值也不会报错

obj?.data?.person?.name

?. 可以无限链下去,不论有多少属性,只要有最后可以访问到属性,就会直接赋值最后的属性值。否则当任何一个链出现问题,立刻停止,而后赋值undefined。

4.空值合并运算符(对象用的)

??双引号后面是默认值(可变量,可常量)

?? 出现null ,undefined值会后,合并默认值(??后面的值

在??前面没有值的时候会默认??后边的值(类似于三木运算符中的:后面赋值)。

let obj = {age: 0, name: ‘’, sex: null};obj.age ?? ‘111’; // 0obj.name ?? ‘111’; // ‘’obj.sex ?? ‘111’; // 111obj.addr ?? ‘111’; // 111

5.空值赋值运算符??=

和空值合并运算符??类似(可常量、可变量)

当??=左侧的值为null、undefined的时候,会将右侧的值赋值给左侧变量

let obj = {age: 0, name: ‘’, sex: null, addr: undefined};obj.age ??= ‘111’; // 0obj.name ??= ‘111’; // ‘’obj.sex ??= ‘111’; // 111obj.addr ??= ‘111’ ; // 111

6.git打tag

1
2
3
4
5
6
7
8
9
10
11
12
git tag 标签名    --用于新建一个标签,默认为HEAD
git tag -a 标签名 -m "标签说明" --可以指定标签信息
git tag --可以查看所有标签
git tag -d 标签名 --删除标签
git push origin --推送一个本地标签


打tag
git tag 标签名
git pull
git push orign 标签名

7.flex布局(弹性布局)

img

项目默认沿主轴排列

display:flex —属性决定是否使用flex布局

flex-direction:row | row-reverse | column | column-reverse —属性决定主轴的方向(即项目的排列方向)

​ row:主轴为水平方向,起点在左端

​ row-reverse : 主轴为水平方向,起点在右端

​ column:主轴为垂直方向,起点在上沿

​ column : 主轴为垂直方向,起点在下沿

flex-wrap属性: 默认情况下,项目都排成一条线(又称“轴线”),

flex-wrap:nowrap | wrap | wrap-reverse –不换行 | 换行(第一行在上方) | 换行(第一行在下方)

flex-flow —属性是flex-direction和flex-wrap属性的简写形式,默认值为row nowrap

justify-content —-属性定义了项目在主轴上的对齐方式

justify-content: flex-start | flex-end |center | space-between | space-arroud;

space-between : 两端对齐,项目之间的间隔都相等

space-around : 每个项目两侧的间隔相等,

align-items ——–属性定义项目在交叉轴上如何对齐

align-items : flex-start | flex-end | center | baseline | stretch

flex-grow ———- flex-grow属性定义项目的放大比例,默认为 0,即如果存在剩余空间,也不放大,

如果所有项目的flex-grow属性都为1,则他们等分剩余空间,

如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍

flex-shrink属性 ———属性定义了项目的缩小比例,默认为1,如果空间不足,该项目将缩小

如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小,

如果所有项目的flex-shrink属性都为0,其他项目都为1,则空间不足时,前者不缩小,负值对改属性无效

flex-basic ————-属性定义了在分配多余空间之前,项目占据的主轴空间(main size)浏览器根据这个属性,计算主轴是否有多余空间它的默认值为auto,即项目的本来大小

flex属性是 flex-grow,flex-shrink和flex-basic

8.Object.create();

1
2
3
4
5
6
7
8
9
10
11
var proxy = new Proxy({},{
get:function(target,propKey){
return 35;
}
});
//Proxy实例也可以作为其他对象的原型对象
let obj = Object.create(proxy);
//是把现有对象的属性,挂到新建对象的原型上,新建对象为空对象

//Object.create()的第二个参数,为添加的可枚举属性(即自身属性,不是原型的,)例子见下文

useCallback(fn,deps) 相当于 useMemo(( ) => fn ,deps);

9.useReducer

const [state,dispatch] = useReducer(reducer,initalArg,init);

​ 在某些场景下,useReducer会比useState更适用,例如state逻辑较复杂且包含多个子值,或者下一个state依赖于之前的state等,并且useReducer还能给那些会触发深更新的组件做性能优化。

10.fetch

isomorphic-fetch:兼容Node.js和浏览器两种环境运行。

  • 1.Ajax是使用XMLHttpRequest发起的,用起来麻烦,fetch发送一个请求不用像Ajax那样写一大堆代码
  • 2.使用fetch无法取消一个请求,这是因为fetch基于promise,而promise无法做到这一点
  • 3.在默认情况下,fetch不会接受或者发送cookies。
  • 4.fetch没有办法原生监测请求的进度,而XMLHttpRequest可以。
  • 5.fetch由于是ES6规范,兼容性上比不上XMLHttpRequest

发送get请求:

1
2
3
4
5
6
7
8
fetch('https://mock.apifox.cn/m1/1201147-0-default/study?apifoxResponseId=61655474')
.then(response => {
console.log('@',response);
return response.json();
}).then(data => {
console.log('data',data);
})

发送post请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var query = {
apifoxResponseId:61655474,

}
fetch('https://mock.apifox.cn/m1/1201147-0-default/teacher?apifoxToken=UHdcUcSGyGJFKcAUjA0eWz4WfRcbQUJn',{
method:'POST',
body:JSON.stringify(query),
}).then(response => {
return response.json();
}).then(data => {
console.log('data',data);
}).catch(err => {
console.log('err',err);
})

11. Generator函数 (生成器函数)

Generator 函数是 ES6 提供的一种异步编程解决方案, 可以理解成Generator函数是一个状态机,封装了多个内部状态,执行Generator函数会返回一个遍历器对象(Iterator(迭代器)对象)

  • 形式上: Generator函数是一个普通的函数,不过相对于普通函数多出了两个特征。一是在function关键字和函数明之间多了’ *’号;二是函数内部使用了yield表达式,用于定义Generator函数中的每个状态。
  • 语法上: Generator函数封装了多个内部状态(通过yield表达式定义内部状态)。执行Generator函数时会返回一个遍历器对象(Iterator(迭代器)对象)。也就是说,Generator是遍历器对象生成函数,函数内部封装了多个状态。通过返回的Iterator对象,可以依次遍历(调用next方法)Generator函数的每个内部状态。
  • 调用上:普通函数在调用之后会立即执行,而Generator函数调用之后不会立即执行,而是会返回遍历器对象(Iterator对象)。通过Iterator对象的next方法来遍历内部yield表达式定义的每一个状态

Generator函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行

12.计算属性名:

1
2
3
4
5
6
7
8
9
10
11
if(type === 'query'){
this.setState({
queryLoading:true
})
}
if(type === 'refresh'){
this.setState({
refreshLoading:true
})
}

使用js中对象的计算属性 : ES6增加了可计算属性名,可以在文字形式中使用[ ]包裹一个表达式来 当做属性名

1
2
3
4
this.setState({
[type === 'query' ? 'queryLoading' : 'refreshLoading']:true,
})

13.数组扁平化(无论多少层)

1
2
[1,2,3,[1,2,6,7,[1,6,7]]].toString().split(',').map((item) => Number(item) ); // [1, 2, 3, 1, 2, 6, 7, 1, 6, 7]

字符串转字符数组

1
2
[...'abcdefg'] //['a', 'b', 'c', 'd', 'e', 'f', 'g']

14.封装思想

优化1

1
2
3
4
5
const dateArr = ['endDate','issueDate','payDate','deliveryDate','issueAmount'];
for( let value of dateArr){
e.variables[value] = value ==='issueAmount' ? formatter( e.variables[value]) : this.withDateFormat(e.variables[value]);
}

优化2

1
2
3
4
['endDate','issueDate','payDate','deliveryDate','issueAmount'].forEach((item) => {
e.variables[item] = item ==='issueAmount' ? formatter( e.variables[item]) : this.withDateFormat(e.variables[item]);
})

优化3

1
2
3
4
5
['endDate','issueDate','payDate','deliveryDate','issueAmount'].forEach((item) => {
let CB = item ==='issueAmount' ? formatter : this.withDateFormat;
CB(e.variables[item]);
})

优化4

1
2
['endDate','issueDate','payDate','deliveryDate','issueAmount'].forEach((item) => (item ==='issueAmount' ? formatter : this.withDateFormat)(e.variables[item]))

柯里化:

1
2
3
4
5
6
7
8
function compose(f,g) {
return function(...args) {
return f.call(this, g.apply(this, args));
}
}

console.log(compose(x => x**2, (x,y) => x + y)(2,3)); //25

memoization方案在《JavaScript模式》和《JavaScript设计模式》都有提到。memoization是一种将函数执行结果用变量缓存起来的方法。当函数进行计算之前,先看缓存对象中是否有次计算结果,如果有,就直接从缓存对象中获取结果;如果没有,就进行计算,并将结果保存到缓存对象中。[4]

15.随机生成十六进制颜色:

1
2
"#" + ("0000"+ (Math.random() * 0x1000000 << 0).toString(16)).substr(-6)

16.parseInt怪异行为

parseInt是将数字字符串解析为整数的函数

parseInt(numericalString, radix), 始终将其第一个参数转换为字符串(如果不是字符串),然后将该数字字符串解析为整数值。

parseInt( )是内置的js函数,用于解析数字字符串中的整数。例如,解析数字字符串’100’

1
2
3
4
5
6
7
parseInt(0.5)  // 0
parseInt(0.0005) // 0
parseInt(0.000005) // 0
parseInt(0.0000005) // 5
parseInt('5a5') // 5
parseInt(999999999999999999999) // 1

将浮点数转为字符串

1
2
3
4
5
6
0.5.toString() // '0.5'
0.0005.toString() // '0.0005'
0.000005.toString() // '0.000005'
0.0000005.toString() // '5e-7'
String(999999999999999999999) //'1e+21'

0.0000005会转换成5*10^-7,然后用科学计数法表示,就是5e-7 (5乘e的-7次方)

parseInt(‘5e-7’)考虑第一个数字‘5’,但跳过‘e-7’

因为 parseInt() 始终将其第一个参数转换为字符串,所以小于10负6次方的浮点数将以指数表示。 然后 parseInt() 从 float 的指数表示法中提取整数

为了安全地提取浮点数的整数部分,建议使用以下Math.floor()函数

1
2
3
4
5
6
7
8
9
Math.floor(0.5);      // => 0
Math.floor(0.05); // => 0
Math.floor(0.005); // => 0
Math.floor(0.0005); // => 0
Math.floor(0.00005); // => 0
Math.floor(0.000005); // => 0

Math.floor(0.0000005); // => 0

17git版本控制

git log //查看提交历史记录,从最近到最远

git log –pretty=online //加参,简介查看

git reflog // 查看每一次修改历史

git fetch //拉取到本地不合并,更新git remote中所有的远程仓库所包含最新分支,并记录到.git/FETCH_HEAD文件中

git reset –hard commitid //恢复到指定版本

git remote add origin https://github.com/charmer-hash/yangy.github.io //将本地仓库与远程仓库建立联系

git push -u origin master //将本地代码推送到远程(首次推送)

git push -f origin master // 将本地代码强制推送到远程

git remote //查看远程分支的名称

git remote -v // 查看远程分支的名称以及远程仓库的地址

git branch //查看当前的分支

git checkout -b 分支的名称 // 创建并切换到分支上

git switch -c 分支名称 // 创建并切换到分支上

git branch -d 分支名称 // 删除分支(删除本地分支)

git branch 分支名称 //创建分支

git checkout 分支名称 //切换分支

git merge 分支名称 //将分支中的内容合并到当前分支

git stash list //查看所有缓存

git stash save ‘缓存名’

git stash pop stash@{0} //拉去缓存

git reset –soft HEAD^ 取消上次提交

18装饰器 (目前提案将引入ECMAScript

装饰器定义:用来增强类(class)的功能,也可以说增加或者修改类的功能,许多面向对象的语言都有这种语法,目前有一个提案将其引入了ECMAScript,TS已经完整的实现了装饰器装饰器写法 : @ + 函数名, 可以放在类和类方法定义前面装饰器可以用来装饰四种类型的值:

  • 类的属性(public,private,and static)
  • 类的方法(public,private,and static)
  • 属性存取器(accessor) (public,private,and static)

19.HOC高阶组件

高阶组件是React中复用组件逻辑的一种高级技巧,HOC自身不是React API的一部分,是一种基于React的组合特性形成的设计模式。高阶组件的参数为组件,返回值是新组件(高阶组件的本质是一个函数,类工厂)

优点:1.对原有组件增强和优化 2.可以对原有组件的state,props和逻辑执行增删改操作,一般用于代码重用和组件增强优化

应用场景:

  1. 渲染判断鉴权 (例如登录和注册)
1
2
2. 提取组件的公共逻辑,利用高级组件的形式,整合到每一个组件中,代码复用

​ 高阶组件实现的两种方式:

增强props

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
import React from "react";

function enhanceProps(Component) {
return (props) => {
return <Component {...props} region="台湾省"></Component>;
};
}

function City(props) {
return (
<div>
<h2>
{" "}
Home: {props.name}, 等级: {props.level} 区域:{props.region}
</h2>
</div>
);
}

const EnhanceCity = enhanceProps(City);

function Country(props) {
return (
<div>
<h2>
{" "}
Home: {props.name}, 等级: {props.level} 区域:{props.region}
</h2>
</div>
);
}
const EnhanceCountry = enhanceProps(Country);

function CithComponent() {
return (
<div>
hoc
<EnhanceCity name="jack" level={1}></EnhanceCity>
<EnhanceCountry name="tom" level={2}></EnhanceCountry>
</div>
);
}

export default CithComponent;


状态抽离

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
import React, { useState } from "react";

// 高阶组件抽离state
// 1.将input抽离出来,变成一个单独的组件,但是这个组件不受控,所有我们就定义了一个高阶组件
const NameInput = (props) => {
return (
<input type="text" value={props.value} onChange={props.handleChange}></input>
);
};

// 2.在高阶组件中返回一个新的组件,我们在这个组件中定义属性和方法
function withChange(Component) {
return () => {
const [value, setValue] = useState("");
const handleChange = (e) => {
setValue(e.target.value);
};
return <Component value={value} handleChange={handleChange}></Component>;
};
}
// 3.返回一个新组件
const WrappedNameInput = withChange(NameInput);

// 4.这个时候WithComponent就是一个无状态组件,他的组件都被抽离了
function WithComponent() {
return (
<div>
<h2>状态抽离</h2>
<WrappedNameInput></WrappedNameInput> <br/>
<WrappedNameInput></WrappedNameInput> <br/>
<WrappedNameInput></WrappedNameInput>
</div>
);
}

export default WithComponent;


条件渲染

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
function UserPage() {
return <h2>用户页面</h2>;
}

function AdminPage() {
return <h2>管理员页面</h2>;
}

function withAuth(Component) {
const newComponent = function (props) {
console.log('props',props);
const { isAdmin } = props;
if (isAdmin) {
return <AdminPage {...props}> </AdminPage>;
} else {
return <Component {...props}> </Component>;
}
};
newComponent.displayName = 'AuthCpn'
return newComponent;
}

const AuthComponent = withAuth(UserPage);

function ChangeComponent() {
return (
<div>
高阶组件实现登录鉴权
<AuthComponent isAdmin={true}> </AuthComponent>
{/* <AuthComponent isAdmin={false}> </AuthComponent> */}
</div>
);
}

export default ChangeComponent;

20预检请求(preflight请求)

preflight, 一个cors预检请求,属于options。该请求会在浏览器认为即将要执行的请求可能会对服务器造成不可预知的影响时,由浏览器自动发出。

21.数组去重(优雅代码)

将数据转为Set,然后再解构为数组返回

1
2
3
const uniqueArr = (arr) => [...new Set(arr)];
uniqueArr([1,2,3,2,3,3,5,6,6,7,7,7]) //[1, 2, 3, 5, 6, 7]

22.从url获取参数并转为对象(优雅代码)

1
2
3
const getParameters = URL => JSON.parse(`{"${decodeURI(URL.split("?")[1]).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"')}"}`)
getParameters("https://www.google.com.hk/search?q=js+md&newwindow=1"); //{q: 'js+md', newwindow: '1'}

23. 检查对象是否为空(优雅代码)

Reflect.ownKeys( )返回一个由目标对象自身的属性键组成的数组。它的返回值等同于 : Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))Object.keys( )返回属性key组成的数组,但不包括不可枚举的属性,Reflect.ownKeys( )返回所有属性key

1
2
3
4
const isEmpty = obj => Reflect.ownKeys(obj).length === 0 &&  obj.constructor === Object;
isEmpty({}) // true
isEmpty({a:"not empty"}) //false

24.反转字符串

使用split结合reverse,join方法轻松实现

1
2
3
4
const reverse = str => str.split('').reverse().join('');
reverse('this is reverse');
// esrever si siht

25.生成随机16进制

1
2
3
4
const randomHexColor = () => `#${Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, "0")}`
console.log(randomHexColor());
// #a2ce5b

26. 检查当前选项卡是否在后台

浏览器使用选项卡式浏览,任何网页都有可能在后台,此时对用户来说是没有在浏览的, 知道怎么快速检测到,你的网页对用户是隐藏还是可见吗?

1
2
3
4
5
const isTabActive = () => !document.hidden; 

isTabActive()
// true|false

27.检测元素是否处于焦点

activeElement 属性返回文档中当前获得焦点的元素

1
2
3
4
5
const elementIsInFocus = (el) => (el === document.activeElement);

elementIsInFocus(anyElement)
// 元素处于焦点返回true,反之返回false

28.检查设备类型

1
2
3
const judgeDeviceType = () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|OperaMini/i.test(navigator.userAgent) ? 'Mobile' : 'PC';
judgeDeviceType()

29.文字复制到剪贴板

Clipboard API 它的所有操作都是异步的,返回 Promise 对象,不会造成页面卡顿。而且,它可以将任意内容(比如图片)放入剪贴板。

1
2
3
const copyText = async (text) => await navigator.clipboard.writeText(text)
copyText('单行代码 前端世界')

30.获取用户选择的文本

1
2
3
4
5
const getSelectedText = () => window.getSelection().toString();

getSelectedText();
// 返回选中的内容

31.计算数组的平均值

1
2
3
const average = (arr) => arr.reduce((a, b) => a + b) / arr.length;
average([1,9,18,36]) //16

32.理解React Hooks的闭包陷阱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { useEffect, useState } from 'react';

function Dong() {

const [count,setCount] = useState(0);

useEffect(() => {
setInterval(() => {
setCount(count + 1);
}, 500);
}, []);

useEffect(() => {
setInterval(() => {
console.log(count);
}, 500);
}, []);

return <div>guang</div>;
}

export default Dong;

用useState创建一个count状态,在一个useEffect里定时修改它,另一个useEffect里定时打印最新的count值

打印结果不是0,1,2,3 而是 0,0,0,0

hooks原理就是在fiber节点上存放了memorizedState链表,每个hook都从对应的链表元素上存取自己的值

33.截取某个字符前面的内容

截取最后一次出现字符前面的内容:const stockCode = formValues?.stockCode.substring(formValues?.stockCode.lastIndexOf(‘.’) + 1);

34.web缓存


web缓存主要指的是两部分:浏览器缓存和http缓存

浏览器缓存:localStorage,sessionStorage, cookie等(缓存必要的数据,如用户信息,携带到后端的参数,列表数据等)

http缓存:强制缓存(强缓存)和协商缓存

1.http缓存可以减少宽带流量,加快响应速度2.缓存可以从磁盘读取,内存读取,内存读取的缓存更快3.所有带304的资源都是协商缓存,所有标注(从内存中读取/从磁盘中读取)的资源都是强缓存4.缓存主要针对html,css,img等静态资源,动态资源数据的实时性不好,一般只会缓存一些不太容易被改变的静态资源

强缓存:

  • 基于Expries字段实现的强缓存 (基本已废弃,向下兼容会用到)
  • 基于Cache-control实现的强缓存(主流用法)

Expries字段作用是,设定一个强缓存时间,在此时间范围内,则从内存(磁盘)中读取缓存返回,不在这个时间需要对服务器重新发送请求【因为Expries判断强缓存是否过期的机制是:获取本地的时间戳,并对先前拿到的资源文件中的Expries字段的时间做对比】

Expries过度依赖本地时间,如果本地与服务器时间不同步,就会出现资源无法被缓存或者资源永远被缓存的情况。所以Expries几乎不使用了

Cache-control完美解决了Expries本地时间和服务器时间不同步的问题,Cache-control:max-age=N,N就是需要缓存的秒数。从第一次请求资源的时候开始,往后N秒内,资源若再次请求,则直接从磁盘(或内存中读取),不与服务器做任何交互

1
2
3
4
res.writeHead(200,{
'Cache-Control':'max-age=10'
});

img

协商缓存:

  • 基于last-modified的协商缓存实现方式:
  • 基于ETag的协商缓存

基于last-modified的协商缓存

  1. 首先需要在服务器端读出文件修改时间
  2. 将读出来的修改时间赋给响应头的last-modified字段
  3. 最后设置Cache-control: no-cache; (Cache-control:no-cache的意思是跳过强缓存校验,直接协商缓存)
  4. 当客户端读取到last-modified的时候,会在下次请求的标头中携带一个字段: If-Modified-Since
  5. 之后每次对该资源的修改,都会带上If-Modified-Since这个字段,而服务器就需要拿到这个时间并再次读取该资源的修改时间,让他们对比来决定是缓存还是返回新资源

img

基于ETag的协商缓存

ETag就是将原先协商缓存的比较时间戳的形式修改成了比较文件指纹

  1. 第一次请求某资源的时候,服务端读取文件并计算出文件指纹,将文件指纹放在响应头的etag字段中跟资源一起返回给客户端
  2. 第二次请求某资源的时候,客户端自动从缓存中读取出上一次服务端返回的ETag也是文件指纹。并赋给请求头的if-None-Match字段,让上一次的文件指纹跟随请求一起回到服务端
  3. 服务端拿到请求头中的is-None-Match字段值(也就是上一次的文件指纹),并再次读取目标资源并生成文件指纹,两个指纹做对比。如果两个文件指纹完全吻合,说明文件没有被改变,则直接返回304状态码和一个空的响应体并return。如果两个文件指纹不吻合,则说明文件被更改,那么将新的文件指纹重新存储到响应头的ETag中并返回给客户端

img

ETag的缺点:

  • ETag需要计算文件指纹这样意味着,服务端需要更多的计算开销。如果文件尺寸大,数量多,并且计算频繁,那么ETag的计算就会影响服务器的性能。显然,ETag在这样的场景下就不是很适合。
  • ETag有强验证和弱验证,所谓将强验证,ETag生成的哈希码深入到每个字节。哪怕文件中只有一个字节改变了,也会生成不同的哈希值,它可以保证文件内容绝对的不变。但是,强验证非常消耗计算量。ETag还有一个弱验证,弱验证是提取文件的部分属性来生成哈希值。因为不必精确到每个字节,所以他的整体速度会比强验证快,但是准确率不高。会降低协商缓存的有效性。
  1. 能用Cache-control的就不要用expries
  2. 项目中用ETag还是last-modified完全取决于业务场景。

怎样设置缓存:

webpack打包后有哈希值的文件设置强缓存即可,没有哈希值的文件(比如index.html)设置协商缓存

评论