前端基础知识回顾
一段html代码
1 | <body> |
一、数组
slice() 和 splice()
colorList.slice(1) –> [‘green’, ‘orange’]
colorList.slice(-2) –> [‘green’, ‘orange’]
colorList.slice(1, 2) –> [‘green’]
colorList.splice(2, 1) –> [“orange”]; colorList –> [‘red’, ‘green’]nodeList.slice(1) –> Uncaught TypeError: nodeList.slice is not a function
Array.prototype.slice.call(nodeList, 1) –> [li, li]isArray 及 Polyfill
Array.isArray(colorList) –> true
Array.isArray(nodeList) –> false1
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
44if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
```
3. ArrayLike => Array 几种方式
> var arr = Array.from(nodeList); Array.isArray(arr) --> true
> var arr = [...nodeList]; Array.isArray(arr) --> true
> var arr = Array.prototype.slice.call(nodeList); Array.isArray(arr) --> true
#### 二、事件监听
1. DOM0、DOM2、DOM3
> DOM0 注意 onclick 大小写 document.querySelector('ul').onclick = function () { alert('hello world') }
> DOM2是通过addEventListener绑定的事件, 还有IE下的DOM2事件通过attachEvent绑定
2. addEventListener第三个参数如果是 useCapture: true 和 false 分别代表什么?事件传播方式(事件冒泡、事件捕获),分别讲述两者不同
> target.addEventListener(type, listener, useCapture);
3. 两种事件传播方式的执行顺序,console.log() 执行顺序 (默默的思念旭明1秒钟)
> 1:事件捕获, 2:处于目标阶段, 3:事件冒泡阶段
``` javascript
var ulEventTarget = document.querySelector('ul')
ulEventTarget.addEventListener('click', function (e) {
console.log('ul', '捕获')
}, true)
ulEventTarget.addEventListener('click', function (e) {
console.log('ul', '冒泡')
}, false)
nodeList[0].addEventListener('click', function (e) {
console.log('li', '冒泡')
}, false)
nodeList[0].addEventListener('click', function (e) {
console.log('li', '捕获')
}, true)点击穿透 stopPropagation() 取消冒泡或者捕获 及 取消跳转:preventDefault() 取消默认事件;
三、作用域 – 最小访问
1 | for (var i = 0; i < nodeList.length; i++) { |
- i –> 3
- colorList[i] –> undefined
如何解决
3.1 简单方便的绑定一次,复制两次,改一改,优点:缓解脱发
1
2
3
4
5
6
7
8
9nodeList[0].addEventListener('click', function (e) {
e.target.style.color = colorList[0]
})
nodeList[1].addEventListener('click', function (e) {
e.target.style.color = colorList[1]
})
nodeList[2].addEventListener('click', function (e) {
e.target.style.color = colorList[2]
})
> 3.2 发现代码冗余后,抽取一个公共方法出来,一个莫名其妙的闭包就诞生了
1
2
3
4
5
6
7
8
var handleEvent = function (color) {
return function (e) {
e.target.style.color = color
}
}
nodeList[0].addEventListener('click', handleEvent(colorList[0]))
nodeList[1].addEventListener('click', handleEvent(colorList[1]))
nodeList[2].addEventListener('click', handleEvent(colorList[2]))
> 3.3 发现可以帅帅的写个循环,发现代码行数竟然没啥变化,好沮丧啊!
> 闭包 密闭的空间,块级作用域
> 执行方法的时候,可以创建一个新方法,同时开辟一块空间
> 此时新函数,保留了创建函数时的所需要参数 color 的值
> (录制了案发现场,储存 color 路过时的痕迹)
>
> console.log(this) --> `<li>红灯</li>`
> console.log(this) --> `<li>绿灯</li>`
> console.log(this) --> `<li>黄灯</li>`
1
2
3
4
5
6
7
8
9
10
var handleEvent = function (color) {
return function (e) {
e.target.style.color = color
console.log(this)
}
}
for (var i = 0; i < nodeList.length; i++) {
nodeList[i].addEventListener('click', handleEvent(colorList[i]))
}
四、ES6
1 | const len = nodeList.length |
箭头函数
console.log(this) –> Window (指向外层 this)
阐述 let & const & var 区别、作用.
let、const 块级作用域, const/let 不/允许重新赋值.
colorList[i] –> ‘red’
colorList[i] –> ‘green’
colorList[i] –> ‘orange’细节 避免重复查询数组长度(length)
const len = nodeList.length
for of 及 array.entries() 的应用
1 | const handleEvent = color => e => e.target.style.color = color |
五、类、oop
类的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function Light(dom, color = "grey") {
this.color = color
this.isLight = false
this.dom = dom
this.turnOn = function() {
this.isLight = true
this.dom.style.color = this.color
}
this.turnOff = () => {
this.isLight = false
this.dom.style.color = 'grey'
}
this.switch = () => {
this.isLight ? this.turnOff() : this.turnOn()
}
}事件代理 及 switch case 使用 && LampBox.red.switch === LampBox.green.switch ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23var LampBox = {}
colorList.forEach((color, index) => {
LampBox[color] = new Light(document.querySelectorAll('li')[index], color)
})
// console.log(LampBox.red.switch === LampBox.green.switch) false
document.querySelector('ul').addEventListener('click', e => {
switch (e.target.innerText) {
case '红灯':
LampBox.red.switch()
break;
case '绿灯':
LampBox.green.switch()
break;
case '黄灯':
LampBox.orange.switch()
break;
default:
console.warn('bulubulu')
}
})
> 借助 prototype 修改 Light
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Light(dom, color = "grey") {
this.color = color
this.isLight = false
this.dom = dom
}
Light.prototype.turnOn = function() {
this.isLight = true
this.dom.style.color = this.color
}
Light.prototype.turnOff = function() {
this.isLight = false
this.dom.style.color = 'grey'
}
Light.prototype.switch = function() {
this.isLight ? this.turnOff() : this.turnOn()
}
Map 操作 get、set
1
2
3
4
5
6var map = new Map()
for (var i = 0; i < nodeList.length; i++) {
map.set(nodeList[i], new Light(nodeList[i], colorList[i]))
}
document.querySelector('ul').addEventListener('click', e => map.get(e.target).switch())css 动画、异步操作
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
42var map = new Map()
function doScaledTimeout(i, time, cb) {
setTimeout(function() {
cb && cb(i)
}, time);
}
const createLight = (e, color) => {
if (color !== 'orange') {
return new Light(e, color)
}
var yellowL = new Light(e, color)
yellowL.turnOn = function() {
this.isLight = true
// css 动画
this.dom.style.transition = 'color'
this.dom.style.transitionDuration = '.5s'
// color: 1000 yellow 1500 grey 2000 yellow 2500 grey 3000 yellow 3500 grey s
var i = 0
while (i < 3) {
// 异步操作
setTimeout(() => {
this.dom.style.color = this.color
}, 1000)
setTimeout(() => {
this.dom.style.color = 'grey'
}, 500)
i++
}
}
return yellowL
}
for (var i = 0; i < nodeList.length; i++) {
map.set(nodeList[i], createLight(nodeList[i], colorList[i]))
}
document.querySelector('ul').addEventListener('click', e => map.get(e.target).switch())
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
while (i < 3) {
// 需要闭包
setTimeout(() => {
this.dom.style.color = this.color
}, i * 1000);
setTimeout(() => {
this.dom.style.color = 'grey'
// i => 3
if (i === 2) {
this.isLight = false
}
}, i * 1000 + 500);
i++
}
while (i < 3) {
doScaledTimeout(i, i * 1000, () => {
this.dom.style.color = this.color
})
doScaledTimeout(i, i * 1000 + 500, (i) => {
this.dom.style.color = 'grey'
if (i === 2) {
this.isLight = false
}
})
i++
}
Promise 解决方案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function turnOn() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
this.dom.style.color = this.color
resolve(500)
}, 1000);
}.bind(this))
}
function turnOff() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
this.dom.style.color = 'grey'
resolve()
}, 500);
}.bind(this))
}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
44function doPromiseTimeout(i) {
return new Promise((resolve, reject) => {
setTimeout(() => {
this.dom.style.color = this.color
resolve(500)
}, 1000);
}).then(delayMS => {
return new Promise((resolve, reject) => {
setTimeout(() => {
this.dom.style.color = 'grey'
resolve(delayMS)
}, delayMS);
});
});
}
const createLight = (e, color) => {
if (color !== 'orange') {
return new Light(e, color)
}
const yellowL = new Light(e, color)
yellowL.turnOn = function() {
this.isLight = true
// css 动画
this.dom.style.transition = 'color'
this.dom.style.transitionDuration = '.5s'
const doTurnOn = doPromiseTimeout.bind(this)
doTurnOn(0)
.then(() => doTurnOn(1))
.then(() => doTurnOn(2))
}
return yellowL
}
var map = new Map()
for (var i = 0; i < nodeList.length; i++) {
map.set(nodeList[i], createLight(nodeList[i], colorList[i]))
}
document.querySelector('ul').addEventListener('click', e => map.get(e.target).switch())
while promise
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
28const createLight = (e, color) => {
if (color !== 'orange') {
return new Light(e, color)
}
const yellowL = new Light(e, color)
yellowL.turnOn = function() {
this.isLight = true
// css 动画
this.dom.style.transition = 'color'
this.dom.style.transitionDuration = '.5s'
const doTurnOn = doPromiseTimeout.bind(this)
var blink = Promise.resolve()
var i = 0;
while (i < 3) {
blink = blink.then(() => doTurnOn(i))
i++
}
blink.then(() => {
this.isLight = false
})
}
return yellowL
}
- 子类 继承
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
// 闪烁类
function Blink(dom, color = "grey", size) {
// 1. 构造器继承 缺点,修改 this 上的方法时,父类的方法也会被修改
Light.call(this, dom, color)
this.size = size
}
/**
* 2. 继承 父类的 Light.prototype ,
* 为什么不使用 new Light() 来进行继承?
* 因为理论上 Light 类实例化的时候时 是需要参数的,这个时候仅仅是继承,传与不传都不太合适
*
*/
Blink.prototype = Object.create(Light.prototype);
/**
* Blink.prototype.constructor === Light true
*
* 3. new Blink(...size) 时,
* size依旧会传进来,对this.size 进行赋值,
* 所以,不写也不会出错,只是有点奇怪,
* 因为指向了 父类的构造器,而不是自己
*/
Blink.prototype.constructor = Blink;
Blink.prototype.blinkOn = function(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
this.dom.style.color = this.color
resolve()
}, ms);
})
}
Blink.prototype.blinkOff = function(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
this.dom.style.color = 'grey'
resolve(ms)
}, ms);
})
}
Blink.prototype.doPromiseTimeout = function(ms) {
return this.blinkOn(ms).then(() => this.blinkOff(500))
}
Blink.prototype.turnOff = function() {
setTimeout(() => {
this.isLight = false
this.dom.style.color = 'grey'
this.dom.style.fontSize = '12px'
}, 500);
}
Blink.prototype.turnOn = function() {
this.isLight = true
this.dom.style.transition = 'color'
this.dom.style.transitionDuration = '.5s'
this.dom.style.fontSize = this.size
let blink = Promise.resolve(1000)
let i = 0;
while (i < 3) {
blink = blink.then((ms) => this.doPromiseTimeout(ms))
i++
}
blink = blink.then(() => this.turnOff())
}
1
2
3
4
5
6
const createLight = (e, color) => {
if (color !== 'orange') {
return new Light(e, color)
}
return new Blink(e, color, '24px')
}
- 如何实现一个 promise.all (待完善)
- generator yield next (待完善)
- async await (待完善)
实现 bind()
const bar = foo.bind(this)
bar()1
2
3
4
5
6Function.prototype.bind = function (ctx) {
var that = this;
return function () {
that.apply(ctx);
}
}const bar = foo.bind(this)
bar(arg1, arg2)1
2
3
4
5
6Function.prototype.bind = function(ctx) {
var that = this;
return function() {
return that.apply(ctx, arguments);
}
}const bar = foo.bind(this, arg1, arg2)
bar(arg3)1
2
3
4
5
6
7
8
9Function.prototype.bind = function (ctx) {
var that = this;
var args = Array.prototype.slice.call(arguments, 1);
return function () {
var bindArgs = Array.prototype.slice.call(arguments);
that.apply(ctx, args.concat(bindArgs));
}
}当作为构造函数时,this 指向实例,that 指向绑定函数,因为下面一句
fbound.prototype = this.prototype;
,已经修改了 fbound.prototype 为 绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 ctx。
1
2
3
4
5
6
7
8
9
10
11
12
13Function.prototype.bind = function (ctx) {
var that = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fbound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
that.apply(this instanceof fNOP ? this : ctx, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fbound.prototype = new fNOP();
return fbound;
}e.g.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function Point(x, y) {
this.x = x;
this.y = y;
this.getPoint = function() {
console.log(this)
}
}
var p = new Point(1, 2);
p.getPoint(); // '1,2'
var Point2 = Point.bind2(this, 0);
var p2 = new Point2(5);
p2.getPoint(); // '0,5'
console.log(p2 instanceof Point, p2 instanceof Point2, p instanceof Point2);我们直接将
fbound.prototype = this.prototype
,我们直接修改 fbound.prototype 的时候,也会直接修改函数的 prototype。so:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Function.prototype.bind = Function.prototype.bind || function (ctx, ...formerArgs) {
if (typeof this !== 'function') {
throw new TypeError("NOT_A_FUNCTION -- this is not callable");
}
let that = this;
let fNOP = function () {};
let args = Array.prototype.slice.call(arguments, 1);
let fbound = function (...laterArgs) {
that.apply(this instanceof fNOP ? this : ctx, args.concat(laterArgs));
};
if (this.prototype) {
fNOP.prototype = this.prototype;
}
fbound.prototype = new fNOP();
return fbound;
};