关于0.1+0.2==0.3 false
0.1 + 0.2不等于0.3?为什么JavaScript有这种“骚”操作?
关于浮点数的二进制表示,js浮点数精度(存目)
保留2位小数
Number.prototype.toFixed 返回指定小数位数的字符串 必要时四舍五入 且必要时以0补足位数
返回number的方法不如用类似 Math.round(num * 100) / 100 保留两位小数
动态语言
Dynamic Programming Language 动态语言或动态编程语言,程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。动态语言如Javascript Python Ruby等 C/C++ Jave则不是
另外 图灵完备
JS Tips
Caps lock sensitive
1 | var input = document.getElementsByTag('input')[0]; |
getModifierState方法挂在KeyboardEvent或者MouseEvent上,可获取的键盘状态见MDN:KeyboardEvent.getModifierState()
运算符
- 幂运算 2**10=1024
this
this的设计目的是在函数体内部,指代函数当前的运行环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 全局环境执行
f() // 1
// obj 环境执行
obj.f() // 2
另外,严格模式(‘use strict’)下,全局环境下运行时,this不会自动绑定到‘全局对象’上,将变量绑定到全局对象需要显式调用形如global.name = ‘Global’
常用处理
应用场景标题 | 描述 | 补充1 | 补充2 | |
---|---|---|---|---|
数组去重 | Array.from(new Set(array)) | 通过内置数据解构特性进行去重 [] => set => [] |
通过遍历并判断是否存在进行去重[many items].forEach(item => (item <不存在于> uniqueArr) && uniqueArr.push(item)) |
|
数组的最后一个元素 | 获取数组中位置最后的一个元素 | 使用array.at(-1) |
略 | |
数组对象的相关转换 | 略 | 对象到数组:Object.entries() |
数组到对象:Obecjt.fromEntries() |
|
短路操作 | 通过短路操作避免后续表达式的执行 | a或b :a真b不执行 |
a且b :a假b不执行 |
|
基于默认值的对象赋值 | 通过对象解构合并进行带有默认值的对象赋值操作 | {...defaultData, ...data} |
略 | |
多重条件判断优化 | 单个值与多个值进行对比判断时,使用includes 进行优化 |
[404,400,403].includes |
略 | |
交换两个值 | 通过对象解构操作进行简洁的双值交换 | [a, b] = [b, a] | 略 | |
位运算 | 通过位运算提高性能和简洁程度 | 按位与(&)或(\ | )按位取反(~) 取整~~、<<、>> | >>>移位 |
replace() 的回调 |
通过传入回调进行更加细粒度的操作 | 略 | 略 | |
sort() 的回调 |
通过传入回调进行更加细粒度的操作 | 根据字母顺序排序 | 根据真假值进行排序 |
call 和 apply
1 | // 已有构造函数 |
FunctionX.call(thisArg,…args)中的第一个参数也可以是对象,函数相当于将剩余参数以FunctionX的实现方式应用到该对象内返回一个结果
apply与call的区别仅在:剩余参数是数组,即FunctionX.call(thisArg,[…args])
bind
bind是基于call或apply实现的函数原型方法 FunctionX.bind(thisArg,…args)返回一个新的函数,该函数套用FunctionX的样子,但以thisArg代替FunctionX的this,剩余参数填补FunctionX所需参数(即结果函数所需参数为FunctionX所需参数减去…args)
模拟实现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
30Function.prototype.bind = function (context) {
// 调用 bind 的不是函数,需要抛出异常
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
// this 指向调用者
var self = this;
// 实现第2点,因为第1个参数是指定的this,所以只截取第1个之后的参数
var args = Array.prototype.slice.call(arguments, 1);
// 创建一个空对象
var fNOP = function () {};
// 实现第3点,返回一个函数
var fBound = function () {
// 实现第4点,获取 bind 返回函数的参数
var bindArgs = Array.prototype.slice.call(arguments);
// 然后同传入参数合并成一个参数数组,并作为 self.apply() 的第二个参数
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
// 注释1
}
// 注释2
// 空对象的原型指向绑定函数的原型
fNOP.prototype = this.prototype;
// 空对象的实例赋值给 fBound.prototype
fBound.prototype = new fNOP();
return fBound;
}
关于替换this的操作可用于回调函数调用类函数, 如下类型A中定义回调函数,该回调函数会作为目标对象B的函数形参,process是类A的函数,但如果在目标对象B中调用callback函数,函数中的this会指向到B1
2
3
4
5callback = (response)=>{
this.process(response) // 这里的this指向当前运行环境
}
B.getDataAsync(this.callback)
应使用bind1
B.getDataAsync(this.callback.bind(this))
柯里化Currying
1 | style=function(){this.x="";this.y="";} // 构造函数style |
Garbage Collection
游离dom
1
2
3
4
5
6let root=document.getElementById("root")
for(let i=0;i<2000;i++){
let div=document.createElement("div")
root.appendChild(div)
}
document.body.removeChild(root)此时document中移除了dom,但内存中root还在,而且上千个子元素仍然被root对象引用,应设root=null解除引用
clearInterval和clearTimeOut
关于严格模式
通知浏览器以严格模式运行脚本,严格模式下会将一些在非严格模式下被忽略的错位识别为异常,一些静默行为(无效但非异常操作)会被禁止
- 防止无意添加全局变量,声明全局变量必须用global关键字
- 静默行为被禁止 抛出TypeError
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 如无效的赋值
NaN=0
// 写入不可写属性
var obj={}
Object.defineProperty(obj, 'x', {value:0, writable:false})
// 写入只读
var obj={
get x(){
return 0;
}
}
obj.x = 2
// 扩展不可扩展对象
var fixed = {}
Object.preventExtensions(fixed)
fixed.newProp = 0vanilla.js
it’s a joke. 这是一个空的js,用以提醒其他开发者使用plain JavaScript实现,而不要引入任何library
task 和 microtask
Tasks, microtasks, queues and schedules1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19console.log('script start'); // task
setTimeout(function () {
console.log('setTimeout'); // scheduled task
}, 0);
Promise.resolve()
.then(function () {
console.log('promise1'); // microtask
})
.then(function () {
console.log('promise2');
});
// 结果:
// script start
// promise1
// promise2
// setTimeout
至少Chrome中, Promise.resolve().then()中的回调作为microtask会立即排队等待当前任务执行完, 而setTimeout的回调则在给定延迟之后,排队任务队列中。
节流和防抖
防抖,全时间内后面的覆盖前面的1
2
3
4
5
6
7
8
9function debounce(fn, wait){
let timeout
return function(...args){
clearTimeout(timeout)
timeout = setTimeout(()=> {
fn.apply(this, args)
}, wait)
}
}
事实上带有 this 的函数在使用生命周期钩子的vue/react组件中可能并不好用,应在组件范围声明timer, 在响应函数中clear并重新赋值
注意组件unmount时清理timer避免泄露
节流1
2
3
4
5
6
7
8
9
10
11var throttle = function(delay, action){
// 这里是个全局变量
var last = 0
return function(){
var curr = new Date()
if (curr - last > delay){
action.apply(this, arguments)
last = curr
}
}
}