├── .gitignore
├── 00-ES6
├── 01-let & const
│ └── index.js
├── 02-Template_string
│ └── index.js
├── 03-Destructuring
│ └── index.js
├── 04-Block_scope
│ └── index.js
├── 05-Function
│ ├── 01-Default_Args
│ │ └── index.js
│ └── 02-Arrow_Function
│ │ ├── index.html
│ │ └── index.js
├── 06-Spread
│ └── README.md
└── README.md
├── 01-Algorithm
├── 01-Recursion
│ ├── README.md
│ └── index.js
├── 02-Factorial
│ └── factorial.js
├── 03-Palindrome
│ └── 01-Palindrome.js
├── 04-SubString
│ └── SubString.js
├── 05-Sort
│ ├── README.md
│ ├── bubbleSort.js
│ ├── quickSort.js
│ ├── selectionSort.js
│ └── shellSort.js
├── 06-Cache Algorithm
│ └── FIFO.js
├── 07-Search Algorithm
│ └── README.md
├── Demo
│ └── 01-Longest String.js
└── README.md
├── 02-Higher-order Function
├── 01-Introduction.js
├── 02-Map.js
├── 03-Reduce.js
├── 04-Filter.js
├── 05-Sort.js
├── 06-Some.js
├── 07-Every.js
└── README.md
├── 03-General Knowledge
├── 01-Falsy.html
├── 02-Truthy.js
├── 03-~&~~
│ └── index.js
├── 04-Shift Operators
│ └── index.js
└── README.md
├── 04-Async
├── 01-Highorder.js
├── 02-Callback.js
├── 03-Generator.js
├── 04-Promise.js
├── 05-Async+Await.js
├── 06-CreatePromise.js
├── README.md
├── a.txt
├── b.txt
├── c.txt
└── test.js
├── 05-Regexp
├── 01-Grammar.js
└── README.md
├── 06-Date
├── 01-ParseTime.js
└── README.md
├── 07-OOP
├── 01-Create Object.js
├── 02-Inheritance.js
├── 03-Class.js
└── README.md
├── 08-This
├── README.md
├── apply.js
├── bind.js
├── call.js
├── index.html
├── index.js
└── useStrict.js
├── 09-Skills
├── 01-shuffle
│ ├── README.md
│ ├── index.html
│ ├── self-training.html
│ └── training.js
├── 02-debounce
│ ├── index.html
│ └── index.js
├── 03-traffic light
│ └── index.js
├── 04-throttle
│ └── index.js
├── 05-AccessNestObject
│ ├── README.md
│ └── index.js
└── README.md
├── 10-Design-patterns
├── 01-Factory
│ └── index.js
└── 02-Singleton
│ └── index.js
├── 11-Event Loop
├── 01-stack
│ ├── index.html
│ └── index.js
└── README.md
├── 12-MVVM
└── 01-defineProperty
│ ├── compile.js
│ ├── index.html
│ ├── mvvm.js
│ ├── observer.js
│ └── watcher.js
├── 13-Prototype
├── JQuery
│ └── index.js
├── README.md
├── Zepto
│ └── index.js
└── index.html
├── 14-DataStructure
└── Linked-list
│ └── README.md
├── 15-TypeScript
└── README.md
├── 16-Source Code
├── README.md
└── splice.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # Editor directories and files
6 | .idea
7 | .vscode
--------------------------------------------------------------------------------
/00-ES6/01-let & const/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-18
5 | **/
6 |
7 | // js
8 | // var i = 10
9 | // i = 100
10 | // var j = 20
11 | // j = 200
12 |
13 | // es6
14 | let i = 10
15 | i = 100 // 正确
16 | const j = 20
17 | j = 200 // 报错
18 |
--------------------------------------------------------------------------------
/00-ES6/02-Template_string/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-18
5 | **/
6 |
7 | // 模板字符串
8 |
9 | // js
10 |
11 | // var name = 'stephent'
12 | // var age = 18
13 | // var html = ''
14 |
15 | // html = 'My name is ' + name + ',' + 'I\'m' + age + 'years old.'
16 |
17 | // ES6
18 | const name = 'stephen'
19 | const age = 18
20 | const html = `My name is ${name} , I'm ${age} years old.`
21 |
22 | console.log(html)
23 |
--------------------------------------------------------------------------------
/00-ES6/03-Destructuring/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-18
5 | **/
6 |
7 | // 变量的解构赋值
8 |
9 | // js
10 |
11 | // var obj = { a: 100, b: 200 }
12 | // var a = obj.a
13 | // var b = obj.b
14 |
15 | // var arr = ['xx', 'xxx', 'x']
16 | // var x = arr[0]
17 |
18 | // es6
19 |
20 | const obj = { a: 10, b: 20, c: 30 }
21 | const { a, c } = obj
22 | console.log(a)
23 | console.log(c)
24 |
25 | const arr = ['xx', 'xxx', 'x']
26 | const [x, y, z] = arr
27 | console.log(x)
28 | console.log(y)
29 | console.log(z)
30 |
--------------------------------------------------------------------------------
/00-ES6/04-Block_scope/index.js:
--------------------------------------------------------------------------------
1 | // 块级作用域
2 |
3 | // js
4 |
5 | var obj = { a: 100, b: 200 }
6 | for (var i in obj) {
7 | console.log(i)
8 | }
9 | console.log(i) // b
10 |
11 | // es6
12 |
13 | const obj1 = { a: 10, b: 20 }
14 | for (let n in obj1) {
15 | console.log(n)
16 | }
17 | console.log(n) // undefined
--------------------------------------------------------------------------------
/00-ES6/05-Function/01-Default_Args/index.js:
--------------------------------------------------------------------------------
1 | // 默认参数
2 |
3 | // js
4 |
5 | function A(a, b) {
6 | if (b == null) {
7 | b = 0
8 | a = 1
9 | }
10 | console.log(a)
11 | }
12 |
13 | // es6
14 |
15 | function B(a, b = 0) {
16 | a = 1
17 | console.log(a, b)
18 | }
19 |
--------------------------------------------------------------------------------
/00-ES6/05-Function/02-Arrow_Function/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 箭头函数 this
9 |
10 |
11 |
12 |
13 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/00-ES6/05-Function/02-Arrow_Function/index.js:
--------------------------------------------------------------------------------
1 | // 箭头函数
2 |
3 | // js
4 | var arr = [1, 2, 3]
5 | arr.map(function (item) {
6 | return item + 1
7 | })
8 |
9 | // es6
10 | const arr1 = [1, 2, 3, 4]
11 | arr1.map(item => item + 1)
12 | arr1.map((item, index) => {
13 | console.log(index)
14 | return item + 1
15 | })
16 |
17 | // js
18 |
19 | function fn() {
20 | console.log('real', this)
21 | var arr2 = [1, 2, 3]
22 | // js
23 | arr2.map(function (item) {
24 | console.log('js', this)
25 | return item + 1
26 | })
27 |
28 | // 箭头函数
29 | arr2.map(item => {
30 | console.log('es6', this)
31 | return item + 1
32 | })
33 | }
34 | fn.call({ a: 100 })
35 |
--------------------------------------------------------------------------------
/00-ES6/06-Spread/README.md:
--------------------------------------------------------------------------------
1 | # 扩展运算符(spread)
2 |
3 | ## es5 实现扩展符
4 |
5 | ```javascript
6 | ...a
7 |
8 | =
9 |
10 | function _toConsumableArray (e) {
11 | return _arrayWithoutHoles(e) || _iterableToArray(e) || _nonIterableSpread();
12 | }
13 |
14 | function _nonIterableSpread () {
15 | throw new TypeError("Invalid attempt to spread non-iterable instance");
16 | }
17 |
18 | // 将 e 变换为数组
19 | function _iterableToArray (e) {
20 | if (
21 | Symbol.iterator in Object(e) ||
22 | "[object Arguments]" === Object.prototype.toString.call(e)
23 | )
24 | return Array.from(e);
25 | }
26 | // 拷贝数组并返回
27 | function _arrayWithoutHoles (e) {
28 | if (Array.isArray(e)) {
29 | for (var t = 0, o = new Array(e.length); t < e.length; t++) o[t] = e[t];
30 | return o;
31 | }
32 | }
33 |
34 | ```
35 |
--------------------------------------------------------------------------------
/00-ES6/README.md:
--------------------------------------------------------------------------------
1 | # ES6 常见知识
2 |
3 | ## 目录
4 |
5 | - let/const
6 | - 块级作用域(Block scope)
7 | - 字符串扩展
8 | - 模板字符串(Template string)
9 | - 解构赋值(Destructuring)
10 | - 函数扩展(Funnction)
11 | - 函数默认参数
12 | - 箭头函数
13 | - 箭头函数
14 |
15 | ### let/const
16 |
17 | #### let
18 |
19 | let 用来声明变量, 作用类似 var;
20 | 但是 let 声明的变量, 只在所写的代码块有效.
21 |
22 | 比如中函数体内使用 let 声明变量, 在函数外部使用会报错.
23 |
24 | #### const
25 |
26 | const 是用来声明一个不可变的常量
27 |
28 | ```js
29 | const PI = 3.1415;
30 | PI = 3.14; // TypeError: Assignment to constant variable
31 | ```
32 |
33 | ### template string
34 |
35 | 模板字符串顾名思义,就是在字符串的基础上,使用变量,甚至进行简单的计算。
36 | 使用方式:
37 |
38 | ```js
39 | var n = "stephen";
40 | console.log(`my name is ${n}`);
41 | // 等价于
42 | console.log("my name is" + n + "");
43 | ```
44 |
45 | ### spread operator in es5
46 |
47 | ES5 实现扩展府。先了解扩展符的功能:
48 |
49 | - 使用在函数参数, 相当于 apply
50 | - 对数组进行操作, 相当于 concat
51 | - 对对象进行浅拷贝, 相当于 Object.assign()
52 |
--------------------------------------------------------------------------------
/01-Algorithm/01-Recursion/README.md:
--------------------------------------------------------------------------------
1 | # 递归 Recursion
2 |
3 | ## 何为递归
4 |
5 | 程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用。
6 |
7 | **举个例子:**
8 | 比如说阶乘,
9 | ```
10 | factorial(5) = factorial(4) * 5
11 | factorial(4) = factorial(3) *4
12 | ...
13 | ...
14 | ...
15 | factorial(0) = 1
16 | ```
17 | 从第一个调用 factorial(5) 开始,一直递归调用 `factorial` 函数直到参数为 0。
18 |
19 | 总结来说:
20 | ```
21 | factorial(n) = factorial(n-1) ) n
22 | factorial(0) = 1
23 | ```
24 |
25 |
26 | ## 递归调用栈
27 |
28 | 回到上面的例子:
29 | ```
30 | function factorail(n) {
31 | if (n === 0) return 1
32 | return n * factorial(n - 1)
33 | }
34 | ```
35 |
36 | 如果我们传入参数 3,将会递归调用 factorial(2)、factorial(1) 和 factorial(0),
37 | 会调用 3 次。
38 | 每次函数调用都会压入调用栈,如下:
39 | ```
40 | factorial(0) // 0的阶乘为1
41 | factorial(1) // 该调用依赖factorial(0)
42 | factorial(2) // 该调用依赖factorial(1)
43 | factorial(3) // 该掉用依赖factorial(2)
44 | ```
45 |
46 | 这样的话,如果传入的 n 值特别大,那么这个调用栈将会非常之大。
47 | 如何解决这个问题呢?使用尾递归。
48 |
49 | ## 尾递归
50 |
51 | 尾递归是一种递归的写法,可以避免不断的将函数压栈导致堆栈溢出。
52 | 通过设置一个累加数,并且每一次都将当前的值累加上去,然后递归调用。
53 |
54 | ```
55 | function factorial(n, total = 1) {
56 | if (n === 0) return total
57 | return factorial(n-1, n * total)
58 | }
59 | ```
60 |
--------------------------------------------------------------------------------
/01-Algorithm/01-Recursion/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-8-14
5 | **/
6 |
7 | // 递归
8 |
9 | function factorail(n) {
10 | if (n === 0) return 1
11 | return n * factorial(n - 1)
12 | }
13 |
--------------------------------------------------------------------------------
/01-Algorithm/02-Factorial/factorial.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018/8/28
5 | **/
6 |
7 | // while
8 | // 阶乘
9 |
10 | function factorialWhile(num) {
11 | let result = 1
12 | while (num) {
13 | result *= num
14 | num--
15 | }
16 | return result
17 | }
18 | console.log('factorialWhile(3): ', factorialWhile(3))
19 |
20 |
21 | // for
22 | function factorialFor(num) {
23 | let result = 1
24 | for (let i = 2; i <= num; i++) {
25 | result *= i;
26 | }
27 | return result
28 | }
29 | console.log('factorial(4): ', factorialFor(4))
30 |
31 |
32 | // recursion 递归
33 | function factorialRec(num) {
34 | if (num <= 0) {
35 | return 1
36 | } else {
37 | return num * arguments.callee(num - 1)
38 | }
39 | }
40 | console.log('factorialRec(5): ', factorialRec(5))
41 |
42 |
43 | // tail recursion 尾递归
44 | function factorialTail(num) {
45 | let total = 1
46 | if (num === 0) return total
47 | return (num * factorialTail(num - 1))
48 | }
49 | console.log('factorialTail(6): ', factorialTail(6))
50 |
51 | // in "use strict"
52 | // recursion 严格模式下的递归
53 | var factorialStrict = (function fun(num) {
54 | if (num <= 1) return 1
55 | return num * fun(num - 1)
56 | })
57 | console.log('Recursion in use strict:', factorialStrict(5))
58 |
--------------------------------------------------------------------------------
/01-Algorithm/03-Palindrome/01-Palindrome.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-8-29
5 | **/
6 |
7 | // Palindrome 回文:字符串中顺读和倒读都一样的词语
8 | // 增加一个处理,忽略标点符号、大小写和空格,顺读和倒读一样。
9 |
10 | function palindrome(str) {
11 | let regex = /[\W_]/g;
12 | str = str.replace(regex, '')
13 | let str1 = str.toLowerCase()
14 | let str2 = str.split('').reverse().join('').toLowerCase()
15 | return str1 === str2
16 | }
--------------------------------------------------------------------------------
/01-Algorithm/04-SubString/SubString.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-9-9
5 | **/
6 |
7 | // JS截取字符串可使用 substring()或者slice()
8 |
9 | // substring: substring(start,end)表示从start到end之间的字符串,包括start位置的字符但是不包括end位置的字符。
10 |
11 | // substr: substr(start,length)表示从start位置开始,截取length长度的字符串。
12 |
13 | // split: split('')使用一个指定的分隔符把一个字符串分割存储到数组
14 |
15 | // join: join('')使用一个指定的分隔符把一个字符串合并为同一个字符串
16 |
17 | // slice: slice(start,end)返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。
--------------------------------------------------------------------------------
/01-Algorithm/05-Sort/README.md:
--------------------------------------------------------------------------------
1 | # 排序
2 |
3 | ## 目录
4 |
5 | - [冒泡排序](#冒泡排序)
6 |
7 | - [快速排序](#快速排序)
8 |
9 | - [插入排序](#插入排序)
10 |
11 | - [选择排序](#选择排序)
12 |
13 | - [希尔排序](#希尔排序)
14 |
15 | - [归并排序](#归并排序)
16 |
17 | ## 冒泡排序
18 |
19 | 时间复杂度:O(n^2)
20 | 空间复杂度:O(1)
21 |
22 | 思路:
23 |
24 | 1. 比较相邻元素。如果前一个比后一个大,就交换他们。
25 | 2. 对每一对相邻元素遍历比对大小,做完之后,最后的元素会是最大的数,把它抛出。
26 | 3. 对除去最后元素的数组(因为最后是最大)重复以上步骤。
27 | 4. 比对的数组越来越小,直到没有数字比对。
28 |
29 | ```js
30 | function bubbleSort(arr) {
31 | if (arr.length <= 1) {
32 | return arr
33 | }
34 | for (let i = 0; i < arr.length; i++) {
35 | for (let j = 0; j < arr.length - 1 - i; j++) {
36 | if (arr[j] > arr[j + 1]) {
37 | let temp = arr[j + 1]
38 | arr[j + 1] = arr[j]
39 | arr[j] = temp
40 | }
41 | }
42 | }
43 | return arr
44 | }
45 | ```
46 |
47 | ### 改进
48 |
49 | 1. 记录最后一次交换的位置 pos。因为最后一次交换位置之后项,都已经排好序了,所以下次遍历的时候
50 | 遍历到这个位置 pos 就好了,不需要遍历整个数组。
51 |
52 | ```js
53 | function bubbleSort(arr) {
54 | let i = arr.length - 1
55 | while (i > 0) {
56 | let pos = 0
57 | for (let j = 0; j < i; j++) {
58 | if (arr[j] > arr[j + 1]) {
59 | pos = j
60 | // 交换
61 | let temp = arr[j]
62 | arr[j] = arr[j + 1]
63 | arr[j + 1] = temp
64 | }
65 | }
66 | i = pos
67 | }
68 | }
69 | ```
70 |
71 | 2. 冒泡排序每一次排序可以得到最大值,或者最小值。可以考虑利用每次排序使用正向和反向冒泡,这样一次可以得出最大值和最小值;排序次数可以少几乎一半。
72 |
73 | ```js
74 | function bubbleSort(arr) {
75 | let min = 0
76 | let max = arr.length - 1
77 | let temp, j
78 | while (min < max) {
79 | for (j = min; j < max; j++) {
80 | if (arr[j] > arr[j + 1]) {
81 | // 前比后大,交换
82 | }
83 | }
84 | max-- // 找出来最大值, 最大值位置往前移
85 | for (j = max; j > min; j--) {
86 | if (arr[j] < arr[j - 1]) {
87 | // 前比后小,交换
88 | }
89 | }
90 | min++
91 | }
92 | return arr
93 | }
94 | ```
95 |
96 | ## 快速排序
97 |
98 | 时间复杂度:O(n\*log n)
99 | 空间复杂度:O(log n)
100 |
101 | 思路:
102 |
103 | 1. 先从数列中取出一个数作为“基准”;
104 | 2. 将比这个“基准”小的数全放到“基准”的左边,大于或等于的数全放到“基准”的右边。
105 | 3. 再对左右区间重复第二步,直到各区间只有一个数。
106 |
107 | ```js
108 | const quickSort = function (arr) {
109 | if (arr.length <= 1) {
110 | return arr
111 | }
112 | // 基准位置(理论上可以选取任意位置)
113 | let midIndex = Math.floor(arr.length / 2)
114 | let pivot = arr.splice(midIndex, 1)[0]
115 | let left = []
116 | let right = []
117 | for (let i = 0; i < arr.length; i++) {
118 | if (arr[i] < pivot) {
119 | left.push(arr[i])
120 | } else {
121 | right.push(arr[i])
122 | }
123 | }
124 | return quickSort(left).concat([pivot], quickSort(right))
125 | }
126 | ```
127 |
128 | ## 插入排序
129 |
130 | 时间复杂度:O(n^2)
131 | 空间复杂度:O(1)
132 |
133 | 思路:
134 |
135 | 1. 假定第一个元素是排好序的;
136 | 2. 取下一个元素 i,取改元素的值为 key,i 左边的元素为 j = i - 1;
137 | 3. 当 i 左边的元素不是最后一个元素 (j>=0),并且 该元素的值大于 i 的值(`arr[j] > key`);说明该元素需要移到下一个位置( arr[j+1] = arr[j]);
138 | 4. 继续往左遍历( `j--` ),重复 `3` 步骤;
139 | 5. 将 key 值放到最后遍历到的位置(arr[j+1] = key)。
140 | 6. 重复 `2-5` 步骤,直到遍历完( `i++` )。
141 |
142 | ```js
143 | function insertSort(arr) {
144 | if (arr.length <= 1) {
145 | return arr
146 | }
147 |
148 | for (let i = 1; i < arr.length; i++) {
149 | let key = arr[i]
150 | let j = i - 1
151 | while (j >= 0 && arr[j] > key) {
152 | arr[j + 1] = arr[j]
153 | j--
154 | }
155 | arr[j + 1] = key
156 | }
157 |
158 | return arr
159 | }
160 | ```
161 |
162 | ## 选择排序
163 |
164 | 时间复杂度:O(n^2)
165 | 空间复杂度:O(1)
166 |
167 | 思路:
168 |
169 | 1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置;
170 | 2. 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾;
171 | 3. 重复第二步,直到所有元素均排序完毕。
172 |
173 | ```js
174 | function selection(arr) {
175 | if (arr.length <= 1) {
176 | return arr
177 | }
178 | let minIndex, temp
179 | for (let i = 0; i < arr.length - 1; i++) {
180 | minIndex = i
181 | // 再循环一遍,找最小的数
182 | for (let j = i + 1; j < arr.length; j++) {
183 | if (arr[j] < arr[minIndex]) {
184 | minIndex = j
185 | }
186 | }
187 | // 找到最小数后和 arr[i] 互换位置
188 | temp = arr[i]
189 | arr[i] = arr[minIndex]
190 | arr[minIndex] = temp
191 | }
192 | return arr
193 | }
194 | ```
195 |
196 | ## 希尔排序
197 |
198 | > shell sort
199 |
200 | 也称递减增量排序算法
201 |
202 | 时间复杂度:O(n log n)
203 | 空间复杂度:O(1)
204 |
205 | 思路:
206 |
207 | 1. 在希尔排序中最重要的是分组,我们先找到一个间隔,每隔一定的间隔将这些数字排位一组;
208 | 2. 对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。
209 |
210 | ```js
211 | function shellSort(arr) {
212 | if (arr.length <= 1) {
213 | return arr
214 | }
215 | // 算间隔
216 | let len = Math.floor(arr.length / 2)
217 | while (len > 0) {
218 | for (let i = len; i < arr.length; i++) {
219 | let temp = arr[i]
220 | for (let j = i - len; j >= 0 && temp < arr[j]; j = j - len) {
221 | arr[j + len] = arr[j]
222 | }
223 | arr[j + len] = temp
224 | }
225 | len = Math.floor(len / 2)
226 | }
227 | }
228 | ```
229 |
230 | ## 归并排序
231 |
232 | > merge sort
233 |
234 | 归并排序采用分治法(Divide and Conquer)。将已有序的子序列合并,得到完全游戏的序列。
235 |
236 | 将两个有序表合成有序表,成为 2-路归并。
237 |
238 | 思路:
239 |
240 | 1. 将长度 n 的序列分为两个长度为 n/2 的子序列;
241 | 2. 对两个子序列才有归并排序。
242 |
243 | ```js
244 | function mergeSort(arr) {
245 | if (arr.length <= 1) {
246 | return arr
247 | }
248 | let len = Math.floor(arr.length / 2)
249 | let left = arr.slice(0, len)
250 | let right = arr.slice(len, arr.length)
251 |
252 | return merge(mergeSort(left), mergeSort(right))
253 | }
254 |
255 | function merge(left, right) {
256 | let res = []
257 |
258 | while (left.length && right.length) {
259 | if (left[0] < right[0]) {
260 | res.push(left.shift())
261 | } else {
262 | res.push(right.shift())
263 | }
264 | }
265 |
266 | while (left.length) {
267 | res.push(left.shift())
268 | }
269 |
270 | while (right.length) {
271 | res.push(right.shift())
272 | }
273 |
274 | return res
275 | }
276 | ```
277 |
--------------------------------------------------------------------------------
/01-Algorithm/05-Sort/bubbleSort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-8-28
5 | **/
6 |
7 | // 冒泡排序
8 |
9 | function bubbleSort(arr) {
10 | if (arr.length <= 1) {
11 | return arr
12 | }
13 | for (let i = 0; i < arr.length; i++) {
14 | for (let j = 0; j < arr.length - 1 - i; j++) {
15 | if (arr[j] > arr[j + 1]) {
16 | let temp = arr[j + 1]
17 | arr[j + 1] = arr[j]
18 | arr[j] = temp
19 | }
20 | }
21 | }
22 | return arr
23 | }
24 |
--------------------------------------------------------------------------------
/01-Algorithm/05-Sort/quickSort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-8-14
5 | **/
6 |
7 |
8 | // 快速排序
9 |
10 | const quickSort = function (arr) {
11 | if (arr.length <= 1) {
12 | return arr
13 | }
14 | // 基准位置(理论上可以选取任意位置)
15 | let midIndex = Math.floor(arr.length / 2)
16 | let pivot = arr.splice(midIndex, 1)[0]
17 | let left = []
18 | let right = []
19 | for (let i = 0; i < arr.length; i++) {
20 | if (arr[i] < pivot) {
21 | left.push(arr[i])
22 | } else {
23 | right.push(arr[i])
24 | }
25 | }
26 | return quickSort((left).concat([pivot], quickSort(right)))
27 | }
28 |
--------------------------------------------------------------------------------
/01-Algorithm/05-Sort/selectionSort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-8-14
5 | **/
6 |
7 |
8 | // 选择排序
9 |
10 | function selection(arr) {
11 | if (arr.length <= 1) {
12 | return arr
13 | }
14 | let minIndex, temp
15 | for (let i = 0; i < arr.length - 1; i++) {
16 | minIndex = i;
17 | // 再循环一遍,找最小的数
18 | for (let j = i + 1; j < arr.length; j++) {
19 | if (arr[j] < arr[minIndex]) {
20 | minIndex = j
21 | }
22 | }
23 | // 找到最小数后和 arr[i] 互换位置
24 | temp = arr[i]
25 | arr[i] = arr[minIndex]
26 | arr[minIndex] = temp
27 | }
28 | return arr
29 | }
30 |
--------------------------------------------------------------------------------
/01-Algorithm/05-Sort/shellSort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-8-14
5 | **/
6 |
7 |
8 | // 希尔排序
9 |
10 | function shellSort(arr) {
11 | if (arr.length <= 1) {
12 | return arr
13 | }
14 | // 算间隔
15 | let len = Math.floor(arr.length / 2)
16 | while (len > 0) {
17 | for (let i = len; i < arr.length; i++) {
18 | let temp = arr[i];
19 | for (let j = i - len; j >= 0 && temp < arr[j]; j = j - len) {
20 | arr[j + len] = arr[j]
21 | }
22 | arr[j + len] = temp
23 | }
24 | len = Math.floor(len / 2)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/01-Algorithm/06-Cache Algorithm/FIFO.js:
--------------------------------------------------------------------------------
1 | // FIFO queue
2 |
3 | // FIFO is a cache algorithm。
4 | // when the cache is full
5 | // we remove the cached item that is at the front of the FIFO queue
6 | // since it was added first and add the new item at the tail
7 |
8 | class FIFOCacke {
9 | constructor(limit) {
10 | this.limit = limit || 10
11 | this.map = {}
12 | this.keys = []
13 | }
14 | set(key, value) {
15 | let map = this.map
16 | let keys = this.keys
17 | if (!Object.prototype.hasOwnProperty.call(map, key)) {
18 | if (keys.length === this.limit) {
19 | // remove item at the tail
20 | delete map[keys.shift()]
21 | }
22 | keys.push(key)
23 | }
24 | map[key] = value
25 | }
26 | get(key) {
27 | return this.map[key]
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/01-Algorithm/07-Search Algorithm/README.md:
--------------------------------------------------------------------------------
1 | # 查找算法
2 |
3 | ## 目录
4 |
5 | ## 顺序查找
6 |
7 | 简而言之,就是从第一个元素开始,遍历全部元素,也叫暴力查找。
8 |
9 | ```js
10 | function search(arr, target) {
11 | for (let i = 0; i arr[mid]) {
46 | low = mid + 1
47 | } else {
48 | return mid
49 | }
50 | }
51 | return -1
52 | }
53 | ```
54 |
--------------------------------------------------------------------------------
/01-Algorithm/Demo/01-Longest String.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-9-11
5 | **/
6 |
7 | // Find the longest word in a string
8 |
9 |
10 | // for loop
11 |
12 | function findLongestWord1(str) {
13 | let arr = str.split(" ")
14 | let long = 0
15 | for (let i = 0; i < arr.length; i++) {
16 | if (arr[i], length > long) {
17 | long = arr[i].length
18 | }
19 | }
20 | return long
21 | }
22 |
23 |
24 | // map() sort()
25 | // map 匹配数组中各个字符串的长度, 返回字符串长度
26 | // sort 由大到小排序
27 |
28 | function findLongestWord2(str) {
29 | let arr = str.split(" ")
30 | arr = arr.map(function (item) {
31 | return item.length
32 | }).sort(function (a, b) {
33 | return b - a
34 | })
35 | return arr[0]
36 | }
37 |
38 |
39 | // map() reduce()
40 |
41 | function findLongestWord3(str) {
42 | let arr = str.split(" ")
43 | return arr.map(function (val) {
44 | return val.length
45 | }).reduce(function (p, c) {
46 | return Math.max(p, c)
47 | })
48 | // ES6
49 | // return arr.map(val => val.length).reduce((a, b) => Math.max(a, b))
50 | }
51 |
52 | findLongestWord3('Find the longest word in a string')
53 |
54 |
55 | // sort()
56 |
57 | function findLongestWord4(str) {
58 | let arr = str.split(" ")
59 | let str1 = arr.sort((a, b) => b.length - a.length)
60 | let lenMax = str1[0].length
61 | console.log(lenMax)
62 | return lenMax
63 | }
64 |
65 | findLongestWord4("Find the longest word in a string")
66 |
67 | // sort() pop()
68 | function findLongestWord5(str) {
69 | let arr = str.split(" ")
70 | let str1 = arr.sort((a, b) => a.length - b.length)
71 | let long = str1.pop().length
72 | return long
73 | }
--------------------------------------------------------------------------------
/01-Algorithm/README.md:
--------------------------------------------------------------------------------
1 | # Algorithm 算法学习
2 |
3 | ## 目录
4 |
5 | - **[Recursion 递归](https://github.com/stephentian/daily-js/tree/master/01-Algorithm/01-Recursion)**
6 | - **[Factorial 阶乘](https://github.com/stephentian/daily-js/blob/master/01-Algorithm/02-Factorial/factorial.js)**
7 | - **[Palindrome 回文](https://github.com/stephentian/daily-js/blob/master/01-Algorithm/03-Palindrome/01-Palindrome.js)**
8 | - **[SubString 字符串切割](https://github.com/stephentian/daily-js/blob/master/01-Algorithm/04-SubString/SubString.js)**
9 | - **[Sort 排序](https://github.com/stephentian/daily-js/tree/master/01-Algorithm/05-Sort)**
10 | - **[Cache Algorithm 缓存算法](https://github.com/stephentian/daily-js/tree/master/01-Algorithm/06-Cache%20Algorithm)**
11 | - **[Search Algorithm 查找算法](./07-Search%20Algorithm)**
12 |
--------------------------------------------------------------------------------
/02-Higher-order Function/01-Introduction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-9-12
5 | **/
6 |
7 | // Higher-order Function
8 | // 高阶函数
9 | // JavaScript 的函数其实都指向某个变量。既然变量可以指向函数,函数的参数就能接收变量
10 | // 一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数
11 |
12 | function add(x, y, f) {
13 | return f(x) + f(y)
14 | }
15 |
16 | add(-5, 6, Math.abs)
17 |
--------------------------------------------------------------------------------
/02-Higher-order Function/02-Map.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: tianre96@gmail.com
4 | * day: 2018-9-12
5 | **/
6 |
7 | // the function will runs on each element of the array
8 |
9 | var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
10 |
11 | function pow(x) {
12 | return x * x
13 | }
14 |
15 | var results = arr.map(pow)
16 | console.log(results)
17 |
--------------------------------------------------------------------------------
/02-Higher-order Function/03-Reduce.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-9-12
5 | **/
6 |
7 | // The reduce() method executes a reducer function (that you provide) on each member of the array resulting in a single output value
8 | // reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值
9 |
10 | var arr = [1, 2, 3, 4, 5]
11 | var results = arr.reduce((x, y) => x + y)
12 | console.log(results)
13 |
14 | // Transform [1, 2, 3, 4, 5] into 12345
15 | var results1 = arr.reduce((x, y) => x * 10 + y)
16 | console.log(results1)
17 |
--------------------------------------------------------------------------------
/02-Higher-order Function/04-Filter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-9-12
5 | **/
6 |
7 | // The filter() method creates a new array with all elements that pass the test implemented by the provided function
8 | // filtering out all value
9 | // filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素
10 | // filter() 在于实现一个“筛选”函数
11 |
12 | var arr = [1, 2, 3, 4, 5, 6]
13 | var result = arr.filter(v => v > 3)
14 | //console.log(result)
15 |
16 | var result1 = arr.filter((ele, index, self) => {
17 | // element value
18 | console.log(ele)
19 | // element index
20 | console.log(index)
21 | // arr
22 | //console.log(self)
23 | return true
24 | })
25 |
26 | // Array remove duplicated
27 | // 数组去重
28 |
29 | var r,
30 | arr1 = [1, 2, 2, 3, 4, 4, 5, 1]
31 | r = arr1.filter((ele, index, self) => self.indexOf(ele) === index)
32 | console.log(r)
--------------------------------------------------------------------------------
/02-Higher-order Function/05-Sort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-9-13
5 | **/
6 |
7 | // The sort() method sorts the elements in Place of an array and returns the array.
8 | // The default sort order is according to string Unicode code points. !important
9 | // sort() 方法用原地算法对数组的元素进行排序
10 | // 默认排序顺序是根据字符串Unicode码点
11 |
12 | // 直接在原数组上改变
13 |
14 | var arr = [10, 20, 1, 2]
15 | arr.sort((x, y) => x - y)
16 | console.log(arr)
17 |
18 | var items = [{
19 | name: 'Edward',
20 | value: 21
21 | },
22 | {
23 | name: 'Sharpe',
24 | value: 37
25 | },
26 | {
27 | name: 'And',
28 | value: 45
29 | },
30 | {
31 | name: 'the',
32 | value: -12
33 | },
34 | {
35 | name: 'Magnetic',
36 | value: 100
37 | },
38 | {
39 | name: 'Zeros',
40 | value: 37
41 | }
42 | ];
43 |
44 | // sort by value
45 | var arr1 = items.sort(function (a, b) {
46 | return (a.value - b.value)
47 | });
48 | console.log('arr1: ', arr1)
49 |
50 | // sort by name
51 | var arr2 = items.sort(function (a, b) {
52 | var nameA = a.name.toUpperCase(); // ignore upper and lowercase
53 | var nameB = b.name.toUpperCase(); // ignore upper and lowercase
54 | if (nameA < nameB) {
55 | return -1;
56 | }
57 | if (nameA > nameB) {
58 | return 1;
59 | }
60 | // names must be equal
61 | return 0;
62 | });
63 | console.log('arr2: ', arr2)
--------------------------------------------------------------------------------
/02-Higher-order Function/06-Some.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2020-04-28
5 | **/
6 |
7 | // MDN:
8 | // The some() method tests whether at least one element in the array passes the test implemented by the provided function.
9 | // It returns a Boolean value
10 |
11 | const array = [1, 2, 3, 4]
12 |
13 | const even = e => e % 2 === 0
14 |
15 | console.log(array.some(even))
16 | // true
17 |
--------------------------------------------------------------------------------
/02-Higher-order Function/07-Every.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: tianre96@gmail.com
4 | * day: 2020-04-28
5 | **/
6 |
7 | // The every() method tests whether all elements in the array pass the test impletmented by the provided function.
8 | // return a Boolean value.
9 |
10 | const isBelowFourty = v => v < 40
11 | const isBelowSixty = v => v < 60
12 |
13 | const array = [1, 10, 20, 30, 40, 50]
14 |
15 | console.log(array.every(isBelowFourty))
16 | // false
17 | console.log(array.every(isBelowSixty))
18 | // true
19 |
--------------------------------------------------------------------------------
/02-Higher-order Function/README.md:
--------------------------------------------------------------------------------
1 | # 高阶函数
2 | > 可以接收函数作为参数的函数
3 |
4 | 函数是 js 的一等公民, 可以作为参数和返回值存在
5 |
--------------------------------------------------------------------------------
/03-General Knowledge/01-Falsy.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Document
14 |
15 |
16 |
17 |
18 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/03-General Knowledge/02-Truthy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-9-17
5 | **/
6 |
7 | // A truthy value is a value that is considered 'true' when evaluated in a Boolean context
8 | // Truthy(真值)指的是在Boolean上下文中转换后的值为真的值
9 |
10 | // true
11 | // {}
12 | // []
13 | // numbers !== 0 (e.g. 1, 2, ...)
14 | // String (e.g. 'string')
15 | // new Date()
16 | // -42
17 | // 3.14
18 | // -3.14
19 | // Infinity
20 | // -Infinity
21 |
22 | console.log('true: ', Boolean(true))
23 | console.log('{}: ', Boolean({}))
24 | console.log('[]: ', Boolean([]))
25 | console.log('1: ', Boolean(1))
26 | console.log('0: ', Boolean(0))
27 | console.log('-1: ', Boolean(-1))
28 | console.log('string: ', Boolean('string'))
29 | console.log('new Date(): ', Boolean(new Date()))
30 | console.log('-42: ', Boolean(-42))
31 | console.log('-41: ', Boolean(-41))
32 | console.log('3.14: ', Boolean(3.14))
33 | console.log('-3.14: ', Boolean(-3.14))
34 | console.log('-3.1: ', Boolean(-3.1))
35 | console.log('Infinity: ', Boolean(Infinity))
36 | console.log('-Infinity: ', Boolean(-Infinity))
37 | console.log('PI: ', Boolean(Math.PI))
38 | // console.log(Boolean())
--------------------------------------------------------------------------------
/03-General Knowledge/03-~&~~/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-5-21
5 | **/
6 |
7 |
8 | // ~ 和 ~~
9 |
10 | // 1. 介绍
11 |
12 | // ~ 通俗的讲, 就是 x 进行按位非操作, 结果为 -(x + 1)
13 | // 对于浮点数的结果, 负的向上取整, 正的向下取整
14 |
15 | // console.log(~2.5)
16 | // -3
17 |
18 | // console.log(~3.6)
19 | // -4
20 |
21 |
22 |
23 | // 同理, ~~ 就是对 x 取 -(-(x+1) + 1)
24 |
25 | // console.log(~~2.5)
26 | // 2
27 |
28 |
29 | // 2. 延伸
30 |
31 | // 利用两个按位取反的符号,进行类型的转换,转换成数字符号
32 |
33 | // true
34 | console.log(~~false === 0)
35 | console.log(~~true === 1)
36 | console.log(~~undefined === 0)
37 | console.log(~~!undefined === 1)
38 | console.log(~~null === 0)
39 | console.log(~~!null === 1)
40 | console.log(~~'' === 0)
41 | // console.log(~~false === 0)
42 |
43 |
44 |
45 | // ~ 用法
46 |
47 | // 判断数值中是否有某元素时
48 |
49 | // 原来: if(arr.indexOf(ele) > -1) { ... }
50 | // 现在: if(~arr.indexof(ele)) { ... }
51 |
52 |
53 | // ~~ 用法
54 |
55 | // ~~value 可以代替 parseInt
56 | // 原来: parseInt(-2.99) //-2
57 | // 现在: ~~(2.99) // -2
58 |
--------------------------------------------------------------------------------
/03-General Knowledge/04-Shift Operators/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-5-24
5 | **/
6 |
7 | // 移位操作符
8 |
9 |
10 |
11 | // 1
12 | // << 有符号左移
13 |
14 | // 用法:
15 |
16 | // a << b
17 | // 将 a 的二进制形式向左移 b (< 32) 比特位,右边用 0 填充
18 | // 也可以认为 a*(2^b)
19 | // 10 << 1
20 | // 20
21 | console.log(10 << 2) // 40
22 |
23 |
24 |
25 | // 2
26 | // >> 有符号右移
27 |
28 | // 用法:
29 |
30 | // a >> b
31 | // 将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位、
32 | // 也可以认为 a/(2^b)
33 | // 10 >> 1
34 | // 5
35 | // 20 >> 2
36 | // 5
37 | // -4 >> 1
38 | // -2
39 |
40 | // 小数的往小的计
41 | // 9 >> 1
42 | // 4
43 |
44 |
45 |
46 | // 3
47 | // >>> 无符号右移
48 |
49 | // 用法:
50 |
51 | // 对于非负数,有符号右移和无符号右移总是返回相同的结果
52 | // 9 >>> 2
53 | // 2
54 | // 对于负数却不尽相同
55 | // -4 >>> 1
56 | // 2147483646
57 |
--------------------------------------------------------------------------------
/03-General Knowledge/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript General Knowledge
2 |
3 | > JS 常识
4 |
5 | - [JavaScript General Knowledge](#javascript-general-knowledge)
6 | - [1. 真值与虚值](#1-真值与虚值)
7 | - [真值](#真值)
8 | - [虚值](#虚值)
9 | - [2. Chrome 上返回的 undefined](#2-chrome-上返回的-undefined)
10 | - [3. ~ 和 ~~](#3--和-)
11 | - [this 和 var 定义的变量](#this-和-var-定义的变量)
12 | - [在函数中](#在函数中)
13 | - [在{}对象中](#在对象中)
14 |
15 | ## 1. 真值与虚值
16 |
17 | ### 真值
18 |
19 | ### 虚值
20 |
21 | ## 2. Chrome 上返回的 undefined
22 |
23 | 函数没有返回值的情况下默认返回 undefined
24 |
25 | ## 3. ~ 和 ~~
26 |
27 | ~ 为 按位取反
28 |
29 | ## this 和 var 定义的变量
30 |
31 | ### 在函数中
32 |
33 | ```js
34 | function Fool() {
35 | let name = "Jack"
36 | this.name = "Mick"
37 | funtion fn() {
38 | console.log("name:", name)
39 | console.log("this.name:", this.name)
40 | }
41 | fn()
42 | }
43 |
44 | Fool()
45 | // Jack
46 | // Mick
47 | ```
48 |
49 | - 在函数中,var定义的是局部变量,而this定义的是属性。
50 | - 这两者没有覆盖。
51 | - 新创建的对象是无法获取到局部变量的。
52 |
53 | ### 在{}对象中
54 |
55 | 和 window 对象一样,{} 创建的对象不能用 new ,因为没有构造函数
56 |
--------------------------------------------------------------------------------
/04-Async/01-Highorder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019/2/26
5 | **/
6 |
7 | // 高阶函数
8 |
9 | // 一、
10 | // 写一些判断参数类型的函数
11 | // 1. 判断一个参数是否是字符串
12 | function isString(param) {
13 | return Object.prototype.toString.call(param) === '[object String]'
14 | }
15 | // 2. 判断一个参数是否为数组
16 | function isArray(param) {
17 | return Object.prototype.toString.call(param) === '[object Array]'
18 | }
19 | // 3. isNumber ...
20 | console.log(isString({}))
21 | console.log(isArray([]))
22 | // 如果要判断其他类型的话,js 中类型很多,就要写很多函数
23 |
24 | // 思考:能不能写一个方法直接批量返回这些函数呢?
25 |
26 |
27 | // 二、
28 | // 写一个函数判断类型, 通过它批量生成函数
29 | function isType(type) {
30 | return function (param) {
31 | return Object.prototype.toString.call(param) === `[object ${type}]`
32 | }
33 | }
34 |
35 | let isString1 = isType('String')
36 | let isArray1 = isType('Array')
37 | console.log(isString1('11'))
38 | console.log(isArray1([1]))
39 |
40 | // 类似函数工厂, 生产函数
41 | // 这其实也是闭包的应用, 当函数执行时, 会产生私有作用域, 上下文执行环境, 可以一直引用里面的 type
42 |
43 |
44 | // 三、
45 | // lodash 中有个 after 方法
46 | // 作用: 指定一个函数被调用多少次才会执行
47 | function sleep() {
48 | console.log('睡着了')
49 | }
50 |
51 | // 函数 after
52 | // 接受参数 times 和 fn(函数)
53 | // 返回一个新的函数, 当新的函数执行 times 次后, 执行 fn
54 | // function after(times, fn) {
55 | // ...
56 | // }
57 | // let newSleep = after(3, sleep)
58 | // newSleep()
59 | // newSleep()
60 | // newSleep() // 执行 sleep
61 |
62 | // 思考: after 里的内容
63 |
64 | function after(times, fn) {
65 | let count = 1
66 | return function () {
67 | if (count++ == times) {
68 | fn()
69 | }
70 | }
71 | }
72 | let newSleep = after(3, sleep)
73 | newSleep()
74 | newSleep()
75 | newSleep()
76 |
--------------------------------------------------------------------------------
/04-Async/02-Callback.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019/2/28
5 | **/
6 |
7 | // 回调函数
8 |
9 | // 写一个异步读取 txt 文件的函数, 使用回调函数接收信息
10 | // error first, 回调函数的第一个参数为错误对象
11 |
12 | let fs = require('fs')
13 |
14 | // fs.readFile('./a.txt', 'utf8', function (err, data) {
15 | // if (err) {
16 | // console.log(err)
17 | // } else {
18 | // console.log(data)
19 | // }
20 | // })
21 | // 打印出 aaa
22 |
23 | // 用一个函数把上面的封装起来
24 | // function read(path) {
25 | // fs.readFile(path, 'utf8', function (err, data) {
26 | // // throw Error('报错了!')
27 | // if (err) {
28 | // console.log(err)
29 | // } else {
30 | // console.log(data)
31 | // }
32 | // })
33 | // }
34 | // read('./a.txt')
35 |
36 |
37 |
38 | // 回调函数有以下问题
39 | // 1. 不能 return, 无法正确获取返回值
40 | // let returnData = read('./a.txt')
41 | // console.log('returnData: ', returnData) // undefined
42 |
43 | // 2. 不能使用 try catch 捕获错误
44 | // try {
45 | // let returnData = read('./a.txt')
46 | // } catch (e) {
47 | // console.log('err:', e) // 当 read 函数 throw Error 时, catch 没捕获到错误
48 | // }
49 |
50 | // 3. 回调地狱
51 | // 比如你要读取多个文件,并且要把文件内容拼接起来
52 | // 假设 3 个文件
53 |
54 | // fs.readFile('./a.txt', 'utf8', function (err, dataA) {
55 | // if (err) {
56 | // console.log(err)
57 | // } else {
58 | // fs.readFile('./b.txt', 'utf8', function (err, dataB) {
59 | // if (err) {
60 | // console.log(err)
61 | // } else {
62 | // fs.readFile('./c.txt', 'utf8', function (err, dataC) {
63 | // if (err) {
64 | // console.log(err)
65 | // } else {
66 | // console.log(dataA + dataB + dataC)
67 | // }
68 | // })
69 | // }
70 | // })
71 | // }
72 | // })
73 | /**
74 | * 这种回调金字塔有以下问题
75 | * 1. 非常难看
76 | * 2. 难以维护
77 | * 3. 效率低, 需要一步一步等待结果
78 | **/
79 |
80 |
81 |
82 | // 如何解决回调函数嵌套问题?
83 |
84 | // 1. 事件发布/订阅模型
85 | // EventEmitter 是 node.js 核心模块 event 的一个类
86 | // 通过它可以创建事件发射器的实例, 实例有两个主要方法: 1. on(注册监听), 2. emit(发射事件)
87 | // let EventEmitter = require('events')
88 | // let task = new EventEmitter()
89 | // let html = {}
90 | // html 来存放最终拼接数据
91 |
92 | // task.on('ready', function (key, value) {
93 | // html[key] = value;
94 | // if (Object.keys(html).length == 2) {
95 | // console.log('html:', html)
96 | // }
97 | // })
98 |
99 | // 将读取文件函数分开写, 无需等待读取完第一个文件再读取第二个, 两个读取文件函数同步执行
100 | // fs.readFile('./a.txt', 'utf8', function (err, dataA) {
101 | // task.emit('ready', 'dataA', dataA)
102 | // })
103 | // fs.readFile('./b.txt', 'utf8', function (err, dataB) {
104 | // task.emit('ready', 'dataB', dataB)
105 | // })
106 |
107 | // 2. 通过哨兵函数来处理
108 | // let html1 = {}
109 | // function done(key, value) {
110 | // html1[key] = value;
111 | // if (Object.keys(html1).length == 2) {
112 | // console.log(html1)
113 | // }
114 | // }
115 | // fs.readFile('./d.txt', 'utf8', function (err, dataC) {
116 | // done('dataC', dataC)
117 | // })
118 | // fs.readFile('./b.txt', 'utf8', function (err, dataB) {
119 | // done('dataB', dataB)
120 | // })
121 |
122 | // 但是上面的写法不通用
123 |
124 | // 我们可以将函数内的数据抽象化,
125 | // 比如 done 里面的 2, 可能我们要读取 3 个文件, ... n 个文件, 故不能写死
126 | // 可以写个工厂函数
127 |
128 | function render(length, cb) {
129 | let html2 = {};
130 | return function (key, value) {
131 | html2[key] = value;
132 | if (Object.keys(html2).length == length) {
133 | cb(html2)
134 | }
135 | }
136 | }
137 | let done = render(2, function (html) {
138 | console.log(html)
139 | })
140 | fs.readFile('./c.txt', 'utf8', function (err, dataC) {
141 | done('dataC', dataC)
142 | })
143 | fs.readFile('./b.txt', 'utf8', function (err, dataB) {
144 | done('dataB', dataB)
145 | })
146 |
--------------------------------------------------------------------------------
/04-Async/03-Generator.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019/3/1
5 | **/
6 |
7 | // 生成器
8 | // 生成器是一个函数, 可以用来生成迭代器
9 | // 生成器和普通函数不同,
10 | // 普通函数一旦调用必须执行完,
11 | // 生成器函数中间可以暂停, 执行一会儿停一下再执行
12 |
13 | // 生成器特点: 函数名前加 *
14 |
15 | function* go(a) {
16 | console.log(1)
17 | let b = yield a
18 | console.log(2)
19 | let c = yield b
20 | console.log(3)
21 | return c
22 | }
23 |
24 | let it = go('a')
25 | console.log(it)
26 |
27 | // 生成器函数不会立即执行, 而是返回一个迭代器
28 | // 迭代器是一个对象, 每调用一次 next 就会返回一个对象
29 | // next 第一次被调用不需要参数, 传参数也没意义
30 | // 对象有两个属性, 一个是 value(yield后面的值), 一个是 done(表示是否迭代完成)
31 |
32 | let r1 = it.next()
33 | console.log(r1)
34 | //{ value: 'a', done: false }
35 |
36 | let r2 = it.next('B')
37 | console.log(r2)
38 | //{ value: 'B', done: false }
39 |
40 | let r3 = it.next('C')
41 | console.log(r3)
42 | //{ value: 'C', done: true }
43 |
--------------------------------------------------------------------------------
/04-Async/04-Promise.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-10-25
5 | **/
6 |
7 | // 1. 链式调用
8 | // const promise = doSomething()
9 | // const promise2 = promise.then(successCallback, failureCallback)
10 | // 或者
11 | // const promise2 = doSomething().then(successCallback, failreCallback)
12 |
13 | // 在过去
14 | // doSomething(function(result) {
15 | // doSomethingElse(result, function(newResult) {
16 | // doThirdThing(newResult, function(finalResult) {
17 | // console.log('Got the final result: ' + finalResult);
18 | // }, failureCallback);
19 | // }, failureCallback);
20 | // }, failureCallback);
21 |
22 | // 通过新式函数, then里的参数是可选的
23 | // doSomething().then(function(result) {
24 | // return doSomethingElse(result);
25 | // })
26 | // .then(function(newResult) {
27 | // return doThirdThing(newResult);
28 | // })
29 | // .then(function(finalResult) {
30 | // console.log('Got the final result: ' + finalResult);
31 | // })
32 | // .catch(failureCallback);
33 |
34 |
35 | // 2. Catch 的后续操作
36 | // new Promise((resolve, reject) => {
37 | // console.log('Initial')
38 | // resolve()
39 | // })
40 | // .then(() => {
41 | // throw new Error('Something failed')
42 |
43 | // console.log('Do this')
44 | // })
45 | // .catch(() => {
46 | // console.log('Do that')
47 | // })
48 | // .then(() => {
49 | // console.log('Do this whatever happened before')
50 | // })
51 |
52 |
53 | // 3. 创建一个 Promise
54 | // const myFirstPromise = new Promise((resolve, reject) => {
55 | // 做一些异步操作,最终会调用下面两者之一
56 | // resolve(someValue)
57 | // 或
58 | // reject("failure reason")
59 | // })
60 |
61 | // 让某个函数有 Promise 功能,只需返回一个 Promise 即可
62 | // 比如封装一个 get 请求
63 | // function myAsyncFunc(url) {
64 | // return new Promise((resolve, reject) => {
65 | // const xhr = new XMLHttpRequest()
66 | // xhr.open("GET", url)
67 | // xhr.onload = () => resolve(xhr.responseText)
68 | // xhr.onerror = () => reject(xhr.statusText)
69 | // xhr.send()
70 | // })
71 | // }
72 |
73 |
74 | // 4. 解决回调地狱
75 | // let fs = require('fs')
76 | // fs.readFile('./a.txt','utf8',function(err,data){
77 | // fs.readFile(data,'utf8',function(err,data){
78 | // fs.readFile(data,'utf8',function(err,data){
79 | // console.log(data)
80 | // })
81 | // })
82 | // })
83 |
84 | // 使用 Promise 后
85 | // let fs = require('fs')
86 | // function readFile(url) {
87 | // return new Promise((resolve, reject) => {
88 | // fs.readFile(url, 'utf-8', function(error, data) {
89 | // error && reject(error)
90 | // resolve(data)
91 | // })
92 | // })
93 | // }
94 |
95 | // readFile('./a.txt').then(data => {
96 | // return readFile(data)
97 | // }).then(data => {
98 | // return readFile(data)
99 | // }).then(data => {
100 | // console.log(data)
101 | // })
--------------------------------------------------------------------------------
/04-Async/05-Async+Await.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-10-26
5 | **/
6 |
7 | // 1. 与其他异步操作的对比
8 |
9 | // 先定义一个 Fetch 方法
10 | // var fetch = require('node-fetch')
11 |
12 | // function fetchUser() {
13 | // return new Promise((resolve, reject) => {
14 | // fetch('https://api.github.com/users/stephentian')
15 | // .then((res) => {
16 | // resolve(res)
17 | // }, (error) => {
18 | // reject(error)
19 | // })
20 | // })
21 | // }
22 |
23 | // Promise 方式
24 | // function getUserByPromise() {
25 | // fetchUser()
26 | // .then((data) => {
27 | // console.log(data)
28 | // }, (error) => {
29 | // console.log(error)
30 | // })
31 | // }
32 | // getUserByPromise()
33 |
34 | // Promise 的方式虽然解决了 callback hell
35 | // 但是这种方式充满了 Promise的 then() 方法,如果处理流程复杂的话,整段代码将充满 then
36 |
37 | // Generator 方式
38 | // function* fetchUserByGenerator() {
39 | // const user = yield fetchUser()
40 | // return user
41 | // }
42 | // const g = fetchUserByGenerator()
43 | // const result = g.next().value
44 | // result.then((v) => {
45 | // console.log(v)
46 | // }, (error) => {
47 | // console.log(error)
48 | // })
49 |
50 | // Async 方式
51 | // async function getUserBuAsync() {
52 | // let user = await fetchUser()
53 | // return user
54 | // }
55 | // getUserBuAsync()
56 | // .then(v => console.log(v))
57 |
58 | // 2. async 返回一个 Promise 对象
59 | async function f() {
60 | return "Hello World"
61 | }
62 | f().then(v => console.log(v))
63 | // 如果 async 函数内部抛出异常,则会导致返回的 Promise 对象状态变为 reject 状态
64 | // 抛出的错误而会被 catch 方法回调函数接收到
65 | async function e() {
66 | throw new Error('error!')
67 | }
68 | e().then(v => console.log(v))
69 | .catch(err => console.log(err))
--------------------------------------------------------------------------------
/04-Async/06-CreatePromise.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019/3/1
5 | **/
6 |
7 | // 手写一个 Promise
8 | // 首先分析 Promise 内部结构
9 |
10 | // 1. 标准 Promise
11 |
12 | // 1.1 promise 主体函数
13 |
14 | // 1.1.1 定义状态
15 | // 等待
16 | // 成功
17 | // 失败
18 |
19 | // const PENDING = 'pending'
20 | // const FULFILLED = 'fulfilled'
21 | // const REJECTED = 'rejected'
22 |
23 | // function MyPromise(executor) {
24 | // let self = this // 先缓存当前 promise 实例, 避免异步执行时, this 获取不对
25 | // self.value = null // promise 的值
26 | // self.status = PENDING // 设置初始状态
27 | // self.onResolvedCallbacks = [] // 存放成功的回调函数数组
28 | // self.onRejectedCallbacks = [] // 存放失败的回调函数数组
29 |
30 | // // 1.1.2 resolve 方法
31 | // // 只有 promise 状态为 pending, 可以转成成功状态
32 | // // 并将成功的返回值赋值给 value
33 | // // 如果已经是成功状态, 则什么都不做
34 | // function resolve(value) {
35 | // if (value != null && value.then && typeof value.then == 'function') {
36 | // return value.then(resolve, reject)
37 | // }
38 |
39 | // // 思考: 为什么要用 setTimeout 包起来?
40 | // setTimeout(function () {
41 | // if (self.status == PENDING) {
42 | // self.status = FULFILLED
43 | // self.value = value
44 | // // 调用所有成功回调函数
45 | // self.onResolvedCallbacks.forEach(cb => cb(self.value))
46 | // }
47 | // })
48 | // }
49 |
50 | // // 1.1.3 reject 方法
51 | // // 如果是 pending, 转成失败状态
52 | // // 并将失败原因赋值给 value
53 | // function reject(reason) {
54 | // setTimeout(function () {
55 | // if (self.status == PENDING) {
56 | // self.status = REJECTED
57 | // self.value = reason
58 | // self.onRejectedCallbacks.forEach(cb => cb(self.value))
59 | // }
60 | // })
61 | // }
62 |
63 | // // 1.1.4 函数可能出现异常
64 | // // 需要捕获, 如果出错了需要使用 reject
65 | // try {
66 | // executor(resolve, reject)
67 | // } catch (e) {
68 | // reject(e)
69 | // }
70 | // }
71 |
72 | // 1.2 resolvePromise 函数
73 |
74 | // resolvePromise 针对 resolve 不同值的情况进行处理
75 |
76 | // 1.2.1 函数包含参数
77 | // promise2: promise1.then 返回的新的 promise 对象
78 | // x: promise1 中 onFulfilled 的返回值
79 | // resolve: promise2 的 resolve 方法
80 | // reject: promise2 的 reject 方法
81 |
82 | function resolvePromise(promise2, x, resolve, reject) {
83 | // 如果 onFulfilled 返回的的 x 就是 promise2, 就导致循环引用出错
84 | if (promise2 === x) {
85 | return reject(new TypeError("循环使用"))
86 | }
87 |
88 | // 避免多次调用
89 | let called = false
90 | // 如果 x 是一个 promise 对象
91 | if (x instanceof MyPromise) {
92 | // 如果为等待状态需等待直至 x 被执行或拒绝, 并解析 y 值
93 | if (x.status == PENDING) {
94 | x.then(
95 | function (y) {
96 | resolvePromise(promise2, y, resolve, reject)
97 | },
98 | reason => {
99 | reject(reason)
100 | }
101 | )
102 | } else {
103 | // 如果 x 处于 执行状态/拒绝状态(值已经被解析为普通值), 用相同的值执行传递下去
104 | x.then(resolve, reject)
105 | }
106 | // 如果 x 为对象 或者 函数
107 | } else if (x != null && (typeof x == "objec" || typeof x == "function")) {
108 | // 是否为 theneable 对象(具有 then 方法的对象/函数)
109 | try {
110 | let then = x.then
111 | if (typeof then === "function") {
112 | then.call(
113 | (x, y) => {
114 | if (called) return
115 | called = true
116 | resolvePromise(newPromise, y, resolve, reject)
117 | },
118 | reason => {
119 | if (called) return
120 | called = true
121 | reject(reason)
122 | }
123 | )
124 | } else {
125 | // 否说明为普通对象/函数, 直接 resolve
126 | resolve(x)
127 | }
128 | } catch (e) {
129 | if (called) return
130 | called = true
131 | reject(e)
132 | }
133 | } else {
134 | // 如果 x 是一个普通的值, 则用 x 的值去 resolve promise2
135 | resolve(x)
136 | }
137 | }
138 |
139 | // 1.3 then 方法
140 |
141 | // onFulfilled 用来接收 promise 成功或者失败的返回值
142 | // 如果成功和失败的回调没有传, 则表示 then 没有逻辑, 会把值往后抛
143 |
144 | MyPromise.prototype.then = function (onFulfilled, onRejected) {
145 | // 判断 then 接收的参数是否为 function, 是则忽略
146 | onFulfilled =
147 | typeof onFulfilled == "function"
148 | ? onFulfilled
149 | : function (value) {
150 | return value
151 | }
152 | onRejected =
153 | typeof onRejected == "function"
154 | ? onRejected
155 | : function (reason) {
156 | throw reason
157 | }
158 |
159 | let self = this
160 | let promise2
161 |
162 | // 根据三种情况处理
163 | // if (self.status == PENDING) {
164 | // self.onResolvedCallbacks.push(onFulfilled)
165 | // }
166 | // if (self.status === FULFILLED) {
167 | // onFulfilled(self.value)
168 | // }
169 | // if (self.status === REJECTED) {
170 | // onRejected(self.value)
171 | // }
172 |
173 | // 如果当前 promise 已经是成功状态, 直接将值 传递给 onFulfilled
174 | // 因为 then 有链式调用, 所以返回一个新的 promise 对象
175 | // 状态为 FULFILLED, 即为成功状态时, 直接调用 onFulfilled
176 | // 考虑到有可能 throw, 将代码包在 try catch 里
177 | if (self.status == FULFILLED) {
178 | return (promise2 = new MyPromise(function (resolve, reject) {
179 | setTimeout(function () {
180 | try {
181 | let x = onFulfilled(self.value)
182 | // 如果获取了返回值 x, 则走解析 promise 的过程
183 | resolvePromise(promise2, x, resolve, reject)
184 | } catch (e) {
185 | // 如果执行成功的回调过程中出错了, 用错误原因把 promise2 rejecte
186 | reject(e)
187 | }
188 | })
189 | }))
190 | }
191 |
192 | if (self.status == REJECTED) {
193 | return (promise2 = new MyPromise(function (resolve, reject) {
194 | setTimeout(function () {
195 | try {
196 | let x = onRejected(self.value)
197 | resolvePromise(promise2, x, resolve, reject)
198 | } catch (e) {
199 | reject(e)
200 | }
201 | })
202 | }))
203 | }
204 |
205 | if (self.status == PENDING) {
206 | return (promise2 = new MyPromise(function (resolve, reject) {
207 | self.onResolvedCallbacks.push(function () {
208 | try {
209 | let x = onFulfilled(self.value)
210 | resolvePromise(promise2, x, resolve, reject)
211 | } catch (e) {
212 | reject(e)
213 | }
214 | })
215 | self.onRejectedCallbacks.push(function () {
216 | try {
217 | let x = onRejected(self.value)
218 | resolvePromise(promise2, x, resolve, reject)
219 | } catch (e) {
220 | reject(e)
221 | }
222 | })
223 | }))
224 | }
225 | }
226 |
227 | // 1.4 catch
228 | // catch 的原理就是只传失败的回调
229 | MyPromise.prototype.catch = function (onRejected) {
230 | this.then(null, onRejected)
231 | }
232 |
233 | MyPromise.deferred = MyPromise.defer = function () {
234 | let defer = []
235 | defer.promise = new MyPromise(function (resolve, reject) {
236 | defer.resolve = resolve
237 | defer.reject = reject
238 | })
239 | return defer
240 | }
241 |
242 | // 2. 实现一个同步的 Promise
243 |
244 | const PENDING = "pending"
245 | const FULFILLED = "fulfilled"
246 | const REJECTED = "rejected"
247 | function SyncPromise(executor) {
248 | let self = this
249 | self.status = PENDING
250 | self.value = undefined
251 | self.reason = undefined
252 |
253 | function resolve(value) {
254 | if (self.status === PENDING) {
255 | self.status = FULFILLED
256 | self.value = value
257 | }
258 | }
259 |
260 | function reject(reason) {
261 | if (self.status === PENDING) {
262 | self.status = REJECTED
263 | self.reason = reason
264 | }
265 | }
266 |
267 | try {
268 | executor(resolve, reject)
269 | } catch (e) {
270 | reject(e)
271 | }
272 | }
273 |
274 | SyncPromise.prototype.then = function (onFulfilled, onRejected) {
275 | let self = this
276 | if (self.status === FULFILLED) {
277 | onFulfilled(self.value)
278 | }
279 | if (self.status === REJECTED) {
280 | onRejected(self.reason)
281 | }
282 | }
283 |
284 | // 测试同步 Promise
285 | new SyncPromise((resolve, reject) => {
286 | resolve("1")
287 | }).then(
288 | data => {
289 | console.log(data)
290 | },
291 | reason => {
292 | console.log(reason)
293 | }
294 | )
295 |
296 | new SyncPromise((resolve, reject) => {
297 | reject("请求失败")
298 | }).then(
299 | data => {
300 | console.log(data)
301 | },
302 | reason => {
303 | console.log(reason)
304 | }
305 | )
306 |
307 | // resolve 和 reject 都能准确输出
308 |
309 | // 发现存在的问题
310 |
311 | // 1. 异步不支持
312 | new SyncPromise((resolve, reject) => {
313 | setTimeout(() => {
314 | resolve(2)
315 | })
316 | }).then(
317 | data => {
318 | console.log(data)
319 | },
320 | reason => {
321 | console.log(reason)
322 | }
323 | )
324 | // 同步的话, 会先执行 .then()
325 | // 而 status 还是 PENDING, 无法进入 FULFILLED
326 |
327 | // 2. then 没有容错
328 | new SyncPromise((resolve, reject) => {
329 | resolve(1)
330 | }).then()
331 |
332 | // 比如没有给 then 传递参数, 程序就会报错
333 |
334 | // 3. 实现异步 Promise
335 |
336 | const APENDING = "pending"
337 | const AFULFILLED = "fulfilled"
338 | const AREJECTED = "rejected"
339 | function AsyncPromise() {
340 | let self = this
341 | self.status = APENDING
342 | self.value = undefined
343 | self.reason = undefined
344 | self.onFulfillCallbacks = []
345 | self.onRejectedCallbacks = []
346 |
347 | function resolve(value) {
348 | if (self.status === APENDING) {
349 | self.status = AFULFILLED
350 | self.value = value
351 | }
352 | }
353 |
354 | function reject(reason) {
355 | if (self.status === APENDING) {
356 | self.status = AREJECTED
357 | self.reason = reason
358 | }
359 | }
360 |
361 | try {
362 | fn(resolve, reject)
363 | } catch (e) {
364 | reject(e)
365 | }
366 | }
367 |
368 | function resolveAsyncPromise(newPromise, x, resolve, reject) {
369 | if (newPromise === x) {
370 | return reject(new TypeError("循环使用"))
371 | }
372 |
373 | let called = false
374 | if (x instanceof AsyncPromise) {
375 | if (x.status === APENDING) {
376 | x.then(
377 | y => {
378 | resolveAsyncPromise(newPromise, y, resolve, reject)
379 | },
380 | reason => {
381 | reject(reason)
382 | }
383 | )
384 | } else {
385 | x.then(resolve, reject)
386 | }
387 | } else if (x != null && (typeof x === "object" || typeof x === "function")) {
388 | try {
389 | let then = x.then
390 | if (typeof then === "function") {
391 | then.call(
392 | (x, y) => {
393 | if (called) return
394 | called = true
395 | resolveAsyncPromise(promise, y, resolve, reject)
396 | },
397 | reason => {
398 | if (called) return
399 | called = true
400 | reject(reason)
401 | }
402 | )
403 | } else {
404 | resolve(x)
405 | }
406 | } catch (e) {
407 | if (called) return
408 | called = true
409 | reject(e)
410 | }
411 | } else {
412 | resolve(x)
413 | }
414 | }
415 |
416 | AsyncPromise.prototype.then = function (onFulfilled, onRejected) {
417 | let self = this
418 | let newPromise
419 | onFulfilled =
420 | typeof onFulfilled === "function"
421 | ? onFulfilled
422 | : function (value) {
423 | return value
424 | }
425 | onRejected =
426 | typeof onRejected === "function"
427 | ? onRejected
428 | : function (reason) {
429 | throw reason
430 | }
431 |
432 | if (self.status === APENDING) {
433 | return (newPromise = new AsyncPromise((resolve, reject) => {
434 | self.onFulfillCallbacks.push(value => {
435 | try {
436 | let x = onFulfilled(value)
437 | resolvePromise(newPromise, x, resolve, reject)
438 | } catch (e) {
439 | reject(e)
440 | }
441 | })
442 |
443 | self.onRejectedCallbacks.push(reason => {
444 | try {
445 | let x = onRejected(reason)
446 | resolveAsyncPromise(newPromise, x, resolve, reject)
447 | } catch (e) {
448 | reject(e)
449 | }
450 | })
451 | }))
452 | }
453 | }
454 |
--------------------------------------------------------------------------------
/04-Async/README.md:
--------------------------------------------------------------------------------
1 | # Promise 及 Async + Await 学习
2 |
3 | ## 目录
4 |
5 | - **[异步发展流程](#异步发展流程)**
6 | - **[Promise](#promise)**
7 | - **[Async + Await](#async--await)**
8 | - **[异步函数的比较](#异步函数的比较)**
9 | - **[避免太过循序](#避免太过循序)**
10 |
11 | ## 异步发展流程
12 |
13 | ### 1. 同步与异步
14 |
15 | 同步:有一个任务分成两步执行,执行完第一步再执行第二步,连续的执行就叫同步
16 | 异步:有一个任务分成两步执行,执行第一步,在等待第一步完成这段时间执行其他任务,再执行第二步,不连续的执行叫异步
17 |
18 | ### 2. 将异步转换为同步的方式(发展流程)
19 |
20 | - 回调函数实现
21 | - 事件监听
22 | - 发布订阅
23 | - Promise/A+ 和生成器函数
24 | - async/await
25 |
26 |
27 | ## Promise
28 |
29 | ### 1. 链式调用
30 |
31 | 在过去
32 |
33 | ```
34 | doSomething(function(result) {
35 | doSomethingElse(result, function(newResult) {
36 | doThirdThing(newResult, function(finalResult) {
37 | console.log('Got the final result: ' + finalResult);
38 | }, failureCallback);
39 | }, failureCallback);
40 | }, failureCallback);
41 | ```
42 |
43 | 通过新式函数, then 里的参数是可选的
44 |
45 | ```
46 | doSomething().then(function(result) {
47 | return doSomethingElse(result);
48 | })
49 | .then(function(newResult) {
50 | return doThirdThing(newResult);
51 | })
52 | .then(function(finalResult) {
53 | console.log('Got the final result: ' + finalResult);
54 | })
55 | .catch(failureCallback);
56 | ```
57 |
58 | ### 2. Catch 的后续操作
59 |
60 | ```
61 | new Promise((resolve, reject) => {
62 | console.log('Initial')
63 | resolve()
64 | })
65 | .then(() => {
66 | throw new Error('Something failed')
67 |
68 | console.log('Do this')
69 | })
70 | .catch(() => {
71 | console.log('Do that')
72 | })
73 | .then(() => {
74 | console.log('Do this whatever happened before')
75 | })
76 | ```
77 |
78 | ### 3. 创建一个 Promise
79 |
80 | ```
81 | const myFirstPromise = new Promise((resolve, reject) => {
82 | 做一些异步操作,最终会调用下面两者之一
83 | resolve(someValue)
84 | 或
85 | reject("failure reason")
86 | })
87 | ```
88 |
89 | 让某个函数有 Promise 功能,只需返回一个 Promise 即可
90 | 比如封装一个 get 请求
91 |
92 | ```
93 | function myAsyncFunc(url) {
94 | return new Promise((resolve, reject) => {
95 | const xhr = new XMLHttpRequest()
96 | xhr.open("GET", url)
97 | xhr.onload = () => resolve(xhr.responseText)
98 | xhr.onerror = () => reject(xhr.statusText)
99 | xhr.send()
100 | })
101 | }
102 | ```
103 |
104 | ### 4. 解决回调地狱
105 |
106 | 使用 Promise 前
107 |
108 | ```
109 | let fs = require('fs')
110 | fs.readFile('./a.txt','utf8',function(err,data){
111 | fs.readFile(data,'utf8',function(err,data){
112 | fs.readFile(data,'utf8',function(err,data){
113 | console.log(data)
114 | })
115 | })
116 | })
117 | ```
118 |
119 | 使用 Promise 后
120 |
121 | ```
122 | let fs = require('fs')
123 | function readFile(url) {
124 | return new Promise((resolve, reject) => {
125 | fs.readFile(url, 'utf-8', function(error, data) {
126 | error && reject(error)
127 | resolve(data)
128 | })
129 | })
130 | }
131 |
132 | readFile('./a.txt').then(data => {
133 | return readFile(data)
134 | }).then(data => {
135 | return readFile(data)
136 | }).then(data => {
137 | console.log(data)
138 | })
139 | ```
140 |
141 | ## Async + Await
142 |
143 | ### 1. 与其他异步操作的对比
144 |
145 | 先定义一个 Fetch 方法
146 |
147 | ```
148 | var fetch = require('node-fetch')
149 |
150 | function fetchUser() {
151 | return new Promise((resolve, reject) => {
152 | fetch('https://api.github.com/users/stephentian')
153 | .then((res) => {
154 | resolve(res)
155 | }, (error) => {
156 | reject(error)
157 | })
158 | })
159 | }
160 | ```
161 |
162 | (1) Promise 方式, 使用上面封装好的函数
163 |
164 | ```
165 | function getUserByPromise() {
166 | fetchUser()
167 | .then((data) => {
168 | console.log(data)
169 | }, (error) => {
170 | console.log(error)
171 | })
172 | }
173 | getUserByPromise()
174 | ```
175 |
176 | Promise 的方式虽然解决了 callback hell
177 | 但是这种方式充满了 Promise 的 then() 方法,如果处理流程复杂的话,整段代码将充满 then
178 |
179 | (2) Generator 方式
180 |
181 | ```
182 | function* fetchUserByGenerator() {
183 | const user = yield fetchUser()
184 | return user
185 | }
186 | const g = fetchUserByGenerator()
187 | const result = g.next().value
188 | result.then((v) => {
189 | console.log(v)
190 | }, (error) => {
191 | console.log(error)
192 | })
193 | ```
194 |
195 | (3) Async 方式
196 |
197 | ```
198 | async function getUserBuAsync() {
199 | let user = await fetchUser()
200 | return user
201 | }
202 | getUserBuAsync()
203 | .then(v => console.log(v))
204 | ```
205 |
206 | ### 2. async 返回一个 Promise 对象
207 |
208 | ```
209 | async function f() {
210 | return "Hello World"
211 | }
212 | f().then(v => console.log(v))
213 | ```
214 |
215 | 如果 async 函数内部抛出异常,则会导致返回的 Promise 对象状态变为 reject 状态
216 | 抛出的错误而会被 catch 方法回调函数接收到
217 |
218 | ```
219 | async function e() {
220 | throw new Error('error!')
221 | }
222 | e().then(v => console.log(v))
223 | .catch(err => console.log(err))
224 | ```
225 |
226 | ### 3. Async 其他函数的用法
227 |
228 | (1) 箭头函数
229 |
230 | ```
231 | // map some URLs to json-promises
232 | const jsonPromises = urls.map(async url => {
233 | const response = await fetch(url);
234 | return response.json();
235 | });
236 | ```
237 |
238 | > array.map(func) 不在乎我提供给它的是不是异步函数,只把它当作一个返回 Promise 的函数来看待
239 | > 它不会等到第一个函数执行完毕就会调用第二个函数
240 |
241 | (2) 对象方法
242 |
243 | ```
244 | const storage = {
245 | async getAvatar(name) {
246 | const cache = await caches.open('avatars')
247 | return cache.match(`/avatars/${name}.jpg`)
248 | }
249 | }
250 | storage.getAvatar('abcsss').then()
251 | ```
252 |
253 | (3) 类方法
254 |
255 | ```
256 | class Storage {
257 | constructor() {
258 | this.cachePromise = caches.open('avatars')
259 | }
260 |
261 | async getAvatar(name) {
262 | const cache = awat this.cachePromise
263 | return cache.match(`/avatars/${name}.jpg`)
264 | }
265 | }
266 |
267 | const storage = new Storage()
268 | storage.getAvatar('aabbb').then()
269 | ```
270 |
271 | > 类构造函数以及 getter/settings 方法不能是异步的
272 |
273 | ## 异步函数的比较
274 |
275 | 假设我们想获取某个网址并以文本形式记录响应日志
276 |
277 | **Promise 编写代码**
278 |
279 | ```
280 | function logFetch(url) {
281 | return fetch(url)
282 | .then(response => response.text())
283 | .then(text => {
284 | console.log(text);
285 | }).catch(err => {
286 | console.error('fetch failed', err);
287 | });
288 | }
289 | ```
290 |
291 | **利用异步函数 Async + Await 具有相同作用的代码**
292 |
293 | ```
294 | async function logFetch(url) {
295 | try {
296 | const response = await fetch(url);
297 | console.log(await response.text());
298 | }
299 | catch (err) {
300 | console.log('fetch failed', err);
301 | }
302 | }
303 | ```
304 |
305 | > 代码行数虽然相同,但去掉了所有回调
306 | > 这可以提高代码的可读性,对不太熟悉 Promise 的人而言,帮助就更大了
307 |
308 | ## 避免太过循序
309 |
310 | 尽管编写的是看似同步的代码,也一定不要错失并行执行的机会
311 |
312 | ```
313 | async function series() {
314 | await wait(500);
315 | await wait(500);
316 | return "done!";
317 | }
318 | ```
319 |
320 | 以上代码执行完毕需要 1000 毫秒
321 |
322 | ```
323 | asyc function parallel() {
324 | const wait1 = wait(500)
325 | const wait2 = wait(500)
326 | await wait1
327 | await wait2
328 | return 'done!'
329 | }
330 | ```
331 |
332 | 以上代码只需 500 毫秒就可执行完毕,因为两个 wait 是同时发生的
333 |
334 | **按顺序输出获取的数据**
335 | 假定我们想获取一系列网址,并尽快按正确顺序将它们记录到日志中
336 |
337 | ```
338 | function logInOrder(urls) {
339 | // fetch all the URLs
340 | const textPromises = urls.map(url => {
341 | return fetch(url).then(response => response.text());
342 | });
343 |
344 | // log them in order
345 | textPromises.reduce((chain, textPromise) => {
346 | return chain.then(() => textPromise)
347 | .then(text => console.log(text));
348 | }, Promise.resolve());
349 | }
350 | ```
351 |
352 | 如果使用异步函数改写以上代码,又容易让代码变得过于循序
353 |
354 | ```
355 | async function logInOrder(urls) {
356 | for (const url of urls) {
357 | const response = await fetch(url);
358 | console.log(await response.text());
359 | }
360 | }
361 | ```
362 |
363 | **推荐的编码方式** - 可读性强、并行效率高
364 |
365 | ```
366 | async function logInOrder(urls) {
367 | // fetch all the URLs in parallel
368 | const textPromises = urls.map(async url => {
369 | const response = await fetch(url);
370 | return response.text();
371 | });
372 |
373 | // log them in sequence
374 | for (const textPromise of textPromises) {
375 | console.log(await textPromise);
376 | }
377 | }
378 | ```
379 |
380 | 以并行方式获取和读取网址,但将“智能”的 reduce 部分替换成标准单调乏味但可读性强的 for 循环
381 |
--------------------------------------------------------------------------------
/04-Async/a.txt:
--------------------------------------------------------------------------------
1 | a
2 |
--------------------------------------------------------------------------------
/04-Async/b.txt:
--------------------------------------------------------------------------------
1 | bb
2 |
--------------------------------------------------------------------------------
/04-Async/c.txt:
--------------------------------------------------------------------------------
1 | ccc
2 |
--------------------------------------------------------------------------------
/04-Async/test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019/3/2
5 | **/
6 |
7 | // MyPromise 测试用例
8 |
9 | let MyPromise = require('./06-CreatPromise')
10 | let p1 = new MyPromise(function (resolve, reject) {
11 | setTimeout(function () {
12 | let num = Math.random()
13 | if (num < .5) {
14 | resolve(num)
15 | } else {
16 | reject('失败, 小于0.5')
17 | }
18 | })
19 | })
20 | p1.then(function (data) {
21 | console.log(data)
22 | }, function (err) {
23 | console.log(err)
24 | })
25 |
--------------------------------------------------------------------------------
/05-Regexp/01-Grammar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-11-2
5 | **/
6 |
7 | var regex1 = /.a/;
8 | var string1 = 'ba';
9 | var string2 = ' a';
10 | var string3 = '@abc';
11 | var string4 = `
12 | abc
13 | xyz
14 | `;
15 |
16 | console.log(regex1.test(string1));
17 | // true
18 | console.log(regex1.test(string2));
19 | // true
20 | console.log(regex1.test(string3));
21 | // true
22 |
23 | console.log(regex1.test(string4));
24 | // false 无法匹配换行符
--------------------------------------------------------------------------------
/05-Regexp/README.md:
--------------------------------------------------------------------------------
1 | # Regex 正则表达式
2 |
3 | > 多看,多认,多写
4 |
5 | ## 目录
6 |
7 | - **[前提知识](#前提知识)**
8 | - **[语法 Grammar](#语法-grammar)**
9 | - **[方法 Function](#方法-function)**
10 | - **[常见用例](#常见用例)**
11 |
12 | ## 前提知识
13 |
14 | #### 1. **创建正则**
15 | - 字面量方式
16 | ```
17 | var regex = /***/
18 | ```
19 | - 实例方式
20 | ```
21 | var reg = new Regep('/***/')
22 | ```
23 | #### 2. **贪婪模式 Greedy**
24 | 默认模式,正则表达式一般趋向于最大长度匹配(往长了走)
25 |
26 | #### 3. **非贪婪模式**
27 | 非贪婪模式, 懒惰匹配, 匹配长度更短的字符(往短了走)
28 |
29 | #### 4. **lastIndex**
30 | 整数, 下一个匹配字符位置, 从 0 开始算
31 |
32 | ## 语法 Grammar
33 |
34 | ### 基本元字符
35 |
36 | 1. `.`: 匹配任何单个字符(除了换行符)
37 | 2. `\`: 将特殊字符转为字面量
38 | 3. `|`: "或" 操作符
39 | 4. `[]`: 字符集合,匹配集合中的一个字符
40 | 5. `[^]`: 对集合里, "非" 操作符
41 | 6. `-`: 定义一个区间,比如 `[a-d]` 表示去 a-d 之间(a, b, c, d)
42 |
43 | ### 数量元字符
44 |
45 | 1. `{n}`: 匹配 n 次
46 | 2. `{m, n}`: 匹配字符至少 m 次, 至多 n 次
47 | 3. `+`: 匹配字符一次或多次
48 | 4. `*`: 匹配字符 0 次或多次
49 | 5. `?`: 匹配字符 0 次或 1 次
50 |
51 | > 在这些量词后加 `?` 会变成 **非贪婪模式**
52 |
53 | ### 位置元字符
54 |
55 | 1. `^`: 匹配表达式的开始
56 | 2. `$`: 匹配表达式的结束
57 | 3. `\b`: 匹配单词边界
58 | 4. `\B`: 匹配非单词边界
59 | 5. `(?=p)`: 匹配 p 前面的位置
60 | 6. `(?!p)`: 匹配不在 p 的位置
61 |
62 | ### 特殊元字符
63 |
64 | 1. `\d`: 数字, 同 `[0-9]`
65 | 2. `\D`: 非数字, 同 `[^0-9]`
66 | 3. `\s`: 空白符, 包括空格, 水平制表符(`\t`), 垂直制表符(`\v`), 换行符(`\n`), 回车符(`\r`), 换页符(`\f`)
67 | 4. `\S`: 非空白符
68 | 5. `\w`: 数字、字母、下划线, 同 [0-9a-zA-Z]
69 | 6. `\W`: 非数字字母字符
70 | 7. `\n`: 换行符
71 |
72 | ### 标志字符
73 |
74 | 1. `g`: global, 全局搜索
75 | 2. `i`: ignore, 不区分大小写
76 | 3. `m`: multiline, 多行搜索
77 |
78 | ## 方法 Function
79 |
80 | ### 正则对象方法
81 |
82 | #### 1. `test`
83 | 匹配返回 true, 不匹配则返回 false
84 | ```
85 | var regex = /a/
86 | var string = 'aaa'
87 | regex.test(string) // return true
88 | ```
89 |
90 | #### 2. `exec`
91 | 没有匹配是返回 null, 匹配时返回
92 | ```
93 | [0: (整个模式匹配的最长的字符串),
94 | 1: (第二长的字符串),
95 | ...(没有则为 null),
96 | index: 0,
97 | input: ''(要匹配的字符串),
98 | length: 1(前面匹配到的个数)]
99 | ```
100 |
101 | ### string 的方法
102 |
103 | #### 1. `match`
104 |
105 | 返回值和接受参数(正则)是否包含 `g` 有关
106 | - 如果没有 `g` 标志, match 对 string 做一次匹配
107 | ```
108 | var regex = /[a-d]/i
109 | var string = 'aBcDef'
110 | string.match(regex)
111 | // ouput
112 | [
113 | 0: "a"
114 | groups: undefined
115 | index: 0
116 | input: "aBcDef"
117 | ]
118 | ```
119 | - 如果有, 会返回一个数组
120 | ```
121 | var regexg = /[a-d]/gi
122 | string.match(regexg)
123 | // output
124 | ['a', 'B', 'c', 'D']
125 | ```
126 |
127 | #### 2. `search`
128 |
129 | - 接受一个正则作为参数
130 | - 如果参数不为正则, 会隐式的使用 new Regexp() 将其转化为正则
131 | - 匹配到返回 0, 没匹配到返回 -1
132 |
133 | #### 3. `replace`
134 |
135 | - 接受两个参数, 第一个为被替换文本, 第二个为要替换的文本
136 | - 第一个参数可以是正则, 也可以是字符串
137 | - 第二个参数可以是字符串, 也可以是函数
138 |
139 | | 变量名 | 作用 |
140 | | ------ | ------ |
141 | | $$ | 插入一个 $ |
142 | | $& | 插入匹配的子串 |
143 | | $` | 插入当前匹配的子串左边的内容 |
144 | | $' | 插入当前匹配的子串右边的内容 |
145 | | $n | 假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串 |
146 |
147 | ```
148 | var re = /(\w+)\s(\w+)/
149 | var str = "John Smith"
150 | var newstr = str.replace(re, "$2, $1")
151 | console.log(newstr)
152 | // Smith, John
153 |
154 | var string = '2018-11-12'
155 | var today = string.replace(/-/g, '/')
156 | console.log(today)
157 | // '2018/11/12'
158 |
159 | string.replace(/-/g, function(result, key, index, group) {
160 | // 输出 三个参数
161 | // 匹配的正则
162 | // 捕获到的字符
163 | // 正则匹配到的每段字符的第一个字符的索引
164 | // 用于匹配的字符串
165 | })
166 | ```
167 |
168 | #### 4. `split`
169 |
170 | - 接受两个参数, 返回一个数组
171 | - 第一个是用来分割字符串的字符或者正则
172 | - 第二个参数可选作为限制分割多少个字符,也是返回的数组的长度限制
173 |
174 | ```
175 | var string = '123a4Bc56Def'
176 | console.log(string.split(/[a-z]+/i))
177 | // output
178 | ["123", "4", "56", ""]
179 |
180 | console.log(string.split(/[a-z]+/i, 2))
181 | // output
182 | ["123", "4"]
183 | ```
184 |
185 | ## 常见用例
186 |
187 | 1. 写一个 function , 将 'get-element-by-id', 转换为驼峰命名法形式的字符串 'getElementById'
188 | ```
189 | var f = function(str) {
190 | return str.replace(/-\w/g, function(v) {
191 | return v.slice(1).toUpperCase()
192 | })
193 | }
194 | ```
195 |
196 | 2.
--------------------------------------------------------------------------------
/06-Date/01-ParseTime.js:
--------------------------------------------------------------------------------
1 | function parseTime(time, cFormat) {
2 | if (arguments.length === 0) {
3 | return null;
4 | }
5 |
6 | if ((time + "").length === 10) {
7 | time = +time * 1000;
8 | }
9 |
10 | const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}";
11 | let date;
12 | if (typeof time === "object") {
13 | date = time;
14 | } else {
15 | date = new Date(parseInt(time));
16 | }
17 | const formatObj = {
18 | y: date.getFullYear(),
19 | m: date.getMonth() + 1,
20 | d: date.getDate(),
21 | h: date.getHours(),
22 | i: date.getMinutes(),
23 | s: date.getSeconds(),
24 | a: date.getDay()
25 | };
26 | const timeStr = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
27 | console.log(result)
28 | console.log(key)
29 | let value = formatObj[key];
30 | console.log('value:', value)
31 | if (key === "a")
32 | return ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][value - 1];
33 | if (result.length > 0 && value < 10) {
34 | value = "0" + value;
35 | }
36 | return value || 0;
37 | });
38 | return timeStr;
39 | }
40 |
41 | // console.log(parseTime('2018-11-13T07:36:20.370Z', '{y}-{m}-{d} {h}:{i}'))
42 | // console.log(parseTime('1542094580370', '{y}-{m}-{d} {h}:{i}:{s}'))
43 | console.log(parseTime('1542094580370', '{y}-{m}-{d} {h}:{i}:{s} {a}'))
--------------------------------------------------------------------------------
/06-Date/README.md:
--------------------------------------------------------------------------------
1 | # Date 对象
2 |
--------------------------------------------------------------------------------
/07-OOP/01-Create Object.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018/11/16
5 | **/
6 |
7 | // 1. 创建对象
8 | var arr = [1, 2, 3, 4, 5]
9 |
10 | // 可以在 arr 里看到 __proto__
11 | console.log(arr)
12 |
13 | var str = 'StepenTian'
14 |
15 | console.log(str.__proto__)
16 | // 虽然在 str 看不到 __proto__,但它其实有这个隐式属性,指向 String.prototype
--------------------------------------------------------------------------------
/07-OOP/02-Inheritance.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018/11/21
5 | **/
6 |
7 | // 1. New
8 | function Animals(name) {
9 | this.name = name
10 | }
11 | var animal1 = new Animals('animal1')
12 | console.log('animal1:', animal1)
13 |
14 | // New 相当于如下
15 | var animal2 = {}
16 | animal2.__proto__ = Animals.prototype
17 | Animals.call(animal2, 'animal2')
18 | console.log('animal2:', animal2)
19 |
20 |
21 | // 2. 原型继承
22 | function Student(name) {
23 | this.name = name || 'Unnamed'
24 | }
25 |
26 | // 简单把功能及参数继承过来
27 | function PrimaryStudent(name, grade) {
28 | // 将 Student 里的变量继承过来,即把 this 作用域转移过来
29 | Student.call(this, name)
30 | this.grade = grade || 1
31 | }
32 |
33 | // 使用构造函数创建对象
34 | var student1 = new Student('aaa')
35 | var student2 = new PrimaryStudent('bbb')
36 | console.log(Student.prototype.isPrototypeOf(PrimaryStudent.prototype))
37 | // false, 说明不是继承
38 |
39 | // 当前的原型链是:
40 | // student1 --> Student.prototype --> Object.prototype --> null
41 | // student2 --> PrimaryStudent.prototype --> Object.prototype --> null
42 |
43 | // 如果是 继承, 原型链应该为:
44 | // student2 --> PrimaryStudent.prototype --> Student.prototype --> Object.prototype --> null
45 |
46 | // 有这种方式:
47 | function Student(name) {
48 | this.name = name || 'Unnamed'
49 | }
50 |
51 | // 利用 call 把 Student 中通过 this 指定的属性和方法复制到子类的实例中
52 | function PrimaryStudent(name, grade) {
53 | Student.call(this, name)
54 | this.grade = grade || 1
55 | }
56 | PrimaryStudent.prototype = new Student()
57 | PrimaryStudent.prototype.constructor = PrimaryStudent
58 |
59 | // 验证
60 | var student3 = new PrimaryStudent('ccc', 3)
61 | console.log(student3)
62 | console.log(student3.__proto__ === PrimaryStudent.prototype)
63 | console.log(student3.__proto__.__proto__ === Student.prototype)
64 | console.log(Student.prototype.isPrototypeOf(PrimaryStudent.prototype))
65 |
--------------------------------------------------------------------------------
/07-OOP/03-Class.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-11-27
5 | **/
6 |
7 | // Class 继承
8 |
9 | // 普通构造函数
10 | function Student(name) {
11 | this.name = name || 'Unnamed'
12 | }
13 |
14 | Student.prototype.sayName = function () {
15 | console.log('name: ', this.name)
16 | }
17 |
18 | // Class 来实现
19 | //
20 | class Student {
21 | constructor(name) {
22 | this.name = name
23 | }
24 | sayName() {
25 | console.log('name: ', this.name)
26 | }
27 | }
28 |
29 | // class 继承
30 | class PrimaryStudent extends Student {
31 | constructor(name, grade) {
32 | // 需要用 super 来调用父类的构造函数
33 | super(name)
34 | this.grade = grade
35 | }
36 | myGrade() {
37 | console.log('My grade is: ', this.grade)
38 | }
39 | }
--------------------------------------------------------------------------------
/07-OOP/README.md:
--------------------------------------------------------------------------------
1 | # OOP 面向对象编程
2 | > JavaScript在设计之初就是一种面向对象的语言,虽然我们在日常的开发中很少能体验到这一点。
3 |
4 | ## 目录
5 |
6 | **[1. 创建对象](#1-创建对象)**
7 | * [原始对象](#原始对象)
8 | * [初级封装](#初级封装)
9 | * [构造函数](#构造函数)
10 | * [实例和实例化](#实例和实例化)
11 | * [原型 prototype](#原型-prototype)
12 | * [new](#new)
13 |
14 | **[2. 一些概念](#2-一些概念)**
15 | * [instanceof](#instanceof)
16 | * [constructor](#constructor)
17 | * [instanceof 和 constructor](#instanceof-和-constructor)
18 | * [isPrototypeOf](#isprototypeof)
19 | * [hasOwnProperty](#hasownproperty)
20 | * [prototype、\__proto__ 和 constructor-的区别](#prototype-__proto__-和-constructor-的区别)
21 |
22 | **[3. 封装]()**
23 |
24 | **[4. 继承]()**
25 |
26 | **[5. 多态]()**
27 |
28 | ## 前言
29 |
30 | JavaScript是一款基于 `原型`模式的面向对象语言。
31 |
32 | 我们可以把`原型(prototype)`理解为是一个**使用说明书**,每一个类型都会有一个对应的使用说明书,凡是使用说明上规定的成员(参数方法),都是可以使用的。(记住这个概念,后面必考)
33 |
34 | 比如电脑的说明书上规定了开机、关机等行为,配备了GTX1060显卡、I9处理器等参数,那么带有这个说明书的每一台电视机都会具备这些功能及配置。
35 |
36 | 记住面向对象拥有三大特征:
37 | 1. 封装
38 | 2. 继承
39 | 3. 多态
40 |
41 | 后面我们会讲。
42 |
43 |
44 | ## 1. 创建对象
45 |
46 | ### 原始对象
47 |
48 | 可以创建一个 Object 对象
49 |
50 |
51 | ```
52 | var obj = {
53 | name: 'StepenTian',
54 | age: 18,
55 | getSex: function() {
56 | return 'male'
57 | }
58 | }
59 | console.log(obj.getSex())
60 |
61 | // 返回 male
62 | ```
63 |
64 | 我们可以为该实例手动添加任何成员,可以是字符、数字、布尔甚至于一个方法。
65 | 定义的方式,即可以用`实例名.成员名 = 内容`,也可以使用`实例名["成员名"] = 内容`。
66 |
67 | ```
68 | var str = "phone"
69 | obj[str] = 15900000000
70 |
71 | console.log(obj['phone'])
72 | console.log(obj['name'])
73 | ```
74 |
75 | 创建一个 Array
76 |
77 | ```
78 | var arr = [1, 2, 3, 4, 5]
79 | console.log(arr)
80 | ```
81 |
82 | 在 Chrome 控制台打印出
83 | 展开数组发现, 数组里怎么还有个 `__proto__:Array` ?(但其实 `arr.__proto__` 不是 Array 对象)
84 | 展开 `__proto__`, 可以发现一系列数组(`pop, push, shift, unshift`)的方法, 原来操作 `arr` 的方法是从这来的
85 |
86 |
87 | ### 初级封装
88 |
89 | 为了能够得到更好的封装效果, 我们通过一个 function 来统一地构建一个对象的实例
90 |
91 | ```
92 | function Person(name, age) {
93 | return {
94 | name: name,
95 | age: age,
96 | getSex: function() {
97 | return 'male'
98 | }
99 | }
100 | }
101 |
102 | ```
103 |
104 | ### 构造函数
105 |
106 | 上面的 Person 并不是真正的构造函数,JavaScript中提供了真正的构造函数,它的语法和定义一个 function 其实是一样的:
107 |
108 | ```
109 | function Person(name, age) {
110 | this.name = name,
111 | this.age = age,
112 | this.sayName = function() {
113 | return this.name
114 | }
115 | }
116 | ```
117 | [不懂 this,你可以先去看:this]()
118 |
119 | Person 内部通过 this 关键字给实例增加了新的属性和方法。
120 |
121 | ### 实例和实例化实例化
122 |
123 | 使用 new 来创造 Person 的一个对象,这个对象叫 ‘实例’,创造的过程,我们叫实例化
124 |
125 | 可以想象成,我们就是造物主,在创造一个一个人,这些人有名字,有年龄,会说话
126 | 我们可以创造两个例子 亚当和夏娃
127 |
128 | ```
129 | var adam = new Person('adam', 22) // 设定一个法定结婚年龄好
130 | var eva = new Person('eva', 20)
131 | console.log(adam.sayName == eva.sayName)
132 | // false
133 | ```
134 | 我们还可以发现 adam 和 eva 的 sayName 并不指向不一个内存,
135 | 也就意味着,当我们有很多实例的时候,内存开销会非常大。
136 |
137 | 造两个人好累啊,要教他们各种技能,生火,做饭,缝衣服......
138 | 生小孩这种技能不好教,得想个办法,所以
139 | 为什么不给他们发个说明书呢?(还记得说明书的概念吗?)
140 |
141 | ### 原型 prototype
142 |
143 | 任何类型都会有属于自己的原型,并且原型上定义的成员,可以在每个实例中引用,并且是共用的。
144 |
145 | 可以理解为,每个人都有一本属于自己的使用手册,在人类这本使用手册上说明的属性和技能,每个人都可以有。
146 |
147 | 所以我们在 prototype 上定义方法时,发现不同实例运用的方法是相同的
148 |
149 | ```
150 | function Person(name, age) {
151 | this.name = name,
152 | this.age = age
153 | }
154 |
155 | Person.prototype.sayName = function() {
156 | return this.name
157 | }
158 |
159 | var adam = new Person('adam', 22)
160 | var eva = new Person('eva', 20)
161 | console.log(adam.sayName == eva.sayName)
162 | // true
163 | ```
164 |
165 | 由此在设计JavaScript面向对象类型的时候,我们一般遵循以下规则:
166 |
167 | 1. 因为实例不同而不同的内容,用this关键字声明
168 | 2. 无论实例怎样内容完全相同的成员,定义在prototype上
169 |
170 |
171 | ### new
172 |
173 | 1. 定义一个函数 Animal
174 |
175 | ```
176 | function Animals(name) {
177 | this.name = name
178 | }
179 | ```
180 |
181 | 2. 仔细看, 发现其实就是个首字母大写的普通函数; 可以使用 `new` 让它变成构造函数
182 |
183 | ```
184 | var animal1 = new Animals('animal1')
185 | console.log('animal1:', animal1)
186 | ```
187 |
188 | 3. 打印出 `animal1`, 发现 `name` 变为了 `animal1`
189 |
190 | 4. 让我们换一种方式实现, 先定义一个 `animal2` 空对象, 它是由 Animals 构造的, 所以 `__proto__` 应该为构造函数的原型
191 |
192 | ```
193 | var animal2 = {}
194 | animal2.__proto__ = Animals.prototype
195 | Animals.call(animal2, 'animal2')
196 |
197 | console.log('animal2:', animal2)
198 | ```
199 |
200 | 会发现,两次打印出的内容是一样的(除了 name 属性)
201 |
202 |
203 | ## 2. 一些概念
204 |
205 | ### instanceof
206 |
207 | 使用 构造函数 创建一个 新的对象, 该对象被认为是构造函数的一个 实例(即 `instance`),
208 | `instanceof`就是来判断 该对象 是否由 某个构造函数 创建
209 |
210 | ### constructor
211 |
212 | 1. 用于返回创建该对象的函数
213 |
214 | ```
215 | var object = []
216 | console.log(object.constructor) // Array
217 | ```
218 |
219 | 2. 在**一个**类中只能有一个名为 “constructor” 的特殊方法, 用于创建和初始化类中创建的一个对象的一种特殊方法
220 |
221 | ```
222 | class Polgon {
223 | constructor() {
224 | this.name = 'Polygon'
225 | }
226 | }
227 | ```
228 |
229 | ### instanceof 和 constructor
230 |
231 |
232 |
233 | ### isPrototypeOf
234 |
235 | ### hasOwnProperty
236 |
237 | ### prototype、\__proto__ 和 constructor 的区别
238 |
--------------------------------------------------------------------------------
/08-This/README.md:
--------------------------------------------------------------------------------
1 | # This
2 |
3 | ## 目录
4 | - [What is this ?](#what-is-this-)
5 | - [全局环境](#全局环境)
6 | - [函数内部](#函数内部)
7 | * [非严格模式](#非严格模式)
8 | * [严格模式](#严格模式)
9 | - [call 和 apply](#call-和-apply)
10 | - [原生 js 实现 call 和 apply](#原生-js-实现-call-和-apply)
11 | * [Call](#call)
12 | * [Apply](#apply)
13 | - [bind](#bind)
14 | - [原生 js 实现 bind](#原生-js-实现-bind)
15 | - [箭头函数中的 this](#箭头函数中的-this)
16 | - [原型链中的 this](#原型链中的-this)
17 | - [DOM 中的 this](#dom-中的-this)
18 | - [This 原理](#this-原理)
19 |
20 |
21 |
22 | ## What is this ?
23 |
24 | 在中文中,人们常常用“这个、这个”来指代一些东西,但是如果不加 “手势、眼神”等肢体语言的话,是无法理解的。
25 | 在代码中,`this` 也一样,有全局环境和函数内,在严格模式和非严格模式,都有不同的作用;
26 | 在一般情况下,**`this` 只受最靠近的执行环境影响**
27 |
28 | ## 全局环境
29 |
30 | 全局环境,即全局执行上下文
31 |
32 | ```
33 | // 在浏览器中
34 | console.log(this === window) // true
35 |
36 | // 在 Node 中
37 | console.log(this === module.exports) // true
38 | ```
39 |
40 | ---
41 |
42 | ## 函数内部
43 |
44 | ### 非严格模式
45 |
46 | this 默认指向全局对象 window
47 | ```
48 | function f1() {
49 | return this
50 | }
51 |
52 | // 在浏览器中
53 | console.log(f1() === window) // true
54 |
55 | // 在 Node 中
56 | console.log(f1() === global) // true
57 | ```
58 |
59 | ### 严格模式
60 |
61 | this 默认为 `undefined`
62 |
63 | ---
64 |
65 | ## call 和 apply
66 |
67 | 某个函数通过使用继承自 `Function.prototype` 的 `call` 或 `apply` 方法,
68 | 指定一个特定的的 `this` 和传入若干参数,然后调用
69 |
70 | 1. 将 `this` 的指从一个上下文传到另一个
71 |
72 | ```
73 | var obj = {
74 | a: 'Custom'
75 | }
76 |
77 | var a = 'Global'
78 |
79 | function whatsThis(arg) {
80 | console.log(this.a)
81 | }
82 |
83 | whatsThis()
84 | // 'Global'
85 |
86 | whatsThis.call(obj)
87 | whatsThis.apply(obj)
88 | // 'Custom'
89 | ```
90 |
91 | 2. 在绑定到调用的对象时,还可以传入参数(提现 `call` 和 `apply` 的区别)
92 |
93 | ```
94 | function add(c, d) {
95 | console.log(this.a + this.b + c + d)
96 | }
97 |
98 | var o = {
99 | a: 1,
100 | b: 3
101 | }
102 |
103 | add.call(o, 5, 7)
104 | // 16
105 |
106 | add.apply(o, [0, 1])
107 | // 5
108 | ```
109 |
110 | ---
111 |
112 | ## 原生 js 实现 call 和 apply
113 |
114 | 由上节内容可知 `call` 和 `apply` 的作用:
115 | - 改变 `this` 指向
116 | - 传入参数(可不传)
117 | - 执行函数
118 |
119 | #### Call
120 |
121 | **1.** 想看一般怎么改变 `this` 指向:
122 |
123 | ```
124 | var a = 'global a'
125 | var obj1 = {
126 | a: 'obj1 a'
127 | }
128 | var obj2 = {
129 | a: 'obj2 a'
130 | }
131 |
132 | function f () {
133 | console.log(this.a)
134 | }
135 |
136 | // 直接运行, 打印 'global a'
137 | f()
138 | ```
139 |
140 | **2.** 如果 `obj1` 和 `obj2` 里有 `f` 就好了, 就可以直接调用, 就可以打印里面的 `a` 了.
141 | 可惜没有,我们自己造一个吧:
142 |
143 | ```
144 | obj1.f = f
145 | obj1.f()
146 | // 'obj1 a'
147 | ```
148 |
149 | **3.** 成功!但是有个缺点,obj1 里多了个 `f` 函数,改变了对象,所以我们要把它删除
150 |
151 | ```
152 | obj1.f = f
153 | obj1.f()
154 | delete obj1.f
155 | ```
156 |
157 | **4.** 第一步成功,我们可以实现一个 1.0 版本的 `call`, 并且用 `this` 来指代调用的函数
158 |
159 | ```
160 | Function.prototype.myCall = function(obj) {
161 | obj.f = this
162 | obj.f()
163 | delete obj.f
164 | }
165 | ```
166 |
167 | **5.** 传入参数, 第一个为传入对象, 我们要让 `ob.f` 执行 `obj.f(argument[1], arguments[2], ...)`
168 |
169 | ```
170 | // 新定义一个可接受参数的函数
171 | function fn(b, c) {
172 | console.log('参数1: ', b)
173 | console.log('参数2:', c)
174 | console.log('this.a: ', this.a)
175 | }
176 |
177 | Function.prototype.myCall = function(obj) {
178 | obj.f = this
179 | var args = []
180 | for (var i = 1; i < arguments.length; i++) {
181 | args.push('arguments[' + i + ']')
182 | }
183 |
184 | console.log(args)
185 | // [arguments[1], arguments[2]]
186 |
187 | obj.f(args)
188 | // 情形一:直接传入是数组
189 | // '参数1: ["arguments[1]", "arguments[2]"]'
190 | // '参数2: undefined'
191 | // 失败!
192 |
193 | obj.f(args.toString())
194 | // 情形二:传入字符串
195 | // 打印出 '参数1: arguments[1],arguments[2]'
196 | // '参数2: undefined'
197 | // 失败!
198 |
199 | obj.f(arg[0], args[1])
200 | // 情形三:传入参数两个,但是都是字符串
201 | // 参数1: arguments[1]
202 | // 参数2: arguments[2]
203 | // 失败!
204 |
205 | obj.f(arguments[1], arguments[2])
206 | // 情形四:传参成功,但是参数不确定的话,这种方式就不能用
207 | // 参数1: 1
208 | // 参数2: abc
209 | // 成功!
210 |
211 | obj.f(eval(args[0]), eval(args[1]))
212 | // 情形五:改进 '情形三',使用 `eval` 函数,传参成功,但是参数不确定的话,这种方式也不能用
213 | // 参数1: 1
214 | // 参数2: abc
215 | // 成功!
216 |
217 | delete obj.f
218 | }
219 |
220 | fn.myCall(obj, 1, 'abc')
221 | ```
222 |
223 |
224 | **6.** 由上面 **情形三** 可以知道, 我们已经把 传入的参数 传递给函数了,但是传递的是字符串;
225 | 我们可以结合 **情形五** 来改进
226 |
227 | - 使用 `eval`
228 |
229 | ```
230 | Function.prototype.myCall = function(obj) {
231 | var args = []
232 | for (var i = 1; i < arguments.length; i++) {
233 | args.push('arguments[' + i + ']')
234 | }
235 | obj.f = this
236 | eval('obj.f(' + args + ')')
237 | delete obj.f
238 | }
239 |
240 | ```
241 |
242 | - 使用 ES6 中的解构语法
243 |
244 | ```
245 | Function.prototype.myCall = function (obj, ...args) {
246 | obj.f = this
247 | obj.f(...args)
248 | delete obj.f
249 | }
250 |
251 | fn.myCall(obj, 1, 'abc')
252 | // 参数1: 1
253 | // 参数2: abc
254 | // 成功!
255 | ```
256 |
257 | **7.** 根据上面的版本进行优化
258 | (1) 如果第一个参数为null(没有传参),this指向window
259 | (2) 函数调用要有返回值
260 |
261 | ```
262 | Function.prototype.myCall1 = function(obj) {
263 | var args = [],
264 | argsLength = arguments.length,
265 | obj = obj || window,
266 | result;
267 |
268 | obj.f = this
269 |
270 | for (var i = 1; i < argsLength; i++) {
271 | args.push('arguments[' + i + ']')
272 | }
273 | result = eval('obj.f(' + args + ')')
274 |
275 | delete obj.f
276 | return result
277 | }
278 |
279 | var a = 'global a'
280 | var obj = {
281 | a: 'obj a'
282 | }
283 |
284 | function fn(b, c) {
285 | return this.a + ',' + b + ',' + c
286 | }
287 |
288 | console.log(fn.myCall1(obj, 2, 'EFG'))
289 | // 'obj a, 2, EFG'
290 | // 成功!
291 | ```
292 |
293 | **ES 6**
294 | ```
295 | Function.prototype.myCall2 = function (obj) {
296 | var obj = obj || window
297 | obj.f = this
298 |
299 | var args = [...arguments].sclice(1)
300 | var result = obj.f(...args)
301 |
302 | delete obj.f
303 | return result
304 | }
305 | ```
306 |
307 | #### Apply
308 |
309 | `apply` 的实现方式和 `call` 类似,可以先思考下 `apply` 和 `call` 的区别:
310 | `call` 方法接受的是若干个参数的列表,而 `apply` 方法接受的是一个包含多个参数的数组。
311 | 所以要判断接收的参数是否为数组,还有循环数组要从 `0` 开始
312 |
313 | ```
314 | Function.prototype.myApply = function (obj, arr) {
315 | var obj = obj || window,
316 | result,
317 | argsLength = arguments.length;
318 | obj.f = this
319 |
320 | if (!arr) {
321 | result = obj.f()
322 | } else {
323 | var args = []
324 | for (var i = 1; i < argsLength; i++) {
325 | args.push('arguments[' + i + ']')
326 | }
327 | result = eval('obj.f(' + args + ')')
328 | }
329 |
330 | delete obj.f
331 | return result
332 | }
333 | ```
334 |
335 | **ES 6**
336 | ```
337 | Function.prototype.myApply2 = function (obj) {
338 | var obj = obj || window
339 | obj.f = this
340 |
341 | var reslut
342 | if (argument[1]) {
343 | result = obj.f(...argument[1])
344 | } else {
345 | result = obj.f()
346 | }
347 |
348 | delete obj.f
349 | return result
350 | }
351 | ```
352 |
353 | ---
354 |
355 | ## bind
356 |
357 | ES5 引入了 `Function.prototype.bind`。
358 | `f.bind(someObject)` 会创建一个与 `f`具有相同函数体和作用域的函数。
359 | 在新函数中, `this` 被 **永久** 绑定到 `bind` 的第一个参数,无论函数如何被调用。
360 |
361 | ```
362 | function f() {
363 | return this.a
364 | }
365 | var obj1 = {
366 | a: 'obj1 的 a'
367 | }
368 | var obj2 = {
369 | a: 'obj2 的 a'
370 | }
371 |
372 | var firstBind = f.bind(obj1)
373 | console.log(firstBind)
374 | // 'obj1 的 a'
375 | // 创建了 firstBind 函数,和 f 具有相同函数体和作用域
376 |
377 | var secondBind = firstBind.bind(obj2)
378 | console.log(secondBind)
379 | // 新函数中, this 被永久绑定到 bind 的第一个参数了
380 | ```
381 |
382 | ---
383 |
384 | ## 原生 js 实现 bind
385 |
386 | ```
387 | Function.prototype.myBind = function() {
388 | if(typeof this !== 'function') {
389 | throw new Error('非函数不能调用 Function.prototype.bind')
390 | } else {
391 | var self = this,
392 | args = Array.prototype.slice.call(arguments, 1)
393 | F = function() {};
394 | var fBound = function () {
395 | var bindArgs = Array.prototype.slice.call(arguments);
396 | self.apply(this instanceof self ? this : context, args.concat(bindArgs));
397 | }
398 | F.prototype = this.prototype
399 | fBound.prototype = new F()
400 | return fBound
401 | }
402 | }
403 | ```
404 | ---
405 |
406 | ## 箭头函数中的 this
407 |
408 | 在箭头函数中,`this` 与封闭词法上下文的 `this` 保持一致。
409 | 1. 箭头函数在全局代码中,`this` 将被设置为全局对象
410 |
411 | ```
412 | var a = 'global'
413 | var foo = (() => this.a)
414 | console.log(foo())
415 | // global
416 | ```
417 |
418 | **注意** 将this传递给call、bind、或者apply,它将被忽略
419 |
420 | ```
421 | var a = 'global a'
422 | var foo = (() => this.a)
423 | var obj = {
424 | a: 'obj a',
425 | f: foo
426 | }
427 | console.log('obj.f(): ', obj.f())
428 | console.log('foo.call(obj): ', foo.call(obj))
429 | console.log('foo.apply(obj): ', foo.apply(obj))
430 | // 都为 'global a'
431 | ```
432 |
433 | 2. 对象里的 箭头函数 this 将始终指向对象,
434 | 但是若不执行情况下赋值给另一个函数, `this` 将指向另一个函数的执行环境
435 |
436 | ```
437 | var a = 'global a'
438 | var obj = {
439 | a: 'obj a',
440 | f: function() {
441 | var x = (() => this.a)
442 | return x
443 | }
444 | }
445 |
446 | var fn1 = obj.f()
447 | console.log('fn1: ', fn1())
448 | // 'obj a'
449 |
450 | var fn2 = obj.f
451 | console.log('fn2: ', fn2()())
452 | // 'global a'
453 | ```
454 |
455 | ---
456 |
457 | ## 原型链中的 this
458 |
459 | 类似于在 原型链 中定义方法一样,`this` 指向调用该方法的对象
460 |
461 | ```
462 | var obj = {
463 | a: 1,
464 | b: 2,
465 | f: function() {
466 | return this.a + this.b
467 | }
468 | }
469 |
470 | var objChild = Object.create(obj)
471 | objChild.a = 1
472 | objChild.b = 4
473 |
474 | console.log(obj.f())
475 | // 3
476 | console.log(objChild.f())
477 | // 5
478 | // objChild 中没有 `f` 属性,于是在原型链中查找,在 `obj` 中找到 `f`,
479 | // 但是
480 | // 最先查找是以 `objChild.f` 开始查找,所以 `this` 指向 `objChild`
481 | ```
482 |
483 | ---
484 |
485 | ## DOM 中的 this
486 |
487 | 当函数被用作事件处理函数时,它的 `this` 指向触发事件的元素
488 |
489 | ```
490 | function bluify(e) {
491 | // console.log(this === e.currentTarget)
492 | // true
493 |
494 | if (this.style.backgroundColor == 'blue') {
495 | this.style.backgroundColor = 'red'
496 | } else {
497 | this.style.backgroundColor = 'blue'
498 | }
499 | }
500 |
501 | // 获取所有 li 元素
502 | var elements = document.getElementsByTagName('li')
503 |
504 | // 当被点击时,元素变蓝色
505 | for (var i = 0; i < elements.length; i++) {
506 | elements[i].addEventListener('click', bluify, false)
507 | }
508 | ```
509 |
510 | ---
511 |
512 | ## This 的原理
513 |
514 | 想思考个问题:
515 | 为什么 `obj.foo()` 就是在 `obj` 环境执行,而 `var foo = obj.foo`,`foo()` 就变成了全局环境执行?
516 |
517 | 这跟内存里的数据结构有关系
518 |
--------------------------------------------------------------------------------
/08-This/apply.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-8
5 | **/
6 |
7 | // apply
8 |
9 | Function.prototype.myApply = function (context) {
10 | context = context || window
11 | context.fn = this
12 | let result
13 | if (arguments[1]) {
14 | result = context.fn(...arguments[1])
15 | } else {
16 | result = context.fn()
17 | }
18 | delete context.fn
19 | return result
20 | }
21 |
22 | // ES5
23 | Function.prototype.myApply2 = function (obj, arr) {
24 | obj = obj || window
25 | let result
26 | let argsLength = arguments.length
27 | obj.fn = this
28 | if (!arr) {
29 | result = obj.fn()
30 | } else {
31 | let args = []
32 | for (let i = 0; i < argsLength; i++) {
33 | args.push('arr[' + i + ']')
34 | }
35 | result = eval('obj.fn(' + args + ')')
36 | }
37 | delete obj.fn
38 | return result
39 | }
40 |
--------------------------------------------------------------------------------
/08-This/bind.js:
--------------------------------------------------------------------------------
1 | Function.prototype.myBind0 = function (context) {
2 | const self = this,
3 | args = [...arguments].slice(1)
4 | return function F() {
5 | if (this instanceof F) {
6 | return new self(...args, ...arguments)
7 | }
8 | return self.apply(context, args.concat(...arguments))
9 | }
10 | }
11 |
12 | Function.prototype.myBind1 = function (obj, ...arg1) { //arg1收集剩余参数
13 | return (...arg2) => { //返回箭头函数, this绑定调用这个方法(myFind)的函数对象
14 | return this.apply(obj, arg1.concat(arg2)); // 将参数合并
15 | }
16 | }
17 |
18 | Function.prototype.myBind2 = function (obj, ...arg1) {
19 | return (...arg2) => {
20 | let args = arg1.concat(arg2);
21 | let val;
22 | obj._fn_ = this;
23 | val = obj._fn_(...args);
24 | delete obj._fn_;
25 | return val
26 | }
27 | }
28 |
29 | Function.prototype.myBind3 = function (obj) {
30 | let _this = this;
31 | let argArr = [];
32 | let arg1 = [];
33 | for (let i = 1; i < arguments.length; i++) { // 从1开始
34 | arg1.push(arguments[i]); // 这里用arg1数组收集下参数
35 | // 获取arguments是从1开始, 但arg1要从 0(i-1)开始
36 | // 若是用Array.prototype.slice.call(argument)就方便多了
37 | argArr.push('arg1[' + (i - 1) + ']'); // 如果用arguments在返回的函数里运行 会获取不到这个函数里的参数了
38 | }
39 | return function () {
40 | let val;
41 | for (let i = 0; i < arguments.length; i++) { // 从0开始
42 | argArr.push('arguments[' + i + ']');
43 | }
44 | obj._fn_ = _this;
45 | val = eval('obj._fn_(' + argArr + ')');
46 | delete obj._fn_;
47 | return val
48 | };
49 | }
50 |
51 | Function.prototype.myFind4 = function (obj) {
52 | if (obj === null || obj === undefined) {
53 | obj = window;
54 | } else {
55 | obj = Object(obj);
56 | }
57 | let _this = this;
58 | let argArr = [];
59 | let arg1 = [];
60 | for (let i = 1; i < arguments.length; i++) {
61 | arg1.push(arguments[i]);
62 | argArr.push('arg1[' + (i - 1) + ']');
63 | }
64 | return function () {
65 | let val;
66 | for (let i = 0; i < arguments.length; i++) {
67 | argArr.push('arguments[' + i + ']');
68 | }
69 | obj._fn_ = _this;
70 | console.log(argArr);
71 | val = eval('obj._fn_(' + argArr + ')');
72 | delete obj._fn_;
73 | return val
74 | };
75 | }
76 |
--------------------------------------------------------------------------------
/08-This/call.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-7
5 | **/
6 |
7 | // call
8 |
9 | // ES6
10 |
11 | Function.prototype.myCall = function (context) {
12 | constext = context || window
13 | context.fn = this
14 | const args = [...arguments].slice(1)
15 | const result = context.fn(...args)
16 | delete context.fn
17 | return result
18 | }
19 |
20 | Function.prototype.myCall2 = function (obj, ...args) {
21 | obj.fn = this
22 | const result = obj.fn(...args)
23 | delete obj.fn
24 | return result
25 | }
26 |
27 | // ES5
28 |
29 |
30 | function myCall(context) {
31 | let context = context || window
32 | context.fn = this
33 | let args = []
34 | for (let i = 1; i < arguments.length; i++) {
35 | args.push('arguments[' + i + ']')
36 | }
37 | let result = eval('context.fn(' + args + ')')
38 | delete context.fn
39 | return result
40 | }
41 |
--------------------------------------------------------------------------------
/08-This/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | This
9 |
10 |
11 |
12 |
13 |
This --->
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
208 |
209 |
210 |
--------------------------------------------------------------------------------
/08-This/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2018-11-28
5 | **/
6 |
7 |
8 | // 1. 函数内部
9 |
10 | // function f1() {
11 | // return this
12 | // }
13 |
14 | // // 浏览器环境
15 | // true
16 | // console.log('this === window:', this === window)
17 |
18 | // // node 环境
19 | // // true
20 | // console.log('f1() === global:', f1() === global)
21 | // // true
22 | // console.log(this === module.exports)
23 |
24 |
25 |
26 | // 2. 简单回调
27 |
28 | // 情形一
29 | var a = 'Window环境'
30 |
31 | function foo() {
32 | console.log(this.a)
33 | }
34 |
35 | var obj = {
36 | a: 'Obj环境',
37 | foo: foo
38 | }
39 |
40 | function doFoo() {
41 | obj.foo()
42 | }
43 |
44 | function doFoo2(fn) {
45 | fn()
46 | }
47 |
48 | doFoo()
49 | // obj 环境
50 | doFoo2(obj.foo)
51 | // win 环境
52 |
53 | // 情形二
54 | var a = 'Window环境'
55 |
56 | var obj = {
57 | a: 'Obj环境',
58 | foo: function () {
59 | console.log(this.a)
60 | }
61 | }
62 |
63 | function doFoo() {
64 | obj.foo()
65 | }
66 |
67 | function doFoo2(fn) {
68 | fn()
69 | }
70 |
71 | doFoo()
72 | doFoo2(obj.foo)
73 |
74 | // 两次的结果是一样的
75 | // 由此可以看出 this 指向函数 执行时的当前对象,而非声明环境
76 | // 这个方法还学到了: 如何让 函数里的 this, 指向全局
77 |
78 |
79 | // 3. call 和 apply
80 | // 在浏览器中
81 |
82 | var obj = {
83 | a: 'Custom'
84 | }
85 |
86 | var a = 'Global'
87 |
88 | function whatsThis(arg) {
89 | console.log(this.a)
90 | }
91 |
92 | whatsThis()
93 | // 'Global'
94 |
95 | whatsThis.call(obj)
96 | whatsThis.apply(obj)
97 | // 'Custom'
98 |
99 | // 3. node 环境中的 this
100 |
101 | // node 环境中把 var 去掉,将变量挂载到全局变量中
--------------------------------------------------------------------------------
/08-This/useStrict.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | // console.log(window)
4 |
5 | console.log(this === exports)
6 | // true
7 | console.log(this === module.exports)
8 | // true
9 |
10 | function f1() {
11 | return this
12 | }
13 |
14 | console.log(f1() === global)
15 | // false
16 |
17 | // 严格模式下
18 | console.log(f1() === undefined)
19 | // true
--------------------------------------------------------------------------------
/09-Skills/01-shuffle/README.md:
--------------------------------------------------------------------------------
1 | # shuffle 洗牌
2 | > 洗牌算法,将数组随机排序
3 |
4 | *待更新动画演示*
5 |
6 | ### 关于 sort
7 |
8 | JavaScript 开发中有时会遇到要将一个数组随机排序(shuffle)的需求,一般采用array.sort()方法,传入一个比较函数
9 |
10 | ```
11 | var arr = [1, 2, 3, 4, 5]
12 | arr.sort(function() {
13 | return .5 - Math.random()
14 | })
15 |
16 | // ES6
17 | arr.sort(() => .5 - Math.random())
18 |
19 | ```
20 |
21 | 这种写法看似都是随机,但其实是有问题的,**它不是真正的随机**
22 |
23 | ### 验证 sort 随机性
24 |
25 | 使用 表格及动画
26 |
27 | 见 self-training.html
28 |
29 | ### 解决方案
30 |
31 | #### 使用 Fisher–Yates shuffle
32 |
33 | Lodash 库中的 shuffle 算法, 使用的实际是 Fisher–Yates 洗牌算法
34 |
35 | ```
36 | var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
37 |
38 | function shuffle(arr) {
39 | var i = arr.length,
40 | t,
41 | j;
42 | while(i) {
43 | // 在剩下的元素随机选择一位
44 | j = Math.floor(Math.random() * i--);
45 | t = arr[i];
46 | arr[i] = arr[j];
47 | arr[j] = t;
48 | }
49 | return arr
50 | }
51 |
52 | // ES6
53 | function shuffle(arr) {
54 | let i = arr.length
55 | while(i) {
56 | let j = Math.floor(Math.random() * i--);
57 | [arr[j], arr[i]] = [arr[i], arr[j]]
58 | }
59 | return arr
60 | }
61 |
62 | shuffle(arr)
63 | ```
64 |
65 | ### 洗牌算法思路
66 |
67 | 取自:[刘哇勇进阶方案](http://www.cnblogs.com/Wayou/p/fisher_yates_shuffle.html)
68 |
69 | #### 1. 随机取数
70 |
71 | 如果要给每张洗牌, 最随机的方法是,在牌组里随机抽一张,放进另一个牌组,直到所有牌被抽出;
72 | 在代码里,在数组里随机抽取一个数(数组下标),放入一个空数组中,直到原数组所有元素都取掉。
73 |
74 | ```
75 | function shuffle(arr) {
76 | var copy = [],
77 | n = arr.length,
78 | i;
79 | while(n) {
80 | // 在数组里随机选取数组下标
81 | i = Math.floor(Math.random() * arr.length)
82 | if (i in arr) {
83 | copy.push(arr[i])
84 | delete arr[i]
85 | n--
86 | }
87 | }
88 | return copy
89 | }
90 | ```
91 |
92 | **注意:**
93 | 1. Math.random()产生[0,1)的小数
94 | 2. delete 操作只将数组元素的值删除,但不影响数组长度,删除后原来位置的值变为undefined
95 |
96 | 即使一个序号上的元素已经被处理过了,由于随机函数产生的数是随机的,所有这个被处理过的元素序号可能在之后的循环中不断出现,
97 | 一是效率问题,另一个就是逻辑问题了,存在一种可能是永远运行不完!
98 |
99 | #### 2. 改进方案
100 |
--------------------------------------------------------------------------------
/09-Skills/01-shuffle/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | js 数组随机排序
9 |
44 |
45 |
46 |
47 |
48 |
49 | 结果 |
50 | 位置 0 |
51 | 位置 1 |
52 | 位置 2 |
53 | 位置 3 |
54 | 位置 4 |
55 | 位置 5 |
56 | 位置 6 |
57 | 位置 7 |
58 | 位置 8 |
59 | 位置 9 |
60 |
61 |
62 | A出现次数 |
63 | 0 |
64 | 0 |
65 | 0 |
66 | 0 |
67 | 0 |
68 | 0 |
69 | 0 |
70 | 0 |
71 | 0 |
72 | 0 |
73 |
74 |
75 | B出现次数 |
76 | 0 |
77 | 0 |
78 | 0 |
79 | 0 |
80 | 0 |
81 | 0 |
82 | 0 |
83 | 0 |
84 | 0 |
85 | 0 |
86 |
87 |
88 | C出现次数 |
89 | 0 |
90 | 0 |
91 | 0 |
92 | 0 |
93 | 0 |
94 | 0 |
95 | 0 |
96 | 0 |
97 | 0 |
98 | 0 |
99 |
100 |
101 | D出现次数 |
102 | 0 |
103 | 0 |
104 | 0 |
105 | 0 |
106 | 0 |
107 | 0 |
108 | 0 |
109 | 0 |
110 | 0 |
111 | 0 |
112 |
113 |
114 | E出现次数 |
115 | 0 |
116 | 0 |
117 | 0 |
118 | 0 |
119 | 0 |
120 | 0 |
121 | 0 |
122 | 0 |
123 | 0 |
124 | 0 |
125 |
126 |
127 | F出现次数 |
128 | 0 |
129 | 0 |
130 | 0 |
131 | 0 |
132 | 0 |
133 | 0 |
134 | 0 |
135 | 0 |
136 | 0 |
137 | 0 |
138 |
139 |
140 | G出现次数 |
141 | 0 |
142 | 0 |
143 | 0 |
144 | 0 |
145 | 0 |
146 | 0 |
147 | 0 |
148 | 0 |
149 | 0 |
150 | 0 |
151 |
152 |
153 | H出现次数 |
154 | 0 |
155 | 0 |
156 | 0 |
157 | 0 |
158 | 0 |
159 | 0 |
160 | 0 |
161 | 0 |
162 | 0 |
163 | 0 |
164 |
165 |
166 | I出现次数 |
167 | 0 |
168 | 0 |
169 | 0 |
170 | 0 |
171 | 0 |
172 | 0 |
173 | 0 |
174 | 0 |
175 | 0 |
176 | 0 |
177 |
178 |
179 | J出现次数 |
180 | 0 |
181 | 0 |
182 | 0 |
183 | 0 |
184 | 0 |
185 | 0 |
186 | 0 |
187 | 0 |
188 | 0 |
189 | 0 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
336 |
337 |
--------------------------------------------------------------------------------
/09-Skills/01-shuffle/self-training.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
36 |
37 |
38 |
39 |
43 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/09-Skills/01-shuffle/training.js:
--------------------------------------------------------------------------------
1 | class TopAnimate {
2 | constructor(config) {
3 | this.container = document.getElementById(config.el); // 组件容器
4 | this.topNum = config.options.topNum || 8; // 展示多少行数据
5 | this.labelWidth = config.options.labelWidth || 120; // 文字标签宽度
6 | this.spacing = config.options.spacing || 30; // 每行间距
7 | this.maxWidth = config.options.maxWidth || 500; // 最大宽度
8 | this.barHeight = config.options.barHeight || 30; // 数值条高度
9 | this.colors = config.options.colors || []; // 数值条颜色数组
10 | this.title = config.options.title; // 组件标题
11 | this.data; // 组件数据
12 | this.domArr = []; // 组件dom数组
13 |
14 | }
15 | _initData() {
16 |
17 | // 如果color颜色数组长度小于数据长度,则需要填补
18 | if (this.colors.length < this.data.length) {
19 | for (let i = this.colors.length; i < this.data.length; i++) {
20 | this.colors.push('#0057ff')
21 | }
22 | }
23 |
24 | const sort = this.data
25 | const maxValue = sort[0].value;
26 |
27 | // 生成DOM结构
28 | const wrap = this.container;
29 | wrap.style.position = 'relative';
30 | wrap.style.width = '100%';
31 | wrap.style.height = '100%';
32 |
33 | // 生成title
34 | const title = document.createElement('h3');
35 | title.innerText = this.title;
36 | title.style.position = 'absolute';
37 | title.style.top = '0';
38 | title.style.margin = '0';
39 | wrap.appendChild(title);
40 | // const baseLine = document.createElement('div');
41 | // baseLine.className = 'base-line';
42 | // baseLine.style.position = 'absolute';
43 | // baseLine.style.width = '4px';
44 | // baseLine.style.height = '100%';
45 | // baseLine.style.backgroundColor = '#333';
46 | // baseLine.style.left = this.labelWidth + 'px';
47 | // baseLine.style.top = '0';
48 | // wrap.appendChild(baseLine);
49 |
50 | // 循环生成DOM
51 | const flag = document.createDocumentFragment();
52 | sort.slice(0, this.topNum).forEach((item, index) => {
53 | // 每行容器
54 | const rowEl = document.createElement('div');
55 | rowEl.className = 'row';
56 | rowEl.style.position = 'absolute';
57 | rowEl.style.top = `${index * (this.barHeight + this.spacing) + 40 }px`;
58 | rowEl.style.transition = 'top 1s';
59 |
60 | // 标签DOM
61 | const label = document.createElement('span');
62 | label.className = 'item-name';
63 | label.innerText = item.name;
64 | label.style.width = this.labelWidth + 4 + 'px';
65 | label.style.flexGrow = 0;
66 | rowEl.appendChild(label);
67 |
68 | // 数值DOM
69 | const valueEl = document.createElement('span');
70 | valueEl.className = "item-val";
71 | valueEl.innerText = item.value;
72 | valueEl.style.position = 'absolute';
73 | valueEl.style.display = 'inline';
74 | valueEl.style.padding = '0 10px';
75 |
76 | // 数值条DOM
77 | const bar = document.createElement('div');
78 | bar.className = 'item-bar';
79 | bar.style.height = this.barHeight + 'px';
80 | bar.style.lineHeight = this.barHeight + 'px';
81 | bar.style.width = `${item.value / maxValue * this.maxWidth}px`;
82 | bar.style.position = 'relative';
83 | bar.style.transition = 'width 1s';
84 | bar.appendChild(valueEl);
85 |
86 | rowEl.appendChild(bar);
87 | flag.appendChild(rowEl);
88 | // 用于更新数据时dom操作
89 | this.domArr.push({
90 | data: item,
91 | row: rowEl,
92 | label: label,
93 | value: valueEl,
94 | bar: bar
95 | });
96 | });
97 | wrap.appendChild(flag)
98 |
99 | // 判断最小值的文字宽度与背景色div宽度
100 | Array.prototype.slice.call(document.getElementsByClassName('item-val')).forEach((item, index) => {
101 | if (item.clientWidth > item.parentNode.clientWidth) {
102 | item.parentNode.style.backgroundColor = 'unset';
103 | item.style.left = '0';
104 | item.style.right = 'unset';
105 | } else {
106 | item.parentNode.style.backgroundColor = this.colors[index];
107 | item.style.right = '0';
108 | item.style.left = 'unset';
109 | }
110 | })
111 | }
112 |
113 | // 更新数据
114 | update(data) {
115 | // 判断是否是第一次更新数据
116 |
117 | if (!data instanceof Array) {
118 | console.error('options.data is not a Array!');
119 | return false
120 | }
121 | const sort = data.slice(0, this.topNum).sort((a, b) => b.value - a.value);
122 |
123 | if (!this.data) {
124 | this.data = sort;
125 | this._initData();
126 | return false
127 | }
128 | const maxValue = sort[0].value;
129 |
130 | // 对比dom更新值
131 | sort.forEach((newItem, index) => {
132 | this.domArr.some((item, domIndex) => {
133 | if (newItem.name === item.data.name) {
134 | item.bar.style.width = `${newItem.value / maxValue * this.maxWidth}px`;
135 | item.value.innerText = newItem.value;
136 | item.row.style.top = `${index * 40 + 50}px`;
137 | if (item.value.clientWidth > newItem.value / maxValue * this.maxWidth) {
138 | console.log(item.value.clientWidth, newItem.value / maxValue * this.maxWidth)
139 | item.bar.style.backgroundColor = 'unset';
140 | item.value.style.left = '0';
141 | item.value.style.right = 'unset';
142 | } else {
143 | item.bar.style.backgroundColor = this.colors[domIndex];
144 | item.value.style.right = '0';
145 | item.value.style.left = 'unset';
146 | }
147 | return true
148 | }
149 | })
150 | })
151 | }
152 | }
--------------------------------------------------------------------------------
/09-Skills/02-debounce/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 函数去抖(防抖)
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/09-Skills/02-debounce/index.js:
--------------------------------------------------------------------------------
1 | window.onload = function () {
2 | // 简单版本
3 | // fn 为需要防抖的函数, wait 为等待时间
4 | const debounce = (fn, wait = 50) => {
5 | // 创建一个定时器
6 | let timer = 0
7 | return function (...args) {
8 | // 如果 timer 没到时间,用户再点击就不断清除定时器
9 | // 然后 生成一个新定时器,并不会执行函数
10 | if (timer) clearTimeout(timer)
11 | timer = setTimeout(() => {
12 | fn.apply(this, args)
13 | }, wait)
14 | }
15 | }
16 | // 缺点是,第一次调用,也要等待。
17 |
18 | // 带有立即执行选项版
19 | // immediate 是否立即调用函数
20 | function decounce2(fn, wait = 50, immediate = true) {
21 | let timer, context, args
22 |
23 | // 延迟执行函数
24 | const later = () => setTimeout(() => {
25 | timer = null
26 |
27 | // 延迟执行的情况下,函数会在延迟函数中执行
28 | // 使用到之前缓存的参数和上下文
29 | if (!immediate) {
30 | fn.apply(context, args)
31 | context = args = null
32 | }
33 | }, wait)
34 |
35 | return function (...args) {
36 | if (!timer) {
37 |
38 | // 如果没有延迟时间定时器,就创建一个
39 | timer = later()
40 | if (immediate) {
41 | fn.apply(this, params)
42 | } else {
43 | context = this
44 | args = params
45 | }
46 | } else {
47 | // 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个
48 | // 这样做延迟函数会重新计时
49 | clearTimeout(timer)
50 | timer = later()
51 | }
52 | }
53 | }
54 | // 获取当前时间戳
55 | function now() {
56 | // '+'将 new Date() 由 'object' 转换为 'number'
57 | return +new Date()
58 | }
59 | }
--------------------------------------------------------------------------------
/09-Skills/03-traffic light/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-4-24
5 | **/
6 |
7 | // 红路灯问题
8 |
9 | // Details
10 |
11 | // You're writing code to control your town's traffic lights. You need a function to handle each change from green, to yellow, to red, and then to green again.
12 | // Complete the function that takes a string as an argument representing the current state of the light and returns a string representing the state the light should change to.
13 | // For example, update_light('green') should return 'yellow'.
14 |
15 | // 解决方法
16 |
17 | // 普通
18 |
19 | // funciton updateLight(current) {
20 | // switch(current) {
21 | // case 'green':
22 | // return 'yellow';
23 | // case 'yellow':
24 | // return 'red';
25 | // case 'red':
26 | // return 'green';
27 | // default:
28 | // throw 'Error: wrong input';
29 | // break;
30 | // }
31 | // }
32 |
33 |
34 | // 很聪明的方式
35 |
36 | // const updataLight = current => ({
37 | // green: 'yellow',
38 | // yellow: 'red',
39 | // red: 'yellow'
40 | // })[current]
41 |
42 | // 最优解
43 |
44 | // function updateLight(current) {
45 | // return current === 'yellow' ? 'red' : current === 'green'?'yellow':'green';
46 | // }
47 |
--------------------------------------------------------------------------------
/09-Skills/04-throttle/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-8-13
5 | **/
6 |
7 | // 节流
8 |
9 | function throttle(fn, time) {
10 | let timer = null
11 | return function name() {
12 | let context = this,
13 | args = arguments
14 | if (!timer) {
15 | timer = setTimeout(() => {
16 | fn.apply(context, args)
17 | timer = null
18 | }, time);
19 | }
20 | }
21 | }
22 |
23 | function throttle1(fn, delay) {
24 | let canUse = true
25 | return function () {
26 | if (canUse) {
27 | fn.apply(this, arguments)
28 | canUse = false
29 | setTimeout(() => {
30 | canUse = true
31 | }, delay);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/09-Skills/05-AccessNestObject/README.md:
--------------------------------------------------------------------------------
1 | # Access Nested Object
2 |
3 | > 访问嵌套对象
4 |
5 | - [Get Nested Object 获取嵌套对象](#get-nested-object)
6 | - [更新嵌套对象](#update-nested-object)
7 |
8 | ## Get Nested Object
9 |
10 | javascript is wired, one of those things is the error when I try to access a nested object:
11 |
12 | `cannot read property 'xxx' of undefined`
13 |
14 | ```js
15 | const nestObject = {
16 | id: "1",
17 | name: "nestObject",
18 | options: [
19 | {
20 | item1: "item1",
21 | childItem: {
22 | name: "childItem",
23 | },
24 | },
25 | {
26 | item2: "item2",
27 | },
28 | ],
29 | child1: {
30 | name: "child1",
31 | child2: {
32 | name: "child2",
33 | // child3: {
34 | // name: "child3",
35 | // },
36 | },
37 | },
38 | }
39 | ```
40 |
41 | to access the name of child1, the most usual way
42 |
43 | ```js
44 | let child1Name = nestObject.child1.name
45 |
46 | child1Name = nestObject && nestObject.child1 && nestObject.child1.name
47 | ```
48 |
49 | and if you want access the name of child2
50 |
51 | ```js
52 | let child2Name
53 |
54 | child2Name =
55 | nestObject && nestObject.child1 && nestObject.child1.child2
56 | ? nestObject.child1.child2.name
57 | : ""
58 |
59 | // or you can pick this trick
60 |
61 | child2Name = (((nestObject || {}).child1 || {}).child2 || {}).name
62 |
63 | // you cannot access nested array with this trick
64 | ```
65 |
66 | the code look really messy, and if the data nested 5 or more levels deep, the code will more worse.
67 |
68 | ### Reduce
69 |
70 | Reduce is a very powerful method and it can be used to access nested objects
71 |
72 | pass the Access path as Array
73 |
74 | ```js
75 | const getNestObject = (nestObject, pathArr) => {
76 | return pathArr.reduce((obj, key) => {
77 | return obj && obj[key] !== undefined ? obj[key] : undefined
78 | }, nestObject)
79 | }
80 | ```
81 |
82 | or pass the Access path as String
83 |
84 | ```js
85 | const getNestObject2 = (nestObject, pathString) => {
86 | let pathArr = Array.isArray(pathString) ? pathString : pathString.split(".")
87 | return pathArr.reduce((obj, key) => {
88 | return obj && obj[key] !== undefined ? obj[key] : undefined
89 | }, nestObject)
90 | }
91 |
92 | child2Name = getNestObject(nestObject, ["child1", "child2", "name"])
93 |
94 | console.log(child2Name)
95 | ```
96 |
97 | ## Update Nested Object
98 |
99 | Set the `nestOject.chlid1.child2.child3` property
100 |
101 | ```js
102 | const nestObject = {
103 | id: "1",
104 | name: "nestObject",
105 | options: [
106 | {
107 | item1: "item1",
108 | },
109 | {
110 | item2: "item2",
111 | },
112 | ],
113 | child1: {
114 | name: "child1",
115 | child2: {
116 | name: "child2",
117 | // child3: {
118 | // name: "child3",
119 | // },
120 | },
121 | },
122 | }
123 | ```
124 |
125 | ```js
126 | const setNestObject = (nestObject, pathArr, val) => {
127 | if (!pathArr || pathArr.length === 0) {
128 | return
129 | }
130 |
131 | if (pathArr.length === 1) {
132 | nestObject[pathArr[0]] = val
133 | return true
134 | }
135 |
136 | if (
137 | !nestObject.hasOwnProperty(pathArr[0]) ||
138 | typeof nestObject[pathArr[0]] !== "object"
139 | ) {
140 | nestObject[pathArr[0]] = typeof pathArr[1] === "number" ? [] : {}
141 | }
142 | return setNestObject(nestObject[pathArr[0]], pathArr.slice(1), val)
143 | }
144 | ```
145 |
--------------------------------------------------------------------------------
/09-Skills/05-AccessNestObject/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: tianre96@gmail.com
4 | * day: 2020-04-29
5 | **/
6 |
7 | const nestObject = {
8 | id: "1",
9 | name: "nestObject",
10 | options: [
11 | {
12 | item1: "item1",
13 | },
14 | {
15 | item2: "item2",
16 | },
17 | ],
18 | child1: {
19 | name: "child1",
20 | child2: {
21 | name: "child2",
22 | // child3: {
23 | // name: "child3",
24 | // },
25 | },
26 | },
27 | }
28 |
29 | let child1Name = nestObject.child1.name
30 |
31 | child1Name = nestObject && nestObject.child1 && nestObject.child1.name
32 |
33 | let child2Name
34 |
35 | child2Name =
36 | nestObject && nestObject.child1 && nestObject.child1.child2
37 | ? nestObject.child1.child2.name
38 | : ""
39 |
40 | child2Name = (((nestObject || {}).child1 || {}).child2 || {}).name
41 |
42 | // pass the Access path as array
43 | const getNestObject = (nestObject, pathArr) => {
44 | return pathArr.reduce((obj, key) => {
45 | return obj && obj[key] !== undefined ? obj[key] : undefined
46 | }, nestObject)
47 | }
48 |
49 | child2Name = getNestObject(nestObject, ["child1", "child2", "name"])
50 |
51 | console.log(child2Name)
52 |
53 | // ---- update nest object
54 |
55 | /**
56 | * @param path: array
57 | **/
58 |
59 | const setNestObject = (nestObject, pathArr, val) => {
60 | if (!pathArr || pathArr.length === 0) {
61 | return
62 | }
63 |
64 | // the end
65 | if (pathArr.length === 1) {
66 | nestObject[pathArr[0]] = val
67 | return true
68 | }
69 |
70 | // if the property is a Object
71 | // the property doesn't exists or not a Object
72 | if (
73 | !nestObject.hasOwnProperty(pathArr[0]) ||
74 | typeof nestObject[pathArr[0]] !== "object"
75 | ) {
76 | nestObject[pathArr[0]] = typeof pathArr[1] === "number" ? [] : {}
77 | }
78 | return setNestObject(nestObject[pathArr[0]], pathArr.slice(1), val)
79 | }
80 |
--------------------------------------------------------------------------------
/09-Skills/README.md:
--------------------------------------------------------------------------------
1 | # Skills 一些技巧
2 |
3 | > 关于 javascript 日常运用中需要注意的方面及使用技巧
4 |
5 | ## 目录
6 |
7 | 1. [数组随机排序](https://github.com/stephentian/daily-js/tree/master/09-Skills/01-shuffle)
8 | 2. [debounce 防抖]()
9 | 3. [红绿灯问题]()
10 | 4. [throttle 截流]()
11 | 5. [accessing nest object 访问嵌套对象]()
12 |
--------------------------------------------------------------------------------
/10-Design-patterns/01-Factory/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-5
5 | **/
6 |
7 | // 工厂模式
8 |
9 | // 一个简单的例子
10 |
11 | class Man {
12 | constructor (name) {
13 | this.name = name
14 | }
15 | alertName() {
16 | console.log(this.name)
17 | }
18 | }
19 |
20 | class Factory {
21 | static create(name) {
22 | return new Man(name)
23 | }
24 | }
25 |
26 | Factory.create('stephen').alertName()
27 |
28 | // 工厂模式不仅仅是用了 new 实例
29 | // 假设有一份很复杂的代码需要用户调用, 用户不关心代码, 只需要一个接口去调用, 传递需要的参数即可
30 | // 这个构建过程就是工厂
31 |
--------------------------------------------------------------------------------
/10-Design-patterns/02-Singleton/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-5
5 | **/
6 |
7 | // 单例模式
8 |
9 | class Singleton {
10 | constructor () { }
11 | }
12 |
13 | Singleton.getInstance = (function () {
14 | let instance
15 | return function () {
16 | if (!instance) {
17 | instance = new Singleton()
18 | }
19 | return instance
20 | }
21 | })()
22 |
23 | let s1 = Singleton.getInstance()
24 |
25 | let s2 = Singleton.getInstance()
26 |
27 | console.log(s1 === s2)
28 |
--------------------------------------------------------------------------------
/11-Event Loop/01-stack/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | stack
9 |
10 |
11 |
12 |
13 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/11-Event Loop/01-stack/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-6
5 | **/
6 |
7 | // function foo() {
8 | // console.log('1')
9 | // }
10 | // function bar() {
11 | // console.log('2');
12 | // foo()
13 | // }
14 | // bar()
15 |
16 |
17 | // setImmediate(() => {
18 | // console.log('1')
19 | // })
20 |
21 | // process.nextTick(() => {
22 | // console.log('2')
23 | // })
24 |
25 | // setImmediate(() => {
26 | // console.log('3')
27 | // })
28 |
29 | // new Promise((resolve, reject) => {
30 | // resolve(true)
31 | // }).then(() => {
32 | // console.log('4')
33 | // })
34 |
35 | // setTimeout(() => {
36 | // console.log('5')
37 | // }, 0)
38 |
39 | // setTimeout(() => {
40 | // console.log('6')
41 | // }, 0)
42 |
43 | // process.nextTick(() => {
44 | // console.log('7')
45 | // })
46 |
47 |
48 |
49 | console.log('1');
50 |
51 | setTimeout(function () {
52 | console.log('2');
53 | // process.nextTick(function () {
54 | // console.log('3');
55 | // })
56 | new Promise(function (resolve) {
57 | console.log('4');
58 | resolve();
59 | }).then(function () {
60 | console.log('5')
61 | })
62 | })
63 |
64 | new Promise(function (resolve) {
65 | console.log('7');
66 | resolve();
67 | }).then(function () {
68 | console.log('8')
69 | })
70 |
71 | process.nextTick(function () {
72 | console.log('6');
73 | })
74 |
75 |
76 | setTimeout(function () {
77 | console.log('9');
78 | // process.nextTick(function () {
79 | // console.log('10');
80 | // })
81 | new Promise(function (resolve) {
82 | console.log('11');
83 | resolve();
84 | }).then(function () {
85 | console.log('12')
86 | })
87 | })
88 |
89 | // 1, 7, 6, 8
90 | // 2, 4, 9, 11, 3, 10, 5, 12
91 |
--------------------------------------------------------------------------------
/11-Event Loop/README.md:
--------------------------------------------------------------------------------
1 | ## Event Loop
2 | > 事件循环
3 |
4 | ****
5 |
6 | ### 进程和线程
7 | > process && thread
8 |
9 | 官方一点的说法:进程是 CPU 资源分配的最小单位, 线程是 CPU 调度的最小单位
10 | 通俗一点:进程是部门领导, 管着众多线程员工.
11 |
12 | ### 单线程好处
13 |
14 | 1. 避免依赖没有加载完成,
15 | 如果工作线是: 产品经理 -> UI -> 前端 + 后端 -> 测试 -> 产品经理,
16 | 测试工程师测试产品的时候, 不知道前后端还没完成或者完善好, 可能就给产品经理报了一堆的 bug, 那产品经理对你的印象就不好了.
17 |
18 | 2. 节约了上下文切换时间
19 |
20 | 3. 没有锁的问题
21 |
22 |
23 | ### 执行栈
24 |
25 | 执行栈, 在其他编程语言里也被叫做**调用栈**, 是一种存储函数调用环境的栈结构, 后进先出(LIFO)
26 |
27 | 当开始执行 js 代码时, 首先会执行一个包含全局环境的 main 函数, 然后执行我们的代码.
28 |
29 | **爆栈**: 使用递归时, 栈可存放的函数是有限制的, 一旦存放过多没有释放, 就会出现爆栈问题.
30 |
31 | #### Event Loop
32 |
33 | 1. js 自上而下解析, 将其中的同步任务按照执行顺序排列到执行栈中
34 | 2. 程序调用外部 api , 如 ajax, setTimeout 等, 会将此类异步任务排列到 Event Table 中, 继续执行栈中的任务
35 | 3. 当异步函数指定事件完成后, 将回调函数放到 事件队列(Event Queue) 中
36 | 4. 主线程将执行栈中的同步任务执行完, 检查 事件队列 中是否有任务,
37 | - 如果有, 将第一个事件对应的回调函数推送到执行栈中执行, 如果执行过程中遇到异步任务, 则继续将这个异步任务排列到事件队列中.
38 | - 如果没有, 执行栈清空, 执行结束.
39 | 5. 主线程每次执行栈清空, 就去事件队列检查是否有任务, 如果有, 就取出一个推送到执行栈中执行, 这个过程一直循环重复, 故被称为 Event Loop
40 |
41 |
42 | ### 宏任务与微任务
43 |
44 | 在上面第二步中, 异步任务有分成 宏任务 和 微任务(区别是执行顺序不同)
45 |
46 | 宏任务: script 全部代码, setTimeout, setInterval, serImmediate, I/O(磁盘读写或网络通信), UI 渲染
47 | 微任务: process.nextTick, Promise.then(Promise 得到回调后的事件), Object.observe(废弃), MutationObserver
48 |
49 | 在 ES6 中, microtask 成为 jobs,
50 |
51 | 在一次事件循环中, 首先 JS 会执行一个宏任务(全部 JS 代码), 执行完成后判断微任务队列中是否有微任务,
52 | - 如果有, 将它们依次全部执行完, 再开始下一个宏任务;
53 | - 如果没有, 跳到执行下一个宏任务
54 |
55 | **优先级**
56 |
57 | process.nextTick 高于 Promise
58 | setTimeout 高于 setImmediate
59 |
--------------------------------------------------------------------------------
/12-MVVM/01-defineProperty/compile.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-14
5 | **/
6 |
7 | // Compile
8 |
9 | // Compile 主要做解析模板指令, 将模板的变量替换成数据, 然后初始化渲染页面视图
10 | // 将每个指令对应的节点绑定更新函数, 添加监听数据的订阅者, 一旦数据变动, 收到通知, 更新视图
11 |
12 | // 因为遍历解析过程中有多次操作 DOM 节点, 为提高性能和效率, 会先将跟节点 el 转换成文档碎片 fragment 进行解析编译操作
13 | // 解析完成, 再将 fragment 添加回原来的真实的 DOM 节点中
14 |
15 | function Compile(el) {
16 | this.$el = this.isElementNode(el) ? el : document.querySelector(el)
17 | if (this.$el) {
18 | this.$fragment = this.node2Fragment(this.$el)
19 | this.init()
20 | this.$el.appendChild(this.$fragment)
21 | }
22 | }
23 |
24 | Compile.prototype = {
25 | init: function () {
26 | this.compileElement(this.$fragment)
27 | },
28 | node2Fragment: function (el) {
29 | var fragment = document.createDocumentFragment(),
30 | child;
31 | // 将原生节点拷贝到 fragment
32 | while (child = el.firstChild) {
33 | fragment.appendChild(child)
34 | }
35 | return fragment
36 | },
37 | compileElement: function (el) {
38 | var childNodes = el.childNodes,
39 | me = this;
40 | [].slice.call(childNodes).forEach(function (node) {
41 | const text = node.textContent
42 | const reg = /\{\{.*)\}\}/
43 | // 按元素节点方式编译
44 | if (me.isElementNode(node)) {
45 | me.compileElement(node)
46 | } else if (me.isTextNode(node) && reg.test(text)) {
47 | me.compileText(node, RegExp.$1)
48 | }
49 | // 遍历编译子节点
50 | if (node.childNodes && node.childNodes.length) {
51 | me.compileElement(node)
52 | }
53 | })
54 | },
55 | compile: function (node) {
56 |
57 | },
58 |
59 | }
60 |
61 | // 指令处理集合
62 | const compileUtil = {
63 | text: function (node, vm, exp) {
64 | this.bind(node, vm, exp, 'text')
65 | },
66 | bind: function (node, vm, exp, dir) {
67 | const updaterFn = updater[dir + 'Updater']
68 | // 第一次初始化视图
69 | updaterFn && updaterFn(node, vm[exp])
70 | //
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/12-MVVM/01-defineProperty/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Vue 方式的 MVVM
9 |
10 |
11 |
12 |
13 |
14 |
{{ word }}
15 |
16 |
17 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/12-MVVM/01-defineProperty/mvvm.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-03-16
5 | **/
6 |
7 | function MVVM(options) {
8 | this.$options = options || {}
9 | const data = this._data = this.$options.data
10 | const me = this
11 |
12 | // 数据代理
13 | // 实现 vm.xxx ---> vm._data.xxx
14 | Object.keys(data).forEach(function (key) {
15 | me._proxyData(key)
16 | })
17 |
18 | this._initComputed()
19 | observe(data, this)
20 | this.$compile = new Compile(options.el || document.body, this)
21 | }
22 |
23 | MVVM.prototype = {
24 | $watch: function (key, cb, options) {
25 | new Watcher(this, key, cb)
26 | },
27 | _proxyData: function (key, setter, getter) {
28 | const me = this
29 | setter = setter || Object.defineProperty(me, key, {
30 | configurable: false,
31 | enumerable: true,
32 | get: function proxyGetter() {
33 | return me._data[key]
34 | },
35 | set: function proxySetter(newVal) {
36 | me._data[key] = newVal
37 | }
38 | })
39 | },
40 | _initComputed: function () {
41 | const me = this
42 | const computed = this.$options.computed
43 | if (typeof computed === 'object') {
44 | Object.defineProperty(me, key, {
45 | get: typeof computed[key] === 'function' ? computed[key] : computed[key].get,
46 | set: function () { }
47 | })
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/12-MVVM/01-defineProperty/observer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-12
5 | **/
6 |
7 | // Observer 观察员
8 |
9 |
10 | // 利用 Object.defineProperty 来监听属性变动
11 | // 将需要 observe 的数据进行递归遍历, 包括子属性对象的属性, 都加上 setter 和 getter
12 | // 这样的话, 给对象的某个值赋值就会触发 setter, 达到监听数据变化的目的
13 |
14 | function observe(obj) {
15 | if (!obj || typeof obj !== 'object') {
16 | return
17 | }
18 | Object.keys(obj).forEach((key) => {
19 | defineReactive(obj, key, obj[key])
20 | })
21 | }
22 |
23 | // function defineReactive(obj, key, val) {
24 | // observe(val)
25 | // Object.defineProperty(obj, key, {
26 | // enumerable: true,
27 | // configurable: false, // 不能再 define, 再使用 defineProperty 会报错
28 | // get: function () {
29 | // return val
30 | // },
31 | // set: function (newVal) {
32 | // console.log('value changed', val, '--->', newVal)
33 | // val = newVal
34 | // }
35 | // })
36 | // }
37 |
38 | // 测试
39 |
40 | // let data = { name: 'stephentian' }
41 | // observe(data)
42 | // data.name = 'tian'
43 |
44 |
45 | // 可以监听到每个数据变化了, 接下来是通知订阅者, 实现一个消息订阅器
46 | // 很简单, 维护一个数组, 用来收集订阅者, 数据变动触发 notify, 在调用订阅者的 update 方法
47 |
48 | function Dep() {
49 | this.subs = []
50 | }
51 |
52 | Dep.prototype = {
53 | addSub: function (sub) {
54 | this.subs.push(sub)
55 | },
56 | depend: function () {
57 | Dep.target.addDep(this)
58 | },
59 | removeSub: function (sub) {
60 | let index = this.subs.indexOf(sub)
61 | if (index != -1) {
62 | this.subs.splice(index, 1)
63 | }
64 | },
65 | notify: function () {
66 | this.subs.forEach(sub => {
67 | // 调用订阅者 update 方法
68 | sub.update()
69 | })
70 | }
71 | }
72 |
73 | // 代码改善后:
74 |
75 | // function defineReactive(data, key, val) {
76 | // let dep = new Dep()
77 | // observe(val)
78 | // Object.defineProperty(data, key, {
79 | // enumerable: true,
80 | // configurable: false,
81 | // get: function () {
82 | // return val
83 | // },
84 | // set: function (newVal) {
85 | // if (val === newVal) return
86 | // console.log('value changed', val, '--->', newVal)
87 | // val = newVal
88 | // dep.notify() // 通知所有订阅者
89 | // }
90 | // })
91 | // }
92 |
93 |
94 | // 如何分辨谁是 订阅者, 怎么往订阅器添加订阅者呢?
95 | // 整理上面的思路, 可以明确订阅者应该是 Watcher,
96 | // 而且 let dep = new Dep()
97 | // 是在 defineReactive 方法内部定义的, 所以想通过 dep 添加订阅者, 就必须在闭包内操作
98 | // 所以我们在 getter 里操作
99 |
100 | function defineReactive(data, key, val) {
101 | let dep = new Dep()
102 | observe(val)
103 | Object.defineProperty(data, key, {
104 | enumerable: true,
105 | configurable: false,
106 | get: function () {
107 | // 由于需要在闭包内添加 watcher, 所以通过 Dep 定义一个全局 target 属性, 暂存 watcher, 添加完移除
108 | Dep.target && dep.addDep(Dep.target)
109 | return val
110 | },
111 | set: function (newVal) {
112 | if (val === newVal) return
113 | console.log('value changed', val, '--->', newVal)
114 | val = newVal
115 | dep.notify() // 通知所有订阅者
116 | }
117 | })
118 | }
119 |
120 | Dep.target = null
121 |
--------------------------------------------------------------------------------
/12-MVVM/01-defineProperty/watcher.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-13
5 | **/
6 |
7 | // Watcher
8 |
9 | // Watcher 订阅者作为 Observer 和 Compile 之间的通信桥梁
10 | // 主要作用:
11 | // 1. 在自身实例化时往属性订阅器(dep) 里添加自己
12 | // 2. 自身有个 update() 方法
13 | // 3. 待属性变动 dep.notice() 通知时, 能调用自身的 update() 方法, 并触发 Compile 中绑定的回调
14 |
15 | function Watcher(vm, exp, cb) {
16 | this.cb = cb
17 | this.vm = vm
18 | this.exp = exp
19 | this.depIds = {}
20 | // 此处为了触发属性的 getter, 从而在 dep 添加自己, 结合 Observer 更易理解
21 | this.value = this.get()
22 | }
23 |
24 | Watcher.prototype = {
25 | update: function () {
26 | this.run
27 | },
28 | run: function () {
29 | let value = this.value
30 | const oldVal = this.value
31 | if (value !== oldVal) {
32 | this.value = value
33 | this.cb.call(this.vm, value, oldVal) // 执行 Compile 中绑定的回调, 更新视图
34 | }
35 | },
36 | addDep: function (dep) {
37 | // 1. 每次调用 run() 的时候会触发相应的属性 getter
38 | // getter 里面会触发 dep.depend(), 继而触发 addDep
39 | // 2. 假如相应的属性的 dep.id 已经在当前 watcher 的 depIds 里, 说明不是一个新属性, 仅仅是改变了其值而已
40 | // 则不需要将当前 watcher 添加到该属性的 dep 里
41 | // 3. 假如相应的属性是新属性, 则将当前 watcher 添加到新属性的 dep 里
42 | // 如通过 vm.child = { name: 'a' } 改变了 child.name 的值, child.name 就是一个新属性
43 | // 则需要将当前 watcher(child.name) 加入到新的 child.name 的 dep 里
44 | // 因为此时 child.name 是一个新值, 之前到 setter, dep 都已经失效
45 | // 如果不把 watcher 加入到新的 child.name 的 dep 中
46 | // 通过 child.name = xxx 赋值的时候, 对应的 watcher 就收不到通知, 等于失效了
47 | // 4. 每个子属性的 watcher 在添加子属性的 dep 的同时, 也会添加到父属性的 dep
48 | // 监听子属性的同时监听父属性的变更, 这样, 父属性改变是, 子属性的 watcher 也能收到通知, 进行 update()
49 | // 这一步是在 this.get() --> this.getVmVal() 里完成的, forEach 是会从父级开始取值, 间接调用它的 getter
50 | // 触发了 addDep(), 在整个 forEach 中, 当前 watcher 都会加入到每个父级过程属性的 dep
51 | // 例如:
52 | // 当前 watcher 的是 'child.chile.name', 那么 child, child.child, child.child.name 三个属性的 dep 都会加入当前 watcher
53 | if (!this.depIds.hasOwnProperty(dep.id)) {
54 | dep.addDep(this)
55 | this.depIds[dep.id] = dep
56 | }
57 | },
58 | get: function () {
59 | // 将当前的订阅者指向自己
60 | Dep.target = this
61 | // 触发 getter, 添加自己到属性订阅器
62 | let value = this.vm[exp]
63 | // 添加完毕, 重置
64 | Dep.target = null
65 | return value
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/13-Prototype/JQuery/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-19
5 | **/
6 |
7 | // jQuery
8 |
9 | var JQuery = function (selector) {
10 | return new JQuery.fn.init(selector)
11 | }
12 |
13 | // 定义构造函数
14 |
15 | var init = JQuery.fn.init = function (selector) {
16 | var slice = Array.prototype.slice
17 | var dom = slice.call(document.querySelectorAll(selector))
18 |
19 | var i, len = dom ? dom.length : 0
20 | for (i = 0; i < len; i++) {
21 | this[i] = dom[i]
22 | }
23 | this.length = len
24 | this.selector = selector || ''
25 | }
26 |
27 | // 初始化 JQuery.fn
28 | JQuery.fn = JQuery.prototype = {
29 | constructor: JQuery,
30 | // 其他函数
31 | css: function (key, value) { },
32 | html: function (value) { }
33 | }
34 |
35 | // 定义原型
36 | init.prototype = JQuery.fn
37 |
--------------------------------------------------------------------------------
/13-Prototype/README.md:
--------------------------------------------------------------------------------
1 | # 原型的使用
2 | > prototype
3 |
4 | ### 目录
5 |
6 | - jquery
7 | - zepto
8 |
9 |
10 | ### jQuery
11 |
12 | jQuery 为入口函数, 接收一个 selector; 返回一个 jQuery.fn.init(selector) 构造函数.
13 |
14 | 定义 init 为 jQuery.fn.init, 并给 jQuery.fn.init 为一个接收 selector 为参数的函数.
15 |
16 | 函数中根据 selector 获取 dom, 并遍历 dom 属性, 赋值为函数的属性.
17 |
18 | jQuery.fn 赋值给 jQuery.prototype, jQuery.prototype 定义为一个对象,
19 |
20 | 构造器为 jQuery, 还有 jQuery 的相关功能方法.
21 |
22 | 最后定义原型, init.prototype = jQuery.fn
23 |
24 | **jQuery.protorype 为什么要赋值给 jQuery.fn 和 构造函数的 init 的原型呢?**
25 |
26 | 1. 存在即合理
27 |
28 | 2. 为了扩展插件
29 |
30 | 一个简单的插件例子
31 |
32 | ```
33 | $.fn.getNodeName = function () {
34 | return this[0].nodeName
35 | }
36 | ```
37 |
38 | 不通过直接 jQuery.prototype 暴露出来, 虽然最终的目的是放入原型里.
39 |
40 | **好处:**
41 |
42 | - 只用 $ 会暴露在 window 全局变量
43 | - 将插件扩展统一到 $.fn.xxx 这一个接口中, 方便使用(这符合软件设计模式)
44 | - 设计库, 只暴露一个变量就好了.
45 |
46 |
47 | ### zepto
48 |
49 | $ 为入口函数, 接收 selector 参数, 返回一个 zepto.init(selector) 函数.
50 |
51 | zepto.init 通过 selector 获取到 dom;
52 |
53 | 将 dom 和 selector 传给 zepto.Z;
54 |
55 | zepto.Z 里初始化构造函数, new Z(dom, selector)
56 |
57 | Z 为构造函数, 将 dom 的属性映射给自身.
58 |
59 | Z.prototype 被赋值给了 $.fn
60 |
61 | $.fn 为对象, 构造器为 zepto.Z, 里面存有 zepto 的相关方法.
62 |
63 | 最后定义原型, zepto.Z.prototype = Z.prototype = $.fn
64 |
65 | **为什么要把 zepto 原型方法赋值给 $.fn, 而不直接给 Z.prototype 赋值?**
66 |
67 |
68 | ### 插件机制
69 |
70 |
--------------------------------------------------------------------------------
/13-Prototype/Zepto/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | * email: stephentian@foxmail.com
4 | * day: 2019-3-19
5 | **/
6 |
7 | // zepto
8 |
9 | var zepto = {}
10 |
11 | zepto.init = function (selector) {
12 | // 源码比较复杂, 处理情况很多, 这里简化举例
13 | // 比如要解析命令等
14 | var dom = document.querySelectorAll(selector)
15 | return zepto.Z(dom, selector)
16 | }
17 |
18 | // 使用 zepto 时的 $
19 | // 先定义 $
20 |
21 | var $ = function (selector) {
22 | return zepto.init(selector)
23 | }
24 |
25 | // 构造函数
26 |
27 | function Z(dom, selector) {
28 | var i, len = dom ? dom.length : 0
29 | // 把 DOM 元素的属性赋值给自己
30 | for (i = 0; i < len; i++) {
31 | this[i] = dom[i]
32 | }
33 | this.length = len
34 | this.selector = selector || ''
35 | }
36 |
37 | zepto.Z = function (dom, selector) {
38 | return new Z(dom, selector)
39 | }
40 |
41 | $.fn = {
42 | constructor: zepto.Z,
43 | css: function (key, value) { },
44 | html: function (value) {
45 | return '这是一个模拟的html方法'
46 | }
47 | }
48 |
49 | zepto.Z.prototype = Z.prototype = $.fn
50 |
--------------------------------------------------------------------------------
/13-Prototype/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 测试 jQuery 和 zepto
8 |
9 |
10 |
11 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/14-DataStructure/Linked-list/README.md:
--------------------------------------------------------------------------------
1 | # 链表(linked-list)
2 |
3 | ## 介绍
4 |
5 | 链表是一组节点组成的集合,每个节点都使用一个对象的引用来指向它的后一个节点。指向另一节点的引用叫做链。
6 |
7 | ```
8 | |header|next| ---> |data1|next| ---> |data2|next| ---> |data3|next| ---> null
9 | ```
10 |
11 | ## 设计链表
12 |
13 | 链表包含两个类, 一个是 Node 类, 来表示节点的数据; 一个是 LinkedList 类, 提供插入节点、删除节点等操作.
14 |
15 | ### Node 类
16 |
17 | ```
18 | function Node(ele) {
19 | this.ele = ele
20 | this.next = null
21 | }
22 | ```
23 |
24 | ### LinkedList 类
25 |
26 | ```
27 | function LList () {
28 | this.head = new Node('head') // 头节点
29 | this.find = find // 查找节点
30 | this.insert = insert // 插入节点
31 | this.remove = remove // 删除节点
32 | this.findPrev = findPrev // 查找前一个节点
33 | this.display = display // 显示节点
34 | }
35 |
36 | ```
37 |
38 | #### 查找节点
39 |
40 | ```
41 | function find (item) {
42 | var currentNode = this.head
43 | while(currentNode.value != item) {
44 | currentNode = currentNode.next
45 | }
46 | return currentNode
47 | }
48 | ```
49 |
50 | #### 插入节点
51 |
52 | ```
53 | function insert(newEle, item) {
54 | var newNode = new Node(newEle)
55 | var currentNode = this.find(item)
56 | newNode.next = currentNode.next
57 | currentNode.next = newNode
58 | }
59 | ```
60 |
61 | ## 链表的一些操作
62 |
63 | #### 打印链表
64 |
65 | ```
66 | function showList (list) {
67 | let node = list.head
68 | while(node) {
69 | console.log(node.value)
70 | node = node.next
71 | }
72 | }
73 | ```
74 |
75 | #### 逆序打印链表
76 |
77 | ```
78 | function showReverseList(list) {
79 | let node = list.head,
80 | stack = []
81 | while(node) {
82 | stack.push(node.value)
83 | node = node.next
84 | }
85 | for(let len = stack.length - 1; len >=0; len --) {
86 | console.log(stack[len])
87 | }
88 | }
89 | ```
90 |
91 | #### 单向链表逆序
92 |
93 | ```
94 | function reverseList(list) {
95 | let reList = list
96 | let a = list.head
97 | b = null
98 | if (a === null) return
99 |
100 | while(a.next != null) {
101 | b = a.next
102 | a.next = b.next
103 | b.next = reList
104 | reList = b
105 | }
106 | return reList
107 | }
108 | ```
109 |
110 | #### 链表转化成数组
111 |
112 | ```
113 | function list2Array(list) {
114 | if(!list) {
115 | return []
116 | }
117 | var result = []
118 | var p = list.head
119 | while(p) {
120 | result.push(p.value)
121 | p = p.next
122 | }
123 | return result
124 | }
125 |
126 | // 递归
127 | function list2Array(list) {
128 | if (!list) return []
129 |
130 | var result = [list.value]
131 | var resultValues = list2Array(list.next)
132 | return result.concat(resultValues)
133 | }
134 | ```
135 |
136 | #### 数组转链表
137 |
138 | ```
139 | function array2List(arr) {
140 | if(!arr || arr.length === 0) {
141 | return null
142 | }
143 | var nodes = []
144 | for(var i = 0; i < arr.length; i ++) {
145 | var node = {}
146 | node.value = arr[i]
147 | node.next = null
148 | nodes.push(node)
149 | }
150 |
151 | for(var i = 0; i < nodes.length; i++) {
152 | nodes[i].next = nodes[i+1]
153 | }
154 | return nodes[0]
155 | }
156 |
157 | // 不占额外空间
158 | function array2List(arr) {
159 | if(!arr.length) return null
160 | var node,
161 | head = {value: arr[0], next: null}
162 | var pnode = head // pnode 用来保存前一个节点
163 |
164 | for(var i = 1; i < arr.length; i++) {
165 | node = {value: arr[i], next: null}
166 | pnode.next = node
167 | pnode = node
168 | }
169 |
170 | return head
171 | }
172 | ```
173 |
--------------------------------------------------------------------------------
/15-TypeScript/README.md:
--------------------------------------------------------------------------------
1 | # TypeScript
2 |
3 | - [TypeScript](#typescript)
4 | - [介绍](#介绍)
5 | - [引导](#引导)
6 | - [声明类型](#声明类型)
7 | - [基础类型](#基础类型)
8 | - [接口](#接口)
9 | - [类](#类)
10 | - [函数](#函数)
11 | - [函数类型](#函数类型)
12 | - [参数](#参数)
13 | - [`this`](#this)
14 | - [泛型](#泛型)
15 | - [装饰器](#装饰器)
16 | - [属性装饰器](#属性装饰器)
17 | - [类型声明](#类型声明)
18 |
19 | ## 介绍
20 |
21 | 官方:TypeScript 是 JavaScript 类型的超集,它可以编译成纯 JavaScript。
22 |
23 | 可以把 TypeScript 看作是 JavaScript + 一些扩展包。
24 |
25 | ## 引导
26 |
27 | 参考官方文档: [TypeScript 文档简介](https://www.tslang.cn/docs/home.html)
28 |
29 | - 声明类型
30 | - 基础类型
31 | - 接口
32 | - 类
33 | - 函数
34 | - 泛型
35 | - 装饰器
36 | - 类型声明
37 |
38 | ## 声明类型
39 |
40 | - 类型别名( `type sn = number | string;` )
41 | - 接口声明( `interface I { x: number[]; }` )
42 | - 类声明( `class C { }` )
43 | - 枚举声明( `enum E { A, B, C }` )
44 | - 指向某个类型的 `import` 声明
45 |
46 | ## 基础类型
47 |
48 | - number
49 | - string
50 | - boolean
51 | - `[]` / Array 数组
52 | - Tuple 元组
53 | - enum 枚举
54 | - any 未指定类型
55 | - 无类型 Void
56 | - 用于函数,表示没有返回值
57 | - 用于变量,表示它只能为 `undefined` 和 `null`
58 | - null
59 | - undefined
60 | - never 永不存在的值的类型
61 | - 用于函数,表示存在无法到达的终点
62 | - object
63 |
64 | 类型断言
65 |
66 | 好比其他语言的类型转换,告诉编译器,这是什么类型。
67 |
68 | 两种形式,第一种 “尖括号” 语法:
69 |
70 | ```ts
71 | let str:any = "this is a string"
72 | let strLength:number = (str).length
73 | ```
74 |
75 | 第二种,`as` 语法:
76 |
77 | ```ts
78 | let str:any = "this is a string"
79 | let strLength:number = (str as string).length
80 | ```
81 |
82 | 注意:如果在 TypeScript 使用 JSX 时,只有 `as` 语法断言时生效的。
83 |
84 | ## 接口
85 |
86 | 约束对象接口,不需要全部实现,只会检查必需属性。
87 |
88 | ```ts
89 | interface LabelValue {
90 | label: string
91 | }
92 | function printLabel(obj: LabelValue) {
93 | console.log(obj.label)
94 | }
95 | ```
96 |
97 | 可选属性(`?:`)
98 |
99 | ```ts
100 | interface LabelValue {
101 | name?: string
102 | label: string
103 | }
104 | ```
105 |
106 | 可读属性(`readonly`)
107 |
108 | ```ts
109 | interface Point {
110 | readonly x: number;
111 | readonly y: number;
112 | }
113 | ```
114 |
115 | ## 类
116 |
117 | `class` 和 es6 里的功能相同,不过 `ts` 里创建类还会创建一个类的实例类型。
118 |
119 | `extends` 继承
120 |
121 | 修饰符:
122 |
123 | - public
124 | - private
125 | - protected
126 | - readonly
127 |
128 | 存取器:`get`,`set`
129 |
130 | 静态属性:`static`
131 |
132 | 抽象类:`abstract`
133 |
134 | 因为创建类会生成一个类的实例类型,类可以用作 接口。
135 |
136 | ## 函数
137 |
138 | 主要定义 行为 的地方。
139 |
140 | ### 函数类型
141 |
142 | ```ts
143 | let myAdd: (x: number, y: number) => number =
144 | function(x: number, y: number): number { return x + y }
145 | ```
146 |
147 | 只要参数类型是匹配的,即有效,不需要确认参数名是否正确
148 |
149 | 推断类型(赋值语句的一边指定类型另一边没有类型的话,TypeScript 编译器会自动识别出类型)
150 |
151 | ```ts
152 | let myAdd = function(x: number, y: number): number { return x + y; }
153 | // 或者
154 | let myAdd: (baseValue: number, increment: number) => number =
155 | function(x, y) { return x + y; }
156 | ```
157 |
158 | ### 参数
159 |
160 | 可选参数和默认参数
161 |
162 | !可选参数必须跟在必须参数后面
163 |
164 | ```ts
165 | function(x: number, y?: number) {}
166 | function(x: number, y = 1) {}
167 | ```
168 |
169 | 剩余参数
170 |
171 | 剩余参数会被当做个数不限的可选参数。可以一个都没有,同样也可以有任意个。
172 |
173 | ```ts
174 | function(x: number, ...restOfNumber: number[]) {}
175 | ```
176 |
177 | ### `this`
178 |
179 | 箭头函数能保存函数创建时的 this值,而不是调用时的值。
180 |
181 | ```ts
182 | let deck = {
183 | createCardPicker: function() {
184 | () => {
185 | return { suits: this.suits }
186 | }
187 | }
188 | }
189 | ```
190 |
191 | 但是,TypeScript 会警告你犯了一个错误:`this.suits` 为 `any` 。
192 |
193 | 修改的方法是,提供一个显式的 this参数。
194 |
195 | ```ts
196 | interface Card {
197 | suit: string;
198 | }
199 | interface Deck {
200 | suits: string[];
201 | createCardPicker(this: Deck): () => Card;
202 | }
203 | let deck: Deck = {
204 | suits: ["a", "b", "c", "d"],
205 | createCardPicker: function(this: Deck) {
206 | () => {
207 | return { suits: this.suits }
208 | }
209 | }
210 | }
211 | ```
212 |
213 | ## 泛型
214 |
215 | 泛型 Generics。像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。
216 |
217 | 在定义函数、接口或者类的时候,不需要先指定具体的类型,增加复用性。
218 |
219 | 泛型变量
220 |
221 | ```ts
222 | function identity(arg: T): T {
223 | return arg;
224 | }
225 | ```
226 |
227 | 泛型接口
228 |
229 | ```ts
230 | interface GenericIdentityFn {
231 | (arg: T): T;
232 | }
233 |
234 | let myIdentity: GenericIdentityFn = function identity(arg: T): T {
235 | return arg;
236 | }
237 | ```
238 |
239 | 泛型方法
240 |
241 | ```ts
242 | function identity(arg: T): T {
243 | return arg;
244 | }
245 |
246 | let myIdentity: (arg: T) => T = identity;
247 | ```
248 |
249 | 泛型类
250 |
251 | 泛型类型放在类后面。
252 |
253 | ```ts
254 | class GenericsClass {
255 | value: T,
256 | add: (x: T, y: T) => T
257 | }
258 | ```
259 |
260 | 泛型约束
261 |
262 | 对比操作 `any` 类型,想要限制函数去处理 `.length` 属性的类型。 定义一个接口来描述约束条件。使用这个接口和 `extends` 关键字来实现约束
263 |
264 | ```ts
265 | interface Lengthwise {
266 | length: number;
267 | }
268 |
269 | function loggingIdentity(arg: T): T {}
270 | ```
271 |
272 | ! 传入符合约束类型的值,必须包含必须的属性
273 |
274 | ## 装饰器
275 |
276 | 装饰器 Decorators,为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。
277 |
278 | 它能够被附加到类声明,方法, 访问符,属性或参数上。
279 |
280 | 简而言之:用于扩展类、类的属性或者方法。
281 |
282 | ```ts
283 | // vue @Props
284 | export default class App extends Vue {
285 | @Props({ type: Boolean, required: true })
286 | msg!: Boolean
287 | }
288 | // 事件处理:@Emit
289 | // 变更监测:@Watch
290 | ```
291 |
292 | 类装饰器
293 |
294 | 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
295 |
296 | !类装饰器不能用在声明文件中( .d.ts),也不能用在任何外部上下文中(比如declare的类)。
297 |
298 | ```ts
299 | // 当 @sealed 被执行的时候,它将密封此类的构造函数和原型。
300 | function sealed(constructor: Function) {
301 | Object.seal(constructor);
302 | Object.seal(constructor.prototype);
303 | }
304 |
305 | @sealed
306 | class Greeter {
307 | greeting: string;
308 | constructor(message: string) {
309 | this.greeting = message;
310 | }
311 | greet() {
312 | return "Hello, " + this.greeting;
313 | }
314 | }
315 | ```
316 |
317 | 方法装饰器
318 |
319 | 被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。
320 |
321 | 3 个参数:
322 |
323 | 1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
324 | 2. 成员名字。
325 | 3. 成员的属性描述符。
326 |
327 | ### 属性装饰器
328 |
329 | 属性装饰器声明在一个属性声明之前(紧靠着属性声明)。
330 |
331 | 2个参数:
332 |
333 | 1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
334 | 2. 成员的名字。
335 |
336 | ## 类型声明
337 |
338 | 模块补充
339 |
340 | ```ts
341 | declare module "vue/types/vue" {
342 | interface Vue {
343 | $axios: AxiosInstance
344 | }
345 | }
346 | ```
347 |
--------------------------------------------------------------------------------
/16-Source Code/README.md:
--------------------------------------------------------------------------------
1 | # Source Code
2 |
3 | > 源码分析
4 |
5 | ## 相关地址
6 |
7 | `v8` 源码地址:
8 |
9 | `v8 js` 部分:
10 |
11 | 1.
12 | 2.
13 |
14 | ## 目录
15 |
16 | 1. splice
17 |
--------------------------------------------------------------------------------
/16-Source Code/splice.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author: stephentian
3 | */
4 |
5 | // 实现 Array.prototype.splice
6 |
7 | // ES5 规范
8 | // https://262.ecma-international.org/5.1/#sec-15.4.4.12
9 | // ES6 规范
10 | // https://262.ecma-international.org/6.0/#sec-array.prototype.splice
11 |
12 | // splice 参数:
13 | // 1. start
14 | // 起始位
15 | // 2. delete_count
16 | // 可选,移除数组元素个数
17 | // 3. item1, item2, ...
18 | // 可选,添加数组元素
19 |
20 | // 返回值:
21 | // 被删元素组成的数组。
22 |
23 |
24 | // start
25 | // 1. 超出数组长度
26 | // 是:从末尾添加元素
27 | // 否:从 start 位置开始操作
28 | // 2. 小于 0
29 | // -n 表示 倒数第 n 个元素。等价于 arr.length - n
30 | // n 大于 arr.length,则表示开始位置为第 0 位。
31 |
32 | function ComputeSpliceStartIndex(start_i, len) {
33 | if (start_i < 0) {
34 | start_i += len
35 | return start_i < 0 ? 0 : start_i
36 | }
37 |
38 | return start_i > len ? len : start_i
39 | }
40 |
41 | // delete_count
42 | // 1. 被省略,或者 它大于 arr.length - start
43 | // start 和 start 后面的所以元素都被删除
44 | // 2. 小于 0
45 | // 不移除元素
46 |
47 | function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
48 | if (num_arguments == 1) {
49 | return len - start_i
50 | }
51 |
52 | let del_count = delete_count
53 | if (del_count < 0) {
54 | return 0
55 | }
56 |
57 | if (del_count > len - start_i) {
58 | return len - start_i
59 | }
60 | return del_count
61 | }
62 |
63 | // 拷贝需要删除的元素
64 | // SparseSlice
65 |
66 | function SparseSlice(array, start_i, del_count, deleted_elements) {
67 | for (let i = 0; i < del_count; i++) {
68 | const element = array[start_i + i];
69 | deleted_elements[i] = element
70 | }
71 | }
72 |
73 | // 移动元素
74 | // SparseMove
75 | function SparseMove(array, start_i, del_count, num_elements_to_add) {
76 | if (num_elements_to_add === del_count) return
77 |
78 | let new_array = new Array(array.length - del_count + num_elements_to_add)
79 |
80 | for (let i = 0; i < new_array.length; i++) {
81 | if (i < start_i) {
82 | new_array[i] = array[i]
83 | } else if (num_elements_to_add < del_count && i >= start_i + (del_count - num_elements_to_add)) {
84 | // 如果 添加数目 < 删除数目,i 填充位为 start_i + (del_count - num_elements_to_add)
85 | new_array[i] = array[i+(del_count - num_elements_to_add)]
86 | } else if (num_elements_to_add > del_count && i >= start_i + num_elements_to_add) {
87 | new_array[i] = array[i-(num_elements_to_add - del_count)]
88 | } else {
89 | continue
90 | }
91 | }
92 | console.log('new_array:', new_array)
93 |
94 | // array = new_array
95 | return new_array
96 | }
97 |
98 | function ArraySplice(start, delete_count, ...item) {
99 | let num_arguments = arguments.length
100 | let array = Object(this)
101 | let len = array.length;
102 |
103 | let start_i = ComputeSpliceStartIndex(start, len)
104 | let del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i)
105 |
106 | let deleted_elements = new Array(del_count)
107 | let num_elements_to_add = item ? item.length : 0
108 |
109 | console.log('array: ', array)
110 | console.log('len: ', len)
111 | console.log('start_i: ', start_i)
112 | console.log('del_count: ', del_count)
113 | console.log('num_elements_to_add: ', num_elements_to_add)
114 |
115 | SparseSlice(array, start_i, del_count, deleted_elements)
116 | array = SparseMove(array, start_i, del_count, num_elements_to_add)
117 |
118 | // 添加/替换元素
119 | let i = start_i
120 | let j = 0
121 | while (j < item.length) {
122 | array[i++] = item[j++]
123 | }
124 |
125 | console.log('array: ', array)
126 | console.log('deleted_elements: ', deleted_elements)
127 |
128 | return deleted_elements
129 | }
130 |
131 | const months = ['Jan', 'March', 'April', 'June']
132 |
133 | var splice1 = ArraySplice.bind(months)
134 |
135 | splice1(1, 0, 'Feb')
136 | splice1(4, 1, 'May')
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # daily-js
2 |
3 | > JavaScript Book
4 |
5 | ## 目录
6 |
7 | **[前言](#前言)**
8 |
9 | 0. **[ES6](https://github.com/stephentian/daily-js/tree/master/00-ES6)**
10 | 1. **[算法 Algorithm](https://github.com/stephentian/daily-js/tree/master/01-Algorithm)**
11 | 2. **[高阶函数 Higher-order Function](https://github.com/stephentian/daily-js/tree/master/02-Higher-order%20Function)**
12 | 3. **[JS 常识 General Knowledge](https://github.com/stephentian/daily-js/tree/master/03-General%20Knowledge)**
13 | 4. **[异步 Async](https://github.com/stephentian/daily-js/tree/master/04-Async)**
14 | 5. **[正则表达式 Regexp](https://github.com/stephentian/daily-js/tree/master/05-Regexp)**
15 | 6. **[Date](https://github.com/stephentian/daily-js/tree/master/06-Date)**
16 | 7. **[面向对象编程 OOP](https://github.com/stephentian/daily-js/tree/master/07-OOP)**
17 | 8. **[this](https://github.com/stephentian/daily-js/tree/master/08-This)**
18 | 9. **[Skills 一些技巧](https://github.com/stephentian/daily-js/tree/master/09-Skills)**
19 | 10. **[设计模式 Design patterns](https://github.com/stephentian/daily-js/tree/master/10-Design-patterns)**
20 | 11. **[事件循环 Event Loop](https://github.com/stephentian/daily-js/tree/master/11-Event%20Loop)**
21 | 12. **[MVVM](https://github.com/stephentian/daily-js/tree/master/12-MVVM)**
22 | 13. **[原型 Prototype](https://github.com/stephentian/daily-js/tree/master/13-Prototype)**
23 | 14. **[数据结构](https://github.com/stephentian/daily-js/tree/master/14-DataStructure)**
24 | 15. **[TypeScript](https://github.com/stephentian/daily-js/tree/master/15-TypeScript)**
25 | 16. **[Source Code](https://github.com/stephentian/daily-js/tree/master/16-Source%20Code)**
26 |
27 | ## 前言
28 |
29 | ### 简介
30 |
31 | JavaScript 和 C++、Java、C# 等语言一样,是一种语言。他和其它所有语言一样,拥有基本类型(字符,数字,布尔)与三大语句(顺序、条件、循环),基于JavaScript我们可以做很多事情,最重要的是 JavaScript 不仅仅运行于浏览器,还可以直接运行于操作系统(有兴趣的同学可以查看有关“windows脚本宿主”的信息)
32 |
33 | 我们平时所使用到的修改DOM的功能,仅仅是浏览器开发商提供给开发者所使用的. 那么只要浏览器开发商乐意,他们还可以提供更多的功能给我们使用。我们也可以理解为,我们写的JavaScript脚本是在浏览器厂商允许的范围内,对浏览器进行二次开发
34 |
35 | 若浏览器允许,JavaScript 还可以调用运行机器上的 ActiveX 组件(比如使用 Scripting.FileSystemObject 对客户端电脑上的文件进行各种操作). 遗憾的是,由于加载 ActiveX 组件可以变向的调用控件台等存在风险的行为,因此大部分浏览器已经禁止了这项操作
36 |
37 | ### 解释型语言和编译型语言
38 |
39 | JavaScript是一种解释型语言,解释型语言不需要编译也能立刻执行代码,非常适合用于变动性大的场景,比如浏览器,我们可以在任意一个时间将head中添加一个新的js引用,由于是解释型语言,浏览器能够立刻解释这段脚本并执行
40 |
41 | **解释型语言:**
42 | 程序不需要编译,程序在运行时才翻译成机器语言,每执 行一次都要翻译一次。因此效率比较低。比如 Basic 语言,专门有一个解释器能够直接执行 Basic 程序,每个语句都是执行的时候才翻译
43 |
44 | 解释型语言有 Python/JavaScript / Perl /Shell 等
45 |
46 | **编译型语言:**
47 | 程序在执行之前需要一个专门的编译过程,把程序编译成 为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。
48 |
49 | 编译型语言有 C、C++、Delphi 等
50 |
51 | **区别:**
52 |
53 | 解释型语言,执行速度慢、效率低;依靠解释器、跨平台性好
54 | 编译型语言,执行速度快、效率高;依靠编译器、跨平台性差些
55 |
56 | ### 兼容性问题
57 |
58 | JavaScript 语言就好比是我们说的“四川话”、“北京话”、“湖南话”等语言中的语法。只要按照这个语法的规则来,就是正确的语言,也就是说是正确的代码
59 |
60 | 电脑其实是一个笨家伙,他虽然看的懂语法,但他看不懂名词。如果规定电脑是 computer,那么他只知道 compouter 是电脑,PC 就不认识了。这就是兼容性
61 |
62 | 随着技术的发展,大家也正在不停地出台各种规范,希望能有一天所有浏览器都能识别完全一样的JavaScript代码
63 |
64 | **浏览器厂商比较常见的差异有:**
65 |
66 | 1. 创建XmlHttpRequest的方式不同
67 | 2. 创建Xml解析器的方式不同
68 | 3. innerText和innerHtml可能存在的差异
69 | 4. 是否提供Json序列化功能等
70 |
--------------------------------------------------------------------------------