前端基础知识回顾
一段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) –> false- 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- if (!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
 9- nodeList[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
 18- function 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
 23- var 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
 6- var 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
 42- var 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
 17- function 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
 44- function 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
 28- 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)
 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
 6- Function.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
 6- Function.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
 9- Function.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
 13- Function.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
 17- function 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
 19- Function.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;
 };