JavaScript 踩坑指南 (opens new window)
# new操作符
function new(func, ...args) {
const newObject = Object.create(func.prototype);
const k = func.apply(newObject, ...args)
if(typeof k === 'object') {
return k
} else {
return newObject
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# event loop(事件循环/事件轮询)
- JS单线程
- 异步要基于回调来实现
- event loop就是异步回调的实现原理
JS执行步骤:从前到后,一行一行执行、错误终止执行、先执行同步,再执行异步
菲利普·罗伯茨:到底什么是Event Loop呢? | 欧洲 JSConf 2014 (opens new window)
// Call Stack
function multiply(a, b) {
return a * b;
}
function square(n) {
return multiply(n, n);
}
function printSquare(n) {
var squared = square(n);
console.log(squared);
}
printSquare(4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Promise 处理异步
const handlerClick = () => {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.apiopen.top/getJok');
xhr.send()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if(xhr.status >= 200 && shr.status < 300) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
})
p.then((value) => {
}, error => {
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 自定义promise
function MyPromise(executor) {
// 保存初始化状态
var self = this;
// 初始化状态
this.state = PENDING;
// 用于保存 resolve 或者 rejected 传入的值
this.value = null;
// 用于保存 resolve 的回调函数
this.resolvedCallbacks = [];
// 用于保存 reject 的回调函数
this.rejectedCallbacks = [];
// 状态转变为 resolved 方法
function resolve(value) {
// 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变,
if (self.state === PENDING) {
// 修改状态
self.state = RESOLVED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.resolvedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 状态转变为 rejected 方法
function reject(value) {
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变
if (self.state === PENDING) {
// 修改状态
self.state = REJECTED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.rejectedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 将两个方法传入函数执行
try {
fn(resolve, reject);
} catch (e) {
// 遇到错误时,捕获错误,执行 reject 函数
reject(e);
}
}
MyPromise.prototype.then = function (onFulfilled, onReject){
// 保存前一个promise的this
const self = this;
return new MyPromise((resolve, reject) => {
// 封装前一个promise成功时执行的函数
let fulfilled = () => {
try{
const result = onFulfilled(self.value); // 承前
return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //启后
}catch(err){
reject(err)
}
}
// 封装前一个promise失败时执行的函数
let rejected = () => {
try{
const result = onReject(self.reason);
return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
}catch(err){
reject(err)
}
}
switch(self.status){
case PENDING:
self.onFulfilledCallbacks.push(fulfilled);
self.onRejectedCallbacks.push(rejected);
break;
case FULFILLED:
fulfilled();
break;
case REJECT:
rejected();
break;
}
})
}
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# 柯里化
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args)
} else {
return function (...args2) {
return curried.apply(this, args.concat(args2));
}
}
}
}
function curry(fn, ...arges) {
return fn.length <= args.length
? fn(...args)
: curry.bind(null, fn, ...args);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 深拷贝
const object = {
a: 1,
b: 'qwe',
c: [1,2,3],
d: {
test: '',
}
}
JSON.parse(JSON.stringify())
const deepClone = (cloneData) => {
if(typeof cloneData !== 'object' || cloneData == null) {
return cloneData
}
let result
if(cloneData instanceof Array) {
result = []
} else {
result = {}
}
for(let key in cloneData) {
// key 不是原型属性
if(cloneData.hasOwnProperty(key)) {
// 递归调用
result[key] = deepClone(cloneData[key])
}
}
return result
}
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
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
# 防抖
- 防止多次触发
function debonce(func, delay) {
let timer;
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func();
}, delay);
}
function debonce(func, delay) {
let timer;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
func.apply(context, args)
timer = null;
}, delay)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 节流
- 防止多次发出
function throttle(func, delay) {
let timer;
return function () {
if(timer) return
timer = setTimeout(() => {
func.apply(context, arguments);
timer = null;
}, delay)
}
}
// setTimeOut
function throttle(func, delay) {
let timer;
return function () {
if(timer) {
return;
}
let context = this;
let args = arguments;
timer = setTimeout(function () {
func.apply(context, args);
timer = null;
}, delay)
}
}
// Date
function throttle(func, delay) {
let pre = 0;
return function () {
let context = this;
let args = arguments;
let now = new Date();
if(now - pre > delay) {
func.apply(context, args);
pre = now;
}
}
}
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
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
# call、apply、bind
// call
// Function.prototype.newCall = function(obj) {
// var obj = obj || window;
// obj.p = this;
// var newArguments = [];
// for (var i = 1; i < arguments.length; i++) {
// newArguments.push(`argumen[${i}]`)
// }
// var result = eval(`obj.p(${newArguments})`)
// delete onj.p;
// }
// // apply
// Function.prototype.newApplay = function(context) {
// let result = null;
// }
Function.prototype.newCall = function(obj) {
let result = null;
let args = argument.slice(1)
obj.fn = this;
obj.fn();
delete obj.p
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# map
Array.prototype._map(fn) {
for (let i = 0; i < this.length; i ++) {
return fn(this[i]);
}
}
1
2
3
4
5
2
3
4
5
# Search获取参数
function getSearch(name) {
return (new URLSearchParams(window.location.search)).get(name)
}
function getSearch() {
const res = {}
const search = location.search.substr(1)
search.splite('&').forEach((paramstr) => {
const arr = paramStr.split('=');
const key = arr[0]
const val = arr[1]
res[key] = val
})
return res
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# EventEmitter
class EventEmitter {
constructor(defaultMaxListeners = 50) {
this.listeners = {};
this.defaultMaxListeners = defaultMaxListeners;
}
on(eventName, fn) {
if (!this.listener[eventName]) {
this.listeners[eventName] = [];
}
if(this.listeners[eventName].length > this.defaultMaxListeners) {
throw new Error('超出限制');
}
this.listeners[eventName].push(fn);
}
off(eventName, fn) {
let callbacks = this.listener[eventName];
if(!callbacks) return false;
if(!fn) {
callbacks = [];
} else {
for(let i = 0; i< callbacks.length; i++) {
if(callbacks[i] === fn) {
callbacks.splice(i,1)
i--;
}
}
}
}
once(eventName, fn) {
const on = (...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
30
31
32
33
34
35
36
37
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
# iFrame
iFrame 知识总结
# JSBridge 相关知识点
现在 hybrid 应用越来越多,不管在移动端或者PC、Mac端,都需要用到JSBridge