├── .gitignore
├── README.md
├── dataStructure
├── Queue
│ ├── index.js
│ └── test.js
└── Stack
│ ├── index.js
│ └── test.js
├── feature
├── attribute
│ └── MyPromise.js
└── method
│ ├── apply.js
│ ├── bind.js
│ ├── call.js
│ ├── instanceof.js
│ └── new.js
├── framework
├── react
│ └── .gitkeep
└── vue
│ ├── mini-vue-router
│ ├── components
│ │ ├── Link.js
│ │ └── View.js
│ ├── create-macher.js
│ ├── create-route-map.js
│ ├── history
│ │ ├── base.js
│ │ ├── hash.js
│ │ └── html5.js
│ ├── index.js
│ ├── install.js
│ └── util
│ │ └── route.js
│ └── mini-vue
│ ├── compiler.js
│ ├── dep.js
│ ├── index.html
│ ├── observer.js
│ ├── vue.js
│ └── watcher.js
├── functions
├── Array
│ ├── every.js
│ ├── filter.js
│ ├── find.js
│ ├── flat.js
│ ├── forEach.js
│ ├── includes.js
│ ├── map.js
│ ├── reduce.js
│ └── some.js
├── Object
│ ├── assign.js
│ ├── orderAssign.js
│ └── reverseAssign.js
└── utils
│ ├── compose.js
│ ├── curry.js
│ ├── debounce.js
│ ├── deepClone.js
│ ├── getType.js
│ ├── getUrlData.js
│ ├── isEqual.js
│ ├── memoize.js
│ └── throttle.js
├── upload.sh
└── work
└── performance
├── FCP.js
└── FMP.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .vscode
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | # fackAchieve
17 |
18 | 手写 es6 函数,Promise 特性,lodash 库的函数实现,模拟 vue,React 等前端框架的实现和原理的理解。
19 |
20 | ## functions 文件夹
21 |
22 | 手动实现各种函数,包括不限于 ES6 等函数的方法
23 |
24 | ### 内容对应表
25 |
26 | #### Array
27 |
28 | | 方法名称(name) | 位置(position) | 作用(effect) |
29 | | :------------- | :--------------------------------------------------------- | :----------: |
30 | | forEach | [functions/Array/forEach.js](functions/Array/forEach.js) | -- |
31 | | every | [functions/Array/every.js](functions/Array/every.js) | -- |
32 | | some | [functions/Array/some.js](functions/Array/some.js) | -- |
33 | | filter | [functions/Array/filter.js](functions/Array/filter.js) | -- |
34 | | find | [functions/Array/find.js](functions/Array/find.js) | -- |
35 | | reduce | [functions/Array/reduce.js](functions/Array/reduce.js) | -- |
36 | | map | [functions/Array/map.js](functions/Array/map.js) | -- |
37 | | flat | [functions/Array/flat.js](functions/Array/flat.js) | -- |
38 | | includes | [functions/Array/includes.js](functions/Array/includes.js) | -- |
39 |
40 | #### Object
41 |
42 | | 方法名称(name) | 位置(position) | 作用(effect) |
43 | | :------------- | :--------------------------------------------------------------------- | :----------------: |
44 | | assign | [functions/Object/assign.js](functions/Object/assign.js) | -- |
45 | | reverseAssign | [functions/Object/reverseAssign.js](functions/Object/reverseAssign.js) | 逆向 assign |
46 | | orderAssign | [functions/Object/orderAssign.js](functions/Object/orderAssign.js) | 逆向 reverseAssign |
47 |
48 | #### utils
49 |
50 | | 方法名称(name) | 位置(position) | 作用(effect) |
51 | | :------------- | :------------------------------------------------------------- | :-----------: |
52 | | memoize | [functions/utils/memoize.js](functions/utils/memoize.js) | 缓存结果 |
53 | | curry | [functions/utils/curry.js](functions/utils/curry.js) | 柯里化 |
54 | | compose | [functions/utils/compose.js](functions/utils/compose.js) | 合并函数 |
55 | | getType | [functions/utils/getType.js](functions/utils/getType.js) | 判断类型 |
56 | | isEqual | [functions/utils/isEqual.js](functions/utils/isEqual.js) | 判断值相等 |
57 | | deepClone | [functions/utils/deepClone.js](functions/utils/deepClone.js) | 深拷贝 |
58 | | getUrlData | [functions/utils/getUrlData.js](functions/utils/getUrlData.js) | 获取 url 参数 |
59 | | debounce | [functions/utils/debounce.js](functions/utils/debounce.js) | 函数防抖 |
60 | | throttle | [functions/utils/throttle.js](functions/utils/throttle.js) | 函数节流 |
61 |
62 | ## feature 文件夹
63 |
64 | 手动实现各种 ES6 新特性
65 |
66 | | 方法名称(name) | 位置(position) | 描述(desc) |
67 | | :------------- | :--------------------------------------------------------------- | :------------------------: |
68 | | MyPromise | [feature/attribute/MyPromise.js](feature/attribute/MyPromise.js) | 符合 A+规范的 Promise 实现 |
69 | | call | [feature/method/call.js](feature/method/call.js) | 手写 call 函数 |
70 | | apply | [feature/method/apply.js](feature/method/apply.js) | 手写 apply 函数 |
71 | | bind | [feature/method/bind.js](feature/method/bind.js) | 手写 bind 函数 |
72 | | new | [feature/method/new.js](feature/method/new.js) | new 构造方法 |
73 | | instanceof | [feature/method/instanceof.js](feature/method/instanceof.js) | instanceof 方法 |
74 |
75 | ## framework 文件夹
76 |
77 | 模拟 vue,React 等前端框架,了解原理
78 |
79 | | 方法名称(name) | 位置(position) | 描述(desc) |
80 | | :------------- | :---------------------------------------------------------------------- | :-------------: |
81 | | vue | [framework/vue/mini-vue](framework/vue/mini-vue/vue.js) | mini vue |
82 | | vue-router | [framework/vue/mini-vue-router](framework/vue/mini-vue-router/index.js) | mini vue-router |
83 | | react | [framework/react](framework/react) | mini react |
84 |
85 | ## dataStructure 文件夹
86 |
87 | 手动实现各种数据结构
88 |
89 | | 结构名称(name) | 位置(position) | 描述(desc) |
90 | | :------------- | :----------------------------------------------------------- | :--------: |
91 | | Stack | [dataStructure/Stack/index.js](dataStructure/Stack/index.js) | 栈结构 |
92 | | Queue | [dataStructure/Queue/index.js](dataStructure/Queue/index.js) | 队列结构 |
93 |
94 | ## work 文件夹
95 |
96 | 工作中,各种场景会碰到的常用方法或类实现
97 |
98 | | 名称(name) | 位置(position) | 描述(desc) |
99 | | :--------- | :------------------------------------------------- | :--------------: |
100 | | FMP | [work/performance/FMP.js](work/performance/FMP.js) | 首次有效绘制时间 |
101 | | FCP | [work/performance/FCP.js](work/performance/FCP.js) | 首屏时间 |
102 |
--------------------------------------------------------------------------------
/dataStructure/Queue/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ES6语法构建队数据结构
3 | */
4 | class Queue{
5 | constructor() {
6 | this._queue = [];
7 | }
8 | // 查询队顶元素
9 | top() {
10 | return this._queue[0];
11 | }
12 | // 元素出队
13 | pop() {
14 | this._queue.shift();
15 | }
16 | // 元素入队
17 | push(item) {
18 | this._queue.push(item);
19 | }
20 | // 查询队尾元素
21 | peek() {
22 | return this._queue[this._queue.length - 1];
23 | }
24 | // 查询队元素总数
25 | length() {
26 | return this._queue.length;
27 | }
28 | // 清空队
29 | clear() {
30 | this._queue = [];
31 | }
32 | // 队是否为空
33 | isEmpty() {
34 | return this._queue.length === 0;
35 | }
36 | // 获取当前队
37 | getStack() {
38 | return this._queue;
39 | }
40 | // 获取队无符号间隔的字符串
41 | getString() {
42 | let str = "";
43 | this._queue.map((item) => (str += item));
44 | return str;
45 | }
46 | reverseStack() {
47 | return this._queue.reverse();
48 | }
49 | // 获取队无符号间隔的反向字符串
50 | getReverseString() {
51 | let str = "";
52 | this._queue.map((item) => (str += item));
53 | return str.split('').reverse().join('')
54 | }
55 | print(){
56 | console.log(this._queue)
57 | }
58 | }
59 | module.exports = Queue
--------------------------------------------------------------------------------
/dataStructure/Queue/test.js:
--------------------------------------------------------------------------------
1 | const Queue = require("./index")
2 |
3 |
4 | let q = new Queue()
5 | q.push('1')
6 | q.push('2')
7 | q.push('3')
8 | q.push('4')
9 | q.push('5')
10 |
11 | q.pop()
12 | q.pop()
13 | console.log(q.getString())
14 | q.print()
--------------------------------------------------------------------------------
/dataStructure/Stack/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ES6语法构建栈数据结构
3 | */
4 | class Stack {
5 | constructor() {
6 | this._stack = [];
7 | }
8 | // 查询栈顶元素
9 | top() {
10 | return this._stack[this._stack.length - 1];
11 | }
12 | // 元素出栈
13 | pop() {
14 | this._stack.pop();
15 | }
16 | // 元素入栈
17 | push(item) {
18 | this._stack.push(item);
19 | }
20 | // 查询栈底元素
21 | peek() {
22 | return this._stack[0];
23 | }
24 | // 查询栈元素总数
25 | length() {
26 | return this._stack.length;
27 | }
28 | // 清空栈
29 | clear() {
30 | this._stack = [];
31 | }
32 | // 是否为空
33 | isEmpty() {
34 | return this._stack.length === 0;
35 | }
36 | // 获取当前栈
37 | getStack() {
38 | return this._stack;
39 | }
40 | // 获取栈无符号间隔的字符串
41 | getString() {
42 | let str = "";
43 | this._stack.map((item) => (str += item));
44 | return str;
45 | }
46 | reverseStack() {
47 | return this._stack.reverse();
48 | }
49 | // 获取栈无符号间隔的反向字符串
50 | getReverseString() {
51 | let str = "";
52 | this._stack.map((item) => (str += item));
53 | return str.split("").reverse().join("");
54 | }
55 | print() {
56 | console.log(this._stack);
57 | }
58 | }
59 |
60 | module.exports = Stack;
61 |
--------------------------------------------------------------------------------
/dataStructure/Stack/test.js:
--------------------------------------------------------------------------------
1 | const Stack = require("./index")
2 |
3 |
4 | /**
5 | * 进制转换
6 | * @param {*} num 需要转换的10进制数
7 | * @param {*} base 需要转换的进制
8 | * @returns
9 | */
10 | function scale(num, base) {
11 | let s = new Stack();
12 | while (num > 0) {
13 | s.push(num % base);
14 | num = Math.floor((num /= base));
15 | }
16 | return s.getReverseString();
17 | }
18 | console.log(scale(125, 2)); // 1111101
19 | console.log(scale(125, 8)); // 175
20 |
--------------------------------------------------------------------------------
/feature/attribute/MyPromise.js:
--------------------------------------------------------------------------------
1 | // 用常量定义promise的三种状态
2 | const PENDING = 'pending';
3 | const FULFILLED = 'fulfilled';
4 | const REJECTED = 'rejected';
5 |
6 | class MyPromise {
7 | constructor(execurte) {
8 | // 默认状态是等待
9 | this.status = PENDING
10 | // 成功的回调默认值
11 | this.value = undefined
12 | // 失败的回调默认值
13 | this.resaon = undefined
14 | // 成功的回调队列,可以多次then,所以存在多个定义为数组
15 | this.resolveCallBacks = []
16 | // 失败的回调
17 | this.rejecteCallBacks = []
18 | // 针对执行器进行异常处理
19 | try {
20 | execurte(this.resolve, this.reject)
21 | } catch (error) {
22 | this.reject(error)
23 | }
24 | }
25 |
26 | // 成功时候的回调
27 | resolve = (value) => {
28 | queueMicrotask(() => {
29 | if (this.status === PENDING) {
30 | this.status = FULFILLED; // 修改状态
31 | this.value = value;
32 | this.resolveCallBacks.forEach((fn) => fn(this.value)); // 成功的回调
33 | }
34 | })
35 | }
36 | // 失败时候的回调
37 | reject = (resaon) => {
38 | queueMicrotask(() => {
39 | if (this.status === PENDING) {
40 | this.status = REJECTED; // 修改状态
41 | this.resaon = resaon;
42 | this.rejecteCallBacks.forEach((fn) => fn(this.resaon)); // 失败的回调
43 | }
44 | })
45 | }
46 | // then方法
47 | then = (resolveCallBack, rejecteCallBack) => {
48 | // 如果传递空值,则默认向后传递所以添加一个默认情况
49 | resolveCallBack = resolveCallBack ? resolveCallBack : value => value;
50 | // 参数可选
51 | rejecteCallBack = rejecteCallBack ? rejecteCallBack : reason => { throw reason };
52 | let p = new MyPromise((resolve, reject) => {
53 | // 处理不同的返回,如果是正常值直接返回,如果是Promise对象,则返回一个Promise供继续调用
54 | // 成功
55 | if (this.status === FULFILLED) {
56 | // 开启一个微任务,等待p结果的返回。否则程序限制性后返回p的值
57 | // 针对执行的函数进行异常处理
58 | queueMicrotask(() => {
59 | try {
60 | let callbackValue = resolveCallBack(this.value)
61 | this._returnValue(p, callbackValue, resolve, reject)
62 | } catch (error) {
63 | reject(error)
64 | }
65 | })
66 | // 失败
67 | } else if (this.status === REJECTED) {
68 | queueMicrotask(() => {
69 | try {
70 | let callbackValue = rejecteCallBack(this.resaon)
71 | this._returnValue(p, callbackValue, resolve, reject)
72 | } catch (error) {
73 | reject(error)
74 | }
75 | })
76 | // 等待过程
77 | } else {
78 | // 判断为等待状态的情况,存储任务然后后续执行
79 | // 存储成功的任务
80 | this.resolveCallBacks.push(() => {
81 | queueMicrotask(() => {
82 | try {
83 | let callbackValue = resolveCallBack(this.value)
84 | this._returnValue(p, callbackValue, resolve, reject)
85 | } catch (error) {
86 | reject(error)
87 | }
88 | })
89 | })
90 | // 存储失败的情况
91 | this.rejecteCallBacks.push(() => {
92 | queueMicrotask(() => {
93 | try {
94 | let callbackValue = rejecteCallBack(this.resaon)
95 | this._returnValue(p, callbackValue, resolve, reject)
96 | } catch (error) {
97 | reject(error)
98 | }
99 | })
100 | })
101 | }
102 | })
103 | return p
104 | }
105 |
106 | // 注册一个非静态的方法,catch收集错误信息
107 | catch(rejecteCallBack) {
108 | return this.then(undefined, rejecteCallBack)
109 | }
110 |
111 | // 注册一个非静态的方法,无论成功或者失败finally都会执行
112 | finally(callback) {
113 | return this.then((value) => {
114 | return MyPromise.resolve(callback()).then(() => value)
115 | }, (resaon) => {
116 | return MyPromise.resolve(callback()).then(() => { throw resaon })
117 | })
118 | }
119 |
120 | // then可能返回一个普通值,也可能返回一个 Promise,一个内置工具
121 | /**
122 | *
123 | * @param {*} p 当前在运行的Promise
124 | * @param {*} callbackValue 返回值(then出来的值)
125 | * @param {*} resolve 成功回调
126 | * @param {*} reject 失败回调
127 | * @returns
128 | */
129 | _returnValue(p, callbackValue, resolve, reject) {
130 | // 如果p和callbackValue相等,则说明产生了循环引用
131 | if (p === callbackValue) {
132 | return reject(new TypeError('靓仔,你的代码循环引用了'))
133 | }
134 | // 判断callbackValue是不是Promise类型
135 | if (callbackValue instanceof MyPromise) {
136 | callbackValue.then(value => resolve(value), resaon => reject(resaon))
137 | } else {
138 | resolve(callbackValue)
139 | }
140 | }
141 |
142 | // 直接注册resolve方法,表示直接只返回一个成功的结果
143 | static resolve(value) {
144 | // 如果是promise对象则直接返回
145 | if (value instanceof MyPromise) {
146 | return value
147 | } else {
148 | // 如果不是promise对象,则重新创建一个
149 | return new MyPromise((resolve) => {
150 | resolve(value)
151 | })
152 | }
153 | }
154 |
155 | // 静态方法,返回错误的Promise
156 | static reject(resaon) {
157 | if (resaon instanceof MyPromise) {
158 | return this.reject('[object Promise]')
159 | } else {
160 | // 如果不是promise对象,则重新创建一个
161 | return new MyPromise((resolve, reject) => {
162 | reject(resaon)
163 | })
164 | }
165 | }
166 |
167 | // all静态方法,有一个失败,直接返回失败,结果是按照传入的顺序返回
168 | static all(promises) {
169 | // 保存回调结果的数组
170 | let result = [];
171 | // 累加器,用来判断执行的方法队列是否执行完成
172 | let count = 0;
173 | // all 方法也返回一个promise对象
174 | return new MyPromise((resolve, reject) => {
175 | function pushResult(key, value) {
176 | result[key] = value
177 | count++
178 | // 如果累加器和执行的任务列表长度相等,则说明已经完成了整个任务
179 | if (count === promises.length) {
180 | resolve(result)
181 | }
182 | }
183 | // 循环处理要执行的任务
184 | promises.forEach((task, index) => {
185 | if (task instanceof MyPromise) {
186 | task.then((v) => pushResult(index, v), (resaon) => reject(resaon))
187 | } else {
188 | pushResult(index, promises[index])
189 | }
190 | })
191 | })
192 | }
193 |
194 |
195 | // 所有 Promises 都完成后(包含成功和失败)
196 | static allSettled(promises) {
197 | return new MyPromise((resolve) => {
198 | let results = []
199 | let count = 0
200 | promises.forEach((task, index) => {
201 | if (task instanceof MyPromise) {
202 | task.finally(_ => {
203 | count++
204 | results[index] = {
205 | status: task.status,
206 | value: task.value || task.resaon
207 | }
208 | if (count === promises.length) {
209 | resolve(results)
210 | }
211 | })
212 | } else {
213 | count++
214 | results[index] = {
215 | status: 'fulfilled',
216 | value: task
217 | }
218 | if (count === promises.length) {
219 | resolve(results)
220 | }
221 | }
222 | })
223 | })
224 | }
225 |
226 | // 有一个成功就返回
227 | static any(promises) {
228 | return new MyPromise((resolve) => {
229 | promises.forEach((task) => {
230 | if (task instanceof MyPromise) {
231 | task.then(_ => {
232 | resolve(task.value)
233 | })
234 | } else {
235 | resolve(task)
236 | }
237 | })
238 | })
239 | }
240 |
241 | // 有一个改变状态(成功或者失败)就返回
242 | static race(promises) {
243 | return new MyPromise((resolve) => {
244 | promises.forEach((task) => {
245 | if (task instanceof MyPromise) {
246 | task.finally(_ => {
247 | resolve(task.value || task.resaon)
248 | })
249 | } else {
250 | resolve(task)
251 | }
252 | })
253 | })
254 | }
255 | }
256 |
257 | MyPromise.defer = MyPromise.deferred = function () {
258 | let testObj = {}
259 | testObj.promise = new Promise((resolve, reject) => {
260 | testObj.resolve = resolve
261 | testObj.reject = reject
262 | })
263 | return testObj
264 | }
265 |
266 | module.exports = MyPromise
--------------------------------------------------------------------------------
/feature/method/apply.js:
--------------------------------------------------------------------------------
1 | // 思路同call,参数不一样
2 | const arr = [1, 2, 3, 4, 5]
3 | function fackApply(context, arg) {
4 | if (typeof this !== 'function') {
5 | throw new TypeError('当前调用apply方法的不是函数!')
6 | }
7 | const flag = Symbol('function')
8 | const callback = context || (typeof window !== 'undefined' ? window : globalThis)
9 | callback[flag] = this
10 | // 同call一样,只是参数需要展开
11 | const result = callback[flag](...arg)
12 | delete callback[flag]
13 | return result
14 | }
15 |
16 | Function.prototype.fackApply = fackApply
17 |
18 | // const max = Math.max(arr);
19 |
20 | // const max = Math.max.apply(null, arr);
21 |
22 | const max = Math.max.fackApply(null, arr);
23 |
24 | console.log(max)
--------------------------------------------------------------------------------
/feature/method/bind.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 编写思路
3 | * 1.返回一个原返回一个函数
4 | * 2.传递参数并绑定传入的this
5 | * 3.可以通过new调用
6 | */
7 | const user = {
8 | x: 42,
9 | getX: function () {
10 | return this.x;
11 | }
12 | };
13 |
14 | function fackBind(thisArg, ...args) {
15 | if (typeof this !== 'function') {
16 | throw new TypeError('当前调用bind方法的不是函数!')
17 | }
18 | const callback = thisArg || (typeof window !== 'undefined' ? window : globalThis)
19 | return () => this.apply(callback, args)
20 | }
21 |
22 | // MDN版本
23 | function fackBind2(otherThis) {
24 | if (typeof this !== 'function') {
25 | throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
26 | }
27 | var ArrayPrototypeSlice = Array.prototype.slice;
28 | var baseArgs = ArrayPrototypeSlice.call(arguments, 1),
29 | baseArgsLength = baseArgs.length,
30 | fToBind = this,
31 | fNOP = function () { },
32 | fBound = function () {
33 | baseArgs.length = baseArgsLength; // reset to default base arguments
34 | baseArgs.push.apply(baseArgs, arguments);
35 | return fToBind.apply(
36 | fNOP.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs
37 | );
38 | };
39 |
40 | if (this.prototype) {
41 | fNOP.prototype = this.prototype;
42 | }
43 | fBound.prototype = new fNOP();
44 |
45 | return fBound;
46 | };
47 |
48 | Function.prototype.fackBind = fackBind
49 | Function.prototype.fackBind2 = fackBind2
50 |
51 | const generator = user.getX;
52 |
53 | const a = generator.bind(user);
54 | console.log(a());
55 |
56 | const b = generator.fackBind(user);
57 | console.log(b());
58 |
59 | const c = generator.fackBind2(user);
60 | console.log(c());
61 |
--------------------------------------------------------------------------------
/feature/method/call.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 编写思路
3 | * 1.确定函数的作用域
4 | * 2.存储执行函数的执行结果
5 | * 3.删除零时函数并返回
6 | */
7 |
8 | const fackCall = function (context, ...args) {
9 | if (typeof this !== 'function') {
10 | throw new TypeError('当前调用call方法的不是函数!')
11 | }
12 | // 定义一个私有标识符
13 | const flag = Symbol('function')
14 | // 确定返回函数的作用域, 默认是window,区分node和浏览器环境
15 | const callback = context || (typeof window !== 'undefined' ? window : globalThis)
16 | // 把要执行函数的函数体 复制到临时的函数中
17 | callback[flag] = this
18 | // 保存返回的结果
19 | const result = callback[flag](...args)
20 | // 删除临时变量
21 | delete callback[flag]
22 | // 返回结果
23 | return result
24 | }
25 |
26 |
27 | Function.prototype.fackCall = fackCall
28 |
29 | function Product(name, price) {
30 | this.name = name;
31 | this.price = price;
32 | }
33 | function Food(name, price) {
34 | // Product(name, price) 没有this会找不到
35 | // Product.call(this, name, price);
36 | Product.fackCall(this, name, price);
37 | }
38 |
39 | console.log(new Food('张三', 5).name);
40 | console.log(new Food('张三', 5).price);
41 |
--------------------------------------------------------------------------------
/feature/method/instanceof.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @description 编写思路
4 | * 1. 不支持运算符重载,通过函数穿参数解决
5 | * 2. 判断对象A是否在对象B的原型链上
6 | * 3. 如果不存在就一直查询,一直到顶层
7 | * 3. 返回一个布尔值
8 | */
9 | function Car(make, model, year) {
10 | this.make = make;
11 | this.model = model;
12 | this.year = year;
13 | }
14 |
15 | const auto = new Car('Honda', 'Accord', 1998);
16 |
17 |
18 | function fackInstanceof(detectObject, souceObject) {
19 | let leftObj = Object.getPrototypeOf(detectObject) // 相当于detectObject.__proto__
20 | let rightObj = souceObject.prototype // 获取源对象原型
21 | // 循环获取detectObject对象的原型
22 | while (true) {
23 | // 如果是null直接返回(第一次或者原型链顶)
24 | if (Object.is(leftObj, null)) return false
25 | // 如果第一次相等直接返回
26 | if (Object.is(leftObj, rightObj)) return true
27 | // 不相等,继续向上找
28 | leftObj = Object.getPrototypeOf(leftObj)
29 | }
30 | }
31 |
32 | console.log(auto instanceof Car);
33 |
34 | console.log(auto instanceof Object);
35 |
36 | console.log(fackInstanceof(auto, Car));
37 |
38 | console.log(fackInstanceof(auto, Object));
--------------------------------------------------------------------------------
/feature/method/new.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 编写思路
3 | * 1. js不支持运算符重载,所以第一个参数是constructor,剩余的参数是arguments
4 | * 2. 创建一个新对象,并链接构造函数到运算的对象
5 | * 3. 绑定对象的this
6 | * 4. 如果没对象返回this,有对象返回新对象
7 | */
8 |
9 | const fackNew = function (thisArg, ...args) {
10 | if (typeof thisArg !== 'function') {
11 | throw new TypeError('当前调用fackNew的不是函数!')
12 | }
13 | // 定义一个空对象
14 | let temoObj = {}
15 | // 链接该对象(设置该对象的constructor)到另一个对象 ,继承旧对象的原型
16 | // Object.setPrototypeOf(temoObj, thisArg.prototype);
17 | temoObj = Object.create(thisArg.prototype); // 推荐这种
18 | // 设置临时对象的this
19 | thisArg.apply(temoObj, args)
20 | // 定义要返回的对象
21 | let returnObj = temoObj
22 | // 如果函数没有返回对象,则返回this
23 | if (typeof thisArg === 'object') {
24 | returnObj = this
25 | }
26 | return returnObj
27 | }
28 |
29 | // 精简代码版本
30 | const _new = function (thisArg, ...args) {
31 | const temoObj = Object.create(thisArg.prototype)
32 | thisArg.apply(temoObj, args)
33 | return typeof thisArg === 'object' ? this : temoObj
34 | }
35 |
36 |
37 |
38 | function Car(name, age) {
39 | this.name = name;
40 | this.age = age;
41 | }
42 |
43 | const car1 = new Car('张三', 1993);
44 |
45 | console.log(car1.name);
46 |
47 |
48 | const car2 = _new(Car, '李四', 1998);
49 |
50 | console.log(car2.name);
--------------------------------------------------------------------------------
/framework/react/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gzg1023/fackAchieve/472a586e6c201c2e8a93f9fadd42cb9b428e6c38/framework/react/.gitkeep
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/components/Link.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'routerLink',
3 | props: {
4 | to: {
5 | type: String,
6 | require: true
7 | }
8 | },
9 | render(h) {
10 | return h('a',
11 | {
12 | attrs: {
13 | href: this.$router.mode === 'hash' ? `#${this.to}` : `${this.to}`
14 | }
15 | }, this.$slots.default)
16 | }
17 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/components/View.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'routerView',
3 | render(h) {
4 | const route = this.$route
5 | let depth = 0;
6 | this.isRouterVirw = true
7 | let parent = this.$parent
8 | while (parent) {
9 | if(parent.isRouterVirw){
10 | depth ++
11 | }
12 | parent = parent.$parent
13 | }
14 | const record = route.matched[depth]
15 | if(record){
16 | return h(record.component)
17 | }
18 | return h()
19 | }
20 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/create-macher.js:
--------------------------------------------------------------------------------
1 | import createRouteMap from "./create-route-map";
2 | import createRoute from "./util/route";
3 | export default function createMacher(routes) {
4 | const { pathList, pathMap } = createRouteMap(routes)
5 |
6 | // 匹配路由的函数,通过路径拿到组件
7 | function match(path) {
8 | const record = pathMap[path]
9 | if(record){
10 | return createRoute(record,path)
11 | }
12 | return createRoute(null,path)
13 | }
14 | // 动态添加路由
15 | function addRoutes(routes) {
16 | createRouteMap(routes, pathList, pathMap)
17 | }
18 | return{
19 | match,
20 | addRoutes
21 | }
22 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/create-route-map.js:
--------------------------------------------------------------------------------
1 | // 解析routers,生成路由表
2 | export default function createRouteMap(routes, pathList, pathMap) {
3 | // 路由路径表
4 | pathList = pathList ?? []
5 | // 路径映射表
6 | pathMap = pathMap ?? {}
7 |
8 | routes.forEach(route => {
9 | addRouteRecord(route, pathList, pathMap)
10 | });
11 |
12 | return {
13 | pathList,
14 | pathMap
15 | }
16 | }
17 |
18 | function addRouteRecord(route, pathList, pathMap, parentRecord) {
19 | // 处理父子路由
20 | let path = parentRecord ? `${parentRecord.path}/${route.path}` : route.path
21 | let record = {
22 | path: path,
23 | component: route.component,
24 | parentRecord: parentRecord
25 | }
26 | // 如果不存在则添加
27 | if (!pathMap[path]) {
28 | pathList.push(path)
29 | pathMap[path] = record
30 | }
31 | // 处理子路由
32 | if (route.children) {
33 | route.children.forEach((subRoute) => {
34 | addRouteRecord(subRoute, pathList, pathMap, record)
35 | })
36 | }
37 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/history/base.js:
--------------------------------------------------------------------------------
1 | import createRoute from "../util/route";
2 | export default class History {
3 | constructor(router) {
4 | this.router = router
5 | // 当前路由对象。通过match创建的对象
6 | this.current = createRoute(null, '/')
7 | this.callback = null
8 | }
9 |
10 | /**
11 | * 路由跳转函数
12 | * @param {*} path 要跳转的路径
13 | * @param {*} onComplete 回调函数
14 | */
15 | transitionTo(path, onComplete) {
16 | this.current = this.router.matcher.match(path)
17 | this.callback && this.callback(this.current)
18 | onComplete && onComplete()
19 | }
20 |
21 |
22 | listen(callback){
23 | this.callback = callback
24 | }
25 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/history/hash.js:
--------------------------------------------------------------------------------
1 | import History from "./base"
2 |
3 | export default class hashHistory extends History{
4 | constructor(router){
5 | super(router)
6 | // 修正浏览器地址栏。改为/#/
7 | this.ensureSlash()
8 | }
9 |
10 |
11 | ensureSlash(){
12 | if(location.hash){
13 | return
14 | }else{
15 | location.hash = '/'
16 | }
17 | }
18 | // 返回当前路由地址,去除#符号
19 | getCurrentLocation(){
20 | return location.hash.substr(1)
21 | }
22 |
23 | // 设置hash监听
24 | setUpListener(){
25 | window.addEventListener('hashchange',() =>{
26 | this.transitionTo(this.getCurrentLocation())
27 | })
28 | }
29 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/history/html5.js:
--------------------------------------------------------------------------------
1 | import History from "./base"
2 |
3 | export default class HTML5History extends History{
4 | constructor(router){
5 | super(router)
6 | }
7 |
8 |
9 | // 返回当前路由地址,去除#符号
10 | getCurrentLocation(){
11 | return location.pathname
12 | }
13 |
14 | // 设置history监听
15 | setUpListener(){
16 | window.addEventListener('popstate',() =>{
17 | this.transitionTo(this.getCurrentLocation())
18 | })
19 | }
20 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/index.js:
--------------------------------------------------------------------------------
1 |
2 | import install from './install'
3 | import createMacher from './create-macher';
4 | import HTML5History from "./history/html5";
5 | import HashHistory from "./history/hash";
6 | export default class VueRouter {
7 | constructor(options) {
8 | this._routes = options.routes || []
9 | this.matcher = createMacher(this._routes)
10 | let mode = options.mode || 'hash'
11 | this.mode = mode
12 | this.push = this.push
13 | switch (mode) {
14 | case 'history':
15 | this.history = new HTML5History(this, options.base)
16 | break;
17 | case 'hash':
18 | this.history = new HashHistory(this, options.base, this.feedback)
19 | break;
20 | default:
21 | throw new Error('type Error: mode is not a goog value')
22 | }
23 | }
24 |
25 | init(app) {
26 | const history = this.history
27 |
28 | history.listen((current)=>{
29 | app._route = current
30 | })
31 |
32 | history.transitionTo(
33 | history.getCurrentLocation(),
34 | () => {
35 | history.setUpListener()
36 | }
37 | )
38 | }
39 |
40 | push(path) {
41 | if (this.mode === 'hash') {
42 | location.hash = '/' + path
43 | } else {
44 | location.pathname = path
45 | }
46 | }
47 | }
48 | VueRouter.install = install
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/install.js:
--------------------------------------------------------------------------------
1 |
2 | // 保存传入的Vue实例
3 | export let _Vue = null
4 | import Link from "./components/Link";
5 | import View from "./components/View";
6 | export default function install(Vue) {
7 | _Vue = Vue
8 | _Vue.mixin({
9 | beforeCreate() {
10 | // 跟实例
11 | if (this.$options.router) {
12 | this._routerRoot = this
13 | this._router = this.$options.router
14 | this._router.init(this)
15 | _Vue.util.defineReactive(this, '_route', this._router.history.current)
16 | } else /* 组件*/ {
17 | this._routerRoot = this.$parent && this.$parent._routerRoot
18 | }
19 | }
20 | })
21 | _Vue.component(View.name,View)
22 | _Vue.component(Link.name,Link)
23 | Object.defineProperty(Vue.prototype, '$router', {
24 | get () { return this._routerRoot._router }
25 | })
26 |
27 | Object.defineProperty(Vue.prototype, '$route', {
28 | get () { return this._routerRoot._route }
29 | })
30 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue-router/util/route.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 匹配路由对象
3 | * @param {*} record
4 | * @param {*} path
5 | *
6 | */
7 | export default function createRoute(record, path) {
8 | const matched = []
9 | while (record) {
10 | // 使用unshift是因为先拿到子路由,父路由要排到前面
11 | matched.unshift(record)
12 | record = record.parentRecord
13 | }
14 | return {
15 | path,
16 | matched
17 | }
18 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue/compiler.js:
--------------------------------------------------------------------------------
1 | class Compiler {
2 | constructor(vm) {
3 | this.el = vm.$el
4 | this.vm = vm
5 | this.compiler(this.el)
6 | }
7 | // 编译模版,处理各种节点
8 | compiler(el) {
9 | const childNodes = el.childNodes
10 | Array.from(childNodes).forEach((node) => {
11 | if (this.isTextNode(node)) {
12 | // 处理文本
13 | this.compilerText(node)
14 | } else if (this.isElementNode(node)) {
15 | // 处理元素
16 | this.compilerElement(node)
17 | }
18 | // 处理多层节点
19 | if (node.childNodes && node.childNodes.length !== 0) {
20 | this.compiler(node)
21 | }
22 | })
23 | }
24 | // 编译元素节点,处理指令
25 | compilerElement(node) {
26 | Array.from(node.attributes).forEach((attr) => {
27 | let attrName = attr.name
28 | // 判断是否为指令
29 | if (this.isDirective(attrName)) {
30 | // 转化指令
31 | attrName = attrName.substr(2)
32 | let key = attr.value
33 | this.update(node, key, attrName)
34 | }
35 | })
36 | }
37 | // 编译文本节点,处理差值表达式
38 | compilerText(node) {
39 | let reg = /\{\{(.+?)}\}/
40 | let content = node.textContent
41 | if (reg.test(content)) {
42 | // 获取正则匹配的第一个内容
43 | let key = RegExp.$1.trim()
44 | node.textContent = content.replace(reg, this.vm[key])
45 | // 触发依赖
46 | new Watcher(this.vm, key, (newValue) => {
47 | node.textContent = newValue
48 | })
49 | }
50 | }
51 | // 判断元素是否为指令
52 | isDirective(attrName) {
53 | return attrName.startsWith('v-')
54 | }
55 | // 判断元素是否为文本节点
56 | isTextNode(node) {
57 | return node.nodeType === 3
58 | }
59 | // 判断元素是否为元素节点
60 | isElementNode(node) {
61 | return node.nodeType === 1
62 | }
63 | // 更新指令数据
64 | update(node, key, attrName) {
65 | let updateFn
66 | if (attrName.indexOf(':') !== -1) {
67 | attrName = attrName.substr(3)
68 | updateFn = this.onUpdater
69 | updateFn && updateFn.call(this, node, key, this.vm[key], attrName)
70 | } else {
71 | updateFn = this[attrName + 'Updater']
72 | // 此处的this的Compiler对象
73 | updateFn && updateFn.call(this, node, key, this.vm[key])
74 | }
75 |
76 | }
77 | // 处理v-text指令
78 | textUpdater(node, key, value) {
79 | // 文本节点的值用textContent
80 | node.textContent = value
81 | // 收集依赖
82 | new Watcher(this.vm, key, (newValue) => {
83 | node.textContent = newValue
84 | })
85 | }
86 | // 处理v-model指令
87 | modelUpdater(node, key, value) {
88 | // 表单的值是value
89 | node.value = value
90 | // 收集依赖
91 | new Watcher(this.vm, key, (newValue) => {
92 | node.value = newValue
93 | })
94 | // 双向绑定
95 | node.addEventListener('input', (e) => {
96 | console.log(e)
97 | this.vm[key] = node.value
98 | })
99 | }
100 |
101 | // 处理v-show
102 | showUpdater(node, key, value) {
103 | if (value) {
104 | node.style.display = 'block'
105 | } else {
106 | node.style.display = 'none'
107 | }
108 | new Watcher(this.vm, key, (newValue) => {
109 | node.style.display = newValue ? 'block' : 'none'
110 | })
111 |
112 | }
113 | // 处理v-on
114 | onUpdater(node, key, value, handleType) {
115 | // value = value.substr(2)
116 | console.log("🚀 onUpdater", node, key, value)
117 | node.addEventListener(handleType, (e) => {
118 | this.vm[key]()
119 | })
120 | }
121 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue/dep.js:
--------------------------------------------------------------------------------
1 | // 观察者模式的 发布者
2 | class Dep {
3 | constructor() {
4 | // 收集依赖对象
5 | this.subs = []
6 | }
7 | // 添加依赖对象
8 | addSub(sub) {
9 | if (sub && sub.update) {
10 | this.subs.push(sub)
11 | }
12 | }
13 | // 通知方法
14 | notify() {
15 | this.subs.forEach((sub) => {
16 | sub.update()
17 | })
18 | }
19 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | mini vue
7 |
8 |
9 |
10 |
差值表达式
11 |
{{ msg }}
12 |
13 |
{{ count }}
14 | {{ person }}
15 |
16 |
v-text
17 |
18 |
v-model
19 |
20 |
21 |
v-if
22 |
23 |
v-on
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
52 |
53 |
--------------------------------------------------------------------------------
/framework/vue/mini-vue/observer.js:
--------------------------------------------------------------------------------
1 | class Observer {
2 | constructor(targetData) {
3 | this.walk(targetData)
4 | }
5 | // 遍历对象所有属性
6 | walk(targetData) {
7 | // 判断是否为对象
8 | if (!targetData || typeof targetData !== 'object') {
9 | return
10 | }
11 | // 遍历所有属性
12 | Object.keys(targetData).forEach(key => {
13 | this.defineReactive(targetData, key, targetData[key])
14 | })
15 | }
16 |
17 | // 定义响应式数据
18 | defineReactive(obj, key, value) {
19 | // 收集依赖,来统一更新
20 | let dep = new Dep()
21 | // 转化对象的内部属性
22 | this.walk(value)
23 | const _that = this
24 | Object.defineProperty(obj, key, {
25 | enumerable: true,
26 | configurable: true,
27 | // 不返回obj[key]的原因是会递归触发
28 | get() {
29 | // 收集依赖
30 | Dep.target && dep.addSub(Dep.target)
31 | return value
32 | },
33 | set(newValue) {
34 | if (newValue === value) return
35 | value = newValue
36 | // 处理普通值转为对象的情况
37 | _that.walk(newValue)
38 | // 发生通知
39 | dep.notify()
40 | }
41 |
42 | })
43 | }
44 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue/vue.js:
--------------------------------------------------------------------------------
1 | class Vue {
2 | constructor(options) {
3 | // 1. 通过属性保存选项的数据
4 | this.$options = options || {}
5 | this.$data = options.data || {}
6 | // 如果是字符串就说明是选择器
7 | this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
8 | // 2. 把data的成员转化为getter和setter注入到vue实例
9 | this._proxyData(this.$data)
10 | // 3. 调用observer对象,把data属性转化为响应式数据,监听数据的变化
11 | new Observer(this.$data)
12 | // 4. 调用Compiler对象,处理模版编译
13 | new Compiler(this)
14 | }
15 |
16 | _proxyData(data) {
17 | // 遍历对象
18 | Object.keys(data).forEach((key) => {
19 | Object.defineProperty(this, key, {
20 | enumerable: true,
21 | configurable: true,
22 | get() {
23 | return data[key]
24 | },
25 | set(newValue) {
26 | if (newValue === data[key]) return
27 | data[key] = newValue
28 | }
29 |
30 | })
31 | })
32 | }
33 | }
--------------------------------------------------------------------------------
/framework/vue/mini-vue/watcher.js:
--------------------------------------------------------------------------------
1 | class Watcher {
2 | constructor(vm, key, cb) {
3 | this.vm = vm
4 | this.key = key
5 | this.cb = cb
6 | // 把watcher对象记录到Dep类的静态属性target
7 | Dep.target = this
8 | // 触发get方法,在get方法中会调用addSub
9 | this.oldValue = vm[key]
10 | // 重制依赖对象,防止数据混乱
11 | Dep.target = null
12 | }
13 | update() {
14 | let newValue = this.vm[this.key]
15 | if (this.oldValue === newValue) {
16 | return
17 | }
18 | this.cb(newValue)
19 | }
20 | }
--------------------------------------------------------------------------------
/functions/Array/every.js:
--------------------------------------------------------------------------------
1 | // 手写实现every
2 | function fackEvery(callback, thisArg) {
3 | let tempFlag = true
4 | let array = this
5 | for (let index = 0; index < array.length; index++) {
6 | if (!callback.call(thisArg, array[index], index, array)) {
7 | tempFlag = false
8 | }
9 | }
10 | return tempFlag
11 | }
12 |
13 | // MDN官方every
14 | function fackEvery2(callback, thisArg) {
15 | var T, k;
16 | if (this == null) {
17 | throw new TypeError('this is null or not defined');
18 | }
19 | var O = Object(this);
20 | var len = O.length >>> 0;
21 | if (typeof callback !== 'function') {
22 | throw new TypeError();
23 | }
24 | if (arguments.length > 1) {
25 | T = thisArg;
26 | }
27 | k = 0;
28 | while (k < len) {
29 | var kValue;
30 | if (k in O) {
31 | kValue = O[k];
32 | var testResult = callback.call(T, kValue, k, O);
33 | if (!testResult) {
34 | return false;
35 | }
36 | }
37 | k++;
38 | }
39 | return true;
40 | }
41 |
42 | Array.prototype.fackEvery = fackEvery
43 | Array.prototype.fackEvery2 = fackEvery2
44 | let arr = [1, 2, 3]
45 |
46 | function isHasBig(item){
47 | return item >= 2
48 | }
49 |
50 | let S = arr.fackEvery(isHasBig)
51 | console.log(S)
52 | let T = arr.fackEvery2(isHasBig)
53 | console.log(T)
54 | let Z = arr.every(isHasBig)
55 | console.log(Z)
--------------------------------------------------------------------------------
/functions/Array/filter.js:
--------------------------------------------------------------------------------
1 | function fackFiliter(callback, thisArg) {
2 | let array = this
3 | let tempArr = []
4 | if (typeof callback !== "function") {
5 | throw "参数必须为函数";
6 | }
7 | for (let index = 0; index < array.length; index++) {
8 | if (callback.call(thisArg, (array[index]), index, array)) {
9 | tempArr.push((array[index]))
10 | }
11 | }
12 | return tempArr
13 | }
14 |
15 | // MDN官方filter
16 | function fackFiliter2(callback, thisArg) {
17 | if (!((typeof callback === 'Function' || typeof callback === 'function') && this))
18 | throw new TypeError();
19 | var len = this.length >>> 0,
20 | res = new Array(len),
21 | t = this, c = 0, i = -1;
22 | if (thisArg === undefined) {
23 | while (++i !== len) {
24 | if (i in this) {
25 | if (callback(t[i], i, t)) {
26 | res[c++] = t[i];
27 | }
28 | }
29 | }
30 | }
31 | else {
32 | while (++i !== len) {
33 | if (i in this) {
34 | if (callback.call(thisArg, t[i], i, t)) {
35 | res[c++] = t[i];
36 | }
37 | }
38 | }
39 | }
40 | res.length = c;
41 | return res;
42 | }
43 |
44 | Array.prototype.fackFiliter = fackFiliter
45 | Array.prototype.fackFiliter2 = fackFiliter2
46 |
47 | let arr = [1, 2, 3]
48 |
49 | function filiterFunc(item){
50 | return item >= 2
51 | }
52 |
53 | let S = arr.fackFiliter(filiterFunc)
54 | console.log(S)
55 |
56 | let T = arr.fackFiliter2(filiterFunc)
57 | console.log(T)
58 |
59 | let Z = arr.filter(filiterFunc)
60 | console.log(Z)
--------------------------------------------------------------------------------
/functions/Array/find.js:
--------------------------------------------------------------------------------
1 | // 手写实现find
2 | function fackFind(callback, thisArg) {
3 | let temoItem = undefined
4 | let array = this
5 | for (let index = 0; index < array.length; index++) {
6 | if (callback.call(thisArg, array[index], index, array)) {
7 | temoItem = array[index]
8 | break;
9 | }
10 | }
11 | return temoItem
12 | }
13 |
14 | // MDN官方
15 | function fackFind2(callback) {
16 | if (this == null) {
17 | throw new TypeError('"this" is null or not defined');
18 | }
19 | var o = Object(this);
20 | var len = o.length >>> 0;
21 | if (typeof callback !== 'function') {
22 | throw new TypeError('callback must be a function');
23 | }
24 | var thisArg = arguments[1];
25 | var k = 0;
26 | while (k < len) {
27 | var kValue = o[k];
28 | if (callback.call(thisArg, kValue, k, o)) {
29 | return kValue;
30 | }
31 | k++;
32 | }
33 | return undefined;
34 | }
35 |
36 | Array.prototype.fackFind = fackFind
37 | Array.prototype.fackFind2 = fackFind2
38 |
39 | let arr = [1, 2, 3, 2, 5]
40 |
41 | function logFun(item) {
42 | return item == 2
43 | }
44 |
45 | let S = arr.fackFind(logFun)
46 | console.log(S)
47 |
48 | let T = arr.fackFind2(logFun)
49 | console.log(T)
50 |
51 | let Z = arr.find(logFun)
52 | console.log(Z)
--------------------------------------------------------------------------------
/functions/Array/flat.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 拍平数组
3 | * 编写思路
4 | * 1. 可指定的深度处理
5 | * 2. 递归遍历数组
6 | * 3. 会移除数组中的空项
7 | * 3. 返回结果是新数组
8 | */
9 |
10 | let arr = [1, , 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
11 |
12 |
13 | // 深度默认为1
14 | function fackFlat(depth = 1) {
15 | const tempArr = []
16 | function deepArr(arr, depth) {
17 | // forEach循环会自动过滤空值,如果使用其他循环,需要手动过滤空值
18 | arr.forEach(element => {
19 | // 如果是数组,而且深度还存在,则继续调用
20 | if (Array.isArray(element) && depth > 0) {
21 | deepArr(element, --depth)
22 | } else {
23 | tempArr.push(element)
24 | }
25 | });
26 | }
27 | // 通过this拿到调用的数组
28 | deepArr(this, depth)
29 | return tempArr
30 |
31 | }
32 |
33 | // MDN reduce版本
34 | function fackFlat2(arr, d = 1) {
35 | return d > 0 ? arr.reduce((acc, val) =>
36 | acc.concat(Array.isArray(val) ? fackFlat2(val, d - 1) : val), []) : arr.slice();
37 | };
38 |
39 |
40 |
41 |
42 | Array.prototype.fackFlat = fackFlat
43 |
44 | console.log(arr.flat(Infinity))
45 | console.log(arr.fackFlat(Infinity))
46 | console.log(fackFlat(arr, Infinity));
--------------------------------------------------------------------------------
/functions/Array/forEach.js:
--------------------------------------------------------------------------------
1 | // 手写实现forEach
2 | function fackForEach(callback, thisArg) {
3 | let array = this
4 | var _this;
5 | if (typeof callback !== "function") {
6 | throw "参数必须为函数";
7 | }
8 | for (let index = 0; index < array.length; index++) {
9 | callback.call(thisArg, (array[index]), index, array)
10 | }
11 | }
12 |
13 | // MDN方法
14 | function fackForEach2(callback, thisArg) {
15 | var T, k;
16 | if (this == null) {
17 | throw new TypeError(' this is null or not defined');
18 | }
19 | var O = Object(this);
20 | var len = O.length >>> 0;
21 | if (typeof callback !== "function") {
22 | throw new TypeError(callback + ' is not a function');
23 | }
24 | if (arguments.length > 1) {
25 | T = thisArg;
26 | }
27 | k = 0;
28 | while (k < len) {
29 | var kValue;
30 | if (k in O) {
31 | kValue = O[k];
32 | callback.call(T, kValue, k, O);
33 | }
34 | k++;
35 | }
36 | }
37 |
38 | Array.prototype.fackForEach = fackForEach
39 |
40 | Array.prototype.fackForEach2 = fackForEach2
41 |
42 | let arr = [1, 2, 3]
43 |
44 | arr.fackForEach((item, index, array) => {
45 | console.log('1111', item, index, array)
46 | })
47 |
48 | arr.fackForEach2((item, index, array) => {
49 | console.log('2222', item, index, array)
50 | })
51 |
52 | // 原生forEach
53 | arr.forEach((item, index, array) => {
54 | console.log("3333", item, index, array)
55 | });
--------------------------------------------------------------------------------
/functions/Array/includes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 判断数组中是否包含某个元素
3 | * 编写思路
4 | * 1. 传入需要判断的值在数组中判断
5 | * 2. 如果存在就返回true,不存在就返回false
6 | * 3. 从fromIndex 索引处开始查找 valueToFind,默认0
7 | */
8 |
9 | let arr = [1, 2, 3, 4, 5];
10 |
11 | let testItem = 1
12 |
13 | function fackIncludes(valueToFind, fromIndex = 0) {
14 | if (!Array.isArray(this)) {
15 | throw "非法调用";
16 | }
17 | let findFlag = false
18 | for (let index = fromIndex; index < this.length; index++) {
19 | if (this[index] === valueToFind) {
20 | findFlag = true
21 | }
22 | }
23 | return findFlag
24 | }
25 |
26 |
27 | // MDN 实现
28 | function fackIncludes2(valueToFind, fromIndex = 0) {
29 | if (this == null) {
30 | throw new TypeError('"this" is null or not defined');
31 | }
32 | var o = Object(this)
33 | var len = o.length >>> 0
34 | if (len === 0) {
35 | return false;
36 | }
37 | var n = fromIndex | 0;
38 | var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
39 | function sameValueZero(x, y) {
40 | return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
41 | }
42 | while (k < len) {
43 | if (sameValueZero(o[k], valueToFind)) {
44 | return true;
45 | }
46 | k++;
47 | }
48 | return false;
49 | }
50 |
51 |
52 | Array.prototype.fackIncludes = fackIncludes
53 | Array.prototype.fackIncludes2 = fackIncludes2
54 |
55 | console.log(arr.includes(testItem, 2))
56 | console.log(arr.fackIncludes(testItem, 2))
57 | console.log(arr.fackIncludes2(testItem, 2))
--------------------------------------------------------------------------------
/functions/Array/map.js:
--------------------------------------------------------------------------------
1 |
2 | function fackMap(callback, thisArg) {
3 | let array = this
4 | if (typeof callback !== "function") {
5 | throw "参数必须为函数";
6 | }
7 | let tempArr = []
8 | for (let index = 0; index < array.length; index++) {
9 | tempArr.push(callback.call(thisArg, (array[index]), index, array))
10 | }
11 | return tempArr
12 | }
13 |
14 | // MDN方法
15 | function fackMap2(callback, thisArg) {
16 | var T, A, k;
17 | if (this == null) {
18 | throw new TypeError('this is null or not defined');
19 | }
20 | var O = Object(this);
21 | var len = O.length >>> 0;
22 | if (typeof callback !== 'function') {
23 | throw new TypeError(callback + ' is not a function');
24 | }
25 | if (arguments.length > 1) {
26 | T = arguments[1];
27 | }
28 | A = new Array(len);
29 | k = 0;
30 | while (k < len) {
31 | var kValue, mappedValue;
32 | if (k in O) {
33 | kValue = O[k];
34 | mappedValue = callback.call(T, kValue, k, O);
35 | A[k] = mappedValue;
36 | }
37 | k++;
38 | }
39 | return A;
40 | }
41 |
42 | Array.prototype.fackMap = fackMap
43 | Array.prototype.fackMap2 = fackMap2
44 | let arr = [1, 2, 3]
45 |
46 | function logFun(item, index, array) {
47 | console.log('1111', item, index, array)
48 | return item > 1
49 | }
50 |
51 | let S1 = arr.fackMap(logFun)
52 | let S2 = arr.fackMap2(logFun)
53 | let S3 = arr.map(logFun);
54 | console.log(S1)
55 | console.log(S2)
56 | console.log(S3)
--------------------------------------------------------------------------------
/functions/Array/reduce.js:
--------------------------------------------------------------------------------
1 | // 手写实现find
2 | function fackReduce(callback, initialValue) {
3 | if (typeof callback !== "function") {
4 | throw "参数必须为函数";
5 | }
6 | let array = this
7 | let index = 1;
8 | // MDN:没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。
9 | // 如果提供initialValue,从索引0开始
10 | initialValue ? index = 0 : initialValue = array[0]
11 | for (index; index < this.length; index++) {
12 | initialValue = callback.call(null, initialValue, array[index], index, array)
13 | }
14 | return initialValue
15 | }
16 |
17 |
18 | // MDN版本
19 | function fackReduce2(callback) {
20 | if (this === null) {
21 | throw new TypeError('Array.prototype.fackReduce2 ' +
22 | 'called on null or undefined');
23 | }
24 | if (typeof callback !== 'function') {
25 | throw new TypeError(callback +
26 | ' is not a function');
27 | }
28 | var o = Object(this);
29 | var len = o.length >>> 0;
30 | var k = 0;
31 | var value;
32 | if (arguments.length >= 2) {
33 | value = arguments[1];
34 | } else {
35 | while (k < len && !(k in o)) {
36 | k++;
37 | }
38 | if (k >= len) {
39 | throw new TypeError('Reduce of empty array ' +
40 | 'with no initial value');
41 | }
42 | value = o[k++];
43 | }
44 | while (k < len) {
45 | if (k in o) {
46 | value = callback(value, o[k], k, o);
47 | }
48 | k++;
49 | }
50 | return value;
51 | }
52 |
53 |
54 | Array.prototype.fackReduce = fackReduce
55 | Array.prototype.fackReduce2 = fackReduce2
56 |
57 |
58 | let arr = [1, 2, 3, 4, 5, 20]
59 |
60 | function addFunc(a, b, index, array) {
61 | return a + b
62 | }
63 |
64 | let S = arr.reduce(addFunc, 2)
65 | let S2 = arr.fackReduce(addFunc, 2)
66 | let S3 = arr.fackReduce2(addFunc, 2)
67 | console.log('S', S)
68 | console.log('S2', S2)
69 | console.log('S3', S3)
--------------------------------------------------------------------------------
/functions/Array/some.js:
--------------------------------------------------------------------------------
1 | function fackSome(callback, thisArg) {
2 | let tempFlag = false
3 | let array = this
4 | for (let index = 0; index < array.length; index++) {
5 | if (callback.call(thisArg, array[index], index, array)) {
6 | tempFlag = true
7 | }
8 | }
9 | return tempFlag
10 | }
11 |
12 | // MDN官方filter
13 | function fackSome2(callback, thisArg) {
14 | if (this == null) {
15 | throw new TypeError('Array.prototype.some called on null or undefined');
16 | }
17 |
18 | if (typeof callback !== 'function') {
19 | throw new TypeError();
20 | }
21 |
22 | var t = Object(this);
23 | var len = t.length >>> 0;
24 |
25 | var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
26 | for (var i = 0; i < len; i++) {
27 | if (i in t && callback.call(thisArg, t[i], i, t)) {
28 | return true;
29 | }
30 | }
31 |
32 | return false;
33 | }
34 |
35 | Array.prototype.fackSome = fackSome
36 | Array.prototype.fackSome2 = fackSome2
37 |
38 | let arr = [1, 2, 3]
39 |
40 | function logFun(item) {
41 | return item == '3'
42 | }
43 |
44 | let S = arr.fackSome(logFun)
45 | console.log(S)
46 |
47 | let T = arr.fackSome2(logFun)
48 | console.log(T)
49 |
50 | let Z = arr.some(logFun)
51 | console.log(Z)
--------------------------------------------------------------------------------
/functions/Object/assign.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 分配对象
3 | * 编写思路
4 | * 1. 接受一个tatget对象和多个sources对象
5 | * 2. 将所有可枚举属性的值从一个或多个源对象分配到目标对象
6 | * 3. 返回目标对象。
7 | */
8 |
9 | function fackAssign(target, ...source) {
10 | for (let index = 0; index < source.length; index++) {
11 | const element = source[index];
12 | for (const key in element) {
13 | if (Object.hasOwnProperty.call(element, key)) {
14 | target[key] = element[key]
15 | }
16 | }
17 | }
18 | return target
19 | }
20 |
21 | Object.prototype.fackAssign = fackAssign
22 |
23 | const target = { a: 1, b: 2 };
24 | const source = { b: 4, c: 5 };
25 | const source2 = { d: 6, e: 7 };
26 |
27 |
28 | const returnedTarget = Object.assign(target, source, source2);
29 |
30 | const returnedTarget2 = Object.fackAssign(target, source, source2);
31 |
32 | console.log(target);
33 |
34 | console.log(returnedTarget);
35 |
36 | console.log(returnedTarget2);
37 |
--------------------------------------------------------------------------------
/functions/Object/orderAssign.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 逆向分配对象
3 | * 编写思路
4 | * 同reverseAssign,作用是只覆盖源对象存在的属性,没有的属性不处理
5 | */
6 |
7 | function orderAssign(target, ...source) {
8 | for (let index = 0; index < source.length; index++) {
9 | const element = source[index];
10 | for (const key in element) {
11 | if (Object.hasOwnProperty.call(element, key)) {
12 | if (target[key]) {
13 | target[key] = element[key]
14 | }
15 | }
16 | }
17 | }
18 | return target
19 | }
20 |
21 | Object.prototype.orderAssign = orderAssign
22 |
23 |
24 | const target = { a: 1, b: 2, c: 8 };
25 | const source = { a: 4, b: 5 };
26 | const source2 = { d: 6, e: 7 };
27 |
28 |
29 | const returnedTarget = Object.orderAssign(target, source, source2);
30 |
31 |
32 | console.log(returnedTarget);
33 |
--------------------------------------------------------------------------------
/functions/Object/reverseAssign.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 逆向分配对象
3 | * 编写思路
4 | * 同assign,作用是不覆盖源对象有的属性。只存没有的属性
5 | */
6 |
7 | function reverseAssign(target, ...source) {
8 | for (let index = 0; index < source.length; index++) {
9 | const element = source[index];
10 | for (const key in element) {
11 | if (Object.hasOwnProperty.call(element, key)) {
12 | if (!target[key]) {
13 | target[key] = element[key]
14 | }
15 | }
16 | }
17 | }
18 | return target
19 | }
20 |
21 | Object.prototype.reverseAssign = reverseAssign
22 |
23 |
24 | const target = { a: 1, b: 2, c: 8 };
25 | const source = { a: 4, b: 5 };
26 | const source2 = { d: 6, e: 7 };
27 |
28 | const returnedTarget = Object.reverseAssign(target, source, source2);
29 |
30 | console.log(returnedTarget);
31 |
--------------------------------------------------------------------------------
/functions/utils/compose.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | const toUpper = s => s.toUpperCase()
5 | const toLower = s => s.toLowerCase()
6 | const reverse = arr => arr.reverse()
7 | const first = arr => arr[0]
8 | // 从右到左运行
9 | function fackComposeRight (...callback) {
10 | return function (value){
11 | return callback.reverse().reduce((item,fn)=>{
12 | return fn(item)
13 | },value)
14 | }
15 | }
16 | // 从左到右运行
17 | function fackComposeLeft (...callback) {
18 | return function (value){
19 | return callback.reduce((item,fn)=>{
20 | return fn(item)
21 | },value)
22 | }
23 | }
24 | const f = fackComposeRight(toUpper, first, reverse)
25 | const f2 = fackComposeLeft(first, toLower)
26 |
27 | console.log(f(['oNe', 'tWo', 'thRee']))
28 |
29 | console.log(f2(['oNe', 'tWo', 'thRee']))
30 |
--------------------------------------------------------------------------------
/functions/utils/curry.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | function getSum(a, b, c) {
4 | return a + b + c
5 | }
6 | // es6
7 | function fackCurry3(callback) {
8 | return curryHandle = (...args) => {
9 | // 如果参数相同或超出,直接返回结果
10 | if (args.length >= callback.length) {
11 | return callback(...args)
12 | } else {
13 | // 参数不足,则返回函数,继续调用
14 | return (...args2) => curryHandle(...args.concat((args2)))
15 | }
16 | }
17 | }
18 |
19 | // es5
20 | function fackCurry2(callback) {
21 | return function curryHandle() {
22 | var args = Array.prototype.slice.call(arguments)
23 | if (args.length >= callback.length) {
24 | return callback.apply(null, args)
25 | } else {
26 | return function () {
27 | var args2 = Array.prototype.slice.call(arguments)
28 | return curryHandle.apply(null, args.concat(args2))
29 | }
30 | }
31 | }
32 | }
33 |
34 | // 柯里化后的函数
35 | let curried = fackCurry2(getSum) // 测试
36 |
37 | console.log(curried(1, 2, 3))
38 | console.log(curried(1)(2,3))
39 | console.log(curried(1, 2)(3))
40 | console.log(curried(1)(2)(3))
41 |
--------------------------------------------------------------------------------
/functions/utils/debounce.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @desc 函数防抖
3 | * @param func 函数
4 | * @param wait 延迟执行毫秒数
5 | * @param immediate true 表立即执行,false 表非立即执行
6 | */
7 | function debounce(func, wait, immediate) {
8 | let timeout
9 | return function() {
10 | const context = this
11 | const args = arguments
12 | if (timeout) clearTimeout(timeout)
13 | if (immediate) {
14 | const callNow = !timeout
15 | timeout = setTimeout(() => {
16 | timeout = null
17 | }, wait)
18 | if (callNow) func.apply(context, args)
19 | } else {
20 | timeout = setTimeout(() => {
21 | func.apply(context, args)
22 | }, wait)
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/functions/utils/deepClone.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @description 深拷贝一个函数
4 | * 1. 先判断类型如果是基本类型直接返回
5 | * 2. 通过WeakMap结构 解决循环引用的问题
6 | * 3. 通过dp函数递归 如果是对象类型,递归拷贝交换通过key交换值
7 | * 4. 返回新对象
8 | */
9 |
10 | function deepClone(source) {
11 | if (typeof source !== 'object') {
12 | return source
13 | }
14 | let objMap = new WeakMap();
15 | function dp(){
16 | if(objMap.has(source)){
17 | return objMap.get(source)
18 | }
19 |
20 | let target = Array.isArray(source) ? [] : {}; // 判断对象是不是数组
21 | objMap.set(source,target)
22 | for (let key in source) {
23 | // 如果是对象就递归遍历每个key或者
24 | if (typeof source[key] === 'object' && source[key] != null) {
25 | target[key] = dp(source[key]);
26 | } else {
27 | // 如果是普通对象直接赋值
28 | target[key] = source[key];
29 | }
30 | }
31 | return target;
32 | }
33 | return dp(source);
34 | }
35 |
36 | let obj1 = {
37 | a: 1,
38 | b: 2,
39 | c: {
40 | age: 18,
41 | alex: {
42 | '0': [1, 2, 3],
43 | '2': [4, 5, 6]
44 | }
45 | },
46 | d: [
47 | 1,
48 | 2,
49 | 3
50 | ],
51 |
52 | }
53 |
54 |
55 | let obj2 = {}
56 |
57 | let obj3 = {}
58 |
59 | obj2 = obj1
60 |
61 | obj3 = deepClone(obj1)
62 |
63 | obj1.a = 100; // 测试引用修改
64 |
65 | obj1.obj1 = obj1 // 测试循环引用
66 |
67 | console.log(obj1, obj2, obj3)
--------------------------------------------------------------------------------
/functions/utils/getType.js:
--------------------------------------------------------------------------------
1 | let arr = BigInt(123);
2 | // 类型供参考
3 | let typeList = {
4 | '[object String]': 'String',
5 | '[object Number]': 'Number',
6 | '[object Array]': 'Array',
7 | '[object Function]': 'funtion',
8 | '[object Undefined]': 'Undefined',
9 | '[object Null]': 'Null',
10 | '[object Boolean]': 'Boolean',
11 | '[object Symbol]': 'Symbol',
12 | '[object Object]': 'Object',
13 | '[object Set]': 'Set',
14 | '[object Map]': 'Map',
15 | '[object WeakMap]': 'WeakMap',
16 | '[object WeakSet]': 'WeakSet',
17 | '[object ArrayBuffer]': 'ArrayBuffer',
18 | '[object BigInt]': 'BigInt'
19 |
20 | }
21 |
22 | function getType(val) {
23 | return typeList[Object.prototype.toString.call(val)]
24 | }
25 |
26 | console.log(getType(arr))
--------------------------------------------------------------------------------
/functions/utils/getUrlData.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @description 获取url的参数
4 | * 参数 可能是在index.html后面也可能是在路由#的后面
5 | */
6 |
7 | function getUrlData(){
8 | let url = window.location.search ?
9 | window.location.search.substr(1).split('&') :
10 | window.location.hash.substr(window.location.hash.indexOf('?')+1).split('&')
11 | let urlObj = {}
12 | for(let keys in url){
13 | let urlItem = url[keys].split('=')
14 | urlObj[urlItem[0]] = decodeURI(urlItem[1])
15 | }
16 | return urlObj
17 | }
18 |
19 | // 获取参数对象
20 | getUrlData()
21 |
22 |
--------------------------------------------------------------------------------
/functions/utils/isEqual.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @description 判断两个数据是否相等(广义)
4 | * 1. 先处理基本类型 包括String,Number,Null,undefined,NaN,Boolean 不考虑Symbol
5 | * 2. 引用类型分情况处理 包括Object和Array 不考虑Function类型
6 | * 如果是基本类型,那么判断值是否为全等,认为NaN和NaN相等
7 | * 如果是数组,那么判断每个子项是否相同,子项包括 不同类型
8 | * 如果是对象,
9 | * 如果是空对象认为相等,
10 | * 如果对象里面的key和value相等(广义的值相等,内存地址不一样)且原型相同,那么认为是相等的
11 | * 如果是循环引用的对象,那么两个变量同时引用的对象相等,认为相等
12 | */
13 |
14 |
15 | let obj1 = {}
16 |
17 | let obj2 = {}
18 |
19 |
20 | console.log(Object.is(obj1,obj2))
--------------------------------------------------------------------------------
/functions/utils/memoize.js:
--------------------------------------------------------------------------------
1 | function getArea(r) {
2 | console.log(r)
3 | return Math.PI * r * r
4 | }
5 |
6 | function fackMemoize(callback) {
7 | if (typeof callback != 'function') {
8 | throw new TypeError('传入的内容不是函数');
9 | }
10 | let cache = {}
11 | return function (val) {
12 | let key = JSON.stringify(arguments)
13 | cache[key] = cache[key] || callback.call(callback, val)
14 | return cache[key]
15 | }
16 | }
17 |
18 |
19 | let getAreaWithMemory = fackMemoize(getArea)
20 |
21 | console.log(getAreaWithMemory(4))
22 | console.log(getAreaWithMemory(4))
23 | console.log(getAreaWithMemory(4))
24 |
25 |
26 |
--------------------------------------------------------------------------------
/functions/utils/throttle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @desc 函数节流
3 | * @param func 函数
4 | * @param delay 延迟执行毫秒数
5 | * @param mustRunDelay 需要等待的时间
6 | */
7 | function throttle(fn, delay, mustRunDelay) {
8 | let timer = null
9 | let t_start
10 | return function() {
11 | const context = this
12 | const args = arguments
13 | const t_curr = +new Date()
14 | clearTimeout(timer)
15 | if (!t_start) {
16 | t_start = t_curr
17 | }
18 | if (t_curr - t_start >= mustRunDelay) {
19 | fn.apply(context, args)
20 | t_start = t_curr
21 | } else {
22 | timer = setTimeout(function() {
23 | fn.apply(context, args)
24 | }, delay)
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/upload.sh:
--------------------------------------------------------------------------------
1 | echo "开始上传"
2 |
3 | git add .
4 | git commit -am "$1"
5 | git push
6 |
7 | echo "上传完毕"
--------------------------------------------------------------------------------
/work/performance/FCP.js:
--------------------------------------------------------------------------------
1 | /**
2 | * First Contentful Paint
3 | * 首屏时间计算
4 | */
5 | class FCP {
6 | static details = []
7 | static ignoreEleList = ['script', 'style', 'link', 'br']
8 | constructor() {
9 | }
10 | static isEleInArray(target, arr) {
11 | if (!target || target === document.documentElement) {
12 | return false;
13 | }
14 | else if (arr.indexOf(target) !== -1) {
15 | return true;
16 | }
17 | else {
18 | return this.isEleInArray(target.parentElement, arr);
19 | }
20 | }
21 | static isInFirstScreen(target) {
22 | if (!target || !target.getBoundingClientRect)
23 | return false;
24 | var rect = target.getBoundingClientRect(), screenHeight = window.innerHeight, screenWidth = window.innerWidth;
25 | return rect.left >= 0
26 | && rect.left < screenWidth
27 | && rect.top >= 0
28 | && rect.top < screenHeight;
29 | }
30 |
31 | static getFCP() {
32 | return new Promise((resolve, reject) => {
33 | // 5s之内先收集所有的dom变化,并以key(时间戳)、value(dom list)的结构存起来。
34 | var observeDom = new MutationObserver((mutations) => {
35 | if (!mutations || !mutations.forEach)
36 | return;
37 | var detail = {
38 | time: performance.now(),
39 | roots: []
40 | };
41 | mutations.forEach((mutation) => {
42 | if (!mutation || !mutation.addedNodes || !mutation.addedNodes.forEach)
43 | return;
44 | mutation.addedNodes.forEach((ele) => {
45 | if (ele.nodeType === 1 && this.ignoreEleList.indexOf(ele.nodeName.toLocaleLowerCase()) === -1) {
46 | if (!this.isEleInArray(ele, detail.roots)) {
47 | detail.roots.push(ele);
48 | }
49 | }
50 | });
51 | });
52 | if (detail.roots.length) {
53 | this.details.push(detail);
54 | }
55 | });
56 | observeDom.observe(document, {
57 | childList: true,
58 | subtree: true
59 | });
60 | setTimeout(() => {
61 | observeDom.disconnect();
62 | resolve(this.details);
63 | }, 5000);
64 | }).then((details) => {
65 | // 分析上面收集到的数据,返回最终的结果
66 | var result;
67 | details.forEach((detail) => {
68 | for (var i = 0; i < detail.roots.length; i++) {
69 | if (this.isInFirstScreen(detail.roots[i])) {
70 | result = detail.time;
71 | break;
72 | }
73 | }
74 | });
75 | // 遍历当前请求的图片中,如果有开始请求时间在首屏dom渲染期间的,则表明该图片是首屏渲染中的一部分,
76 | // 所以dom渲染时间和图片返回时间中大的为首屏渲染时间
77 | window.performance.getEntriesByType('resource').forEach(function (resource) {
78 | if (resource.initiatorType === 'img' && (resource.fetchStart < result || resource.startTime < result) && resource.responseEnd > result) {
79 | result = resource.responseEnd;
80 | }
81 | });
82 | return result;
83 | });
84 | }
85 | }
86 |
87 |
88 | /**
89 | * FCP.getFCP().then(fst => {
90 | console.log('首屏时间' + fst + 'ms')
91 | })
92 | */
--------------------------------------------------------------------------------
/work/performance/FMP.js:
--------------------------------------------------------------------------------
1 | class FMP {
2 | /**
3 | * get first-meaningful-paint
4 | * 首次有效绘制时间
5 | */
6 | static getFmp(observeTime = 3000) {
7 | if (!Promise
8 | || !window.performance
9 | || !window.performance.timing
10 | || !window.requestAnimationFrame
11 | || !wiÎndow.MutationObserver) {
12 | console.log('fmp can not be retrieved');
13 | Promise.reject(new Error('fmp can not be retrieved'));
14 | }
15 |
16 | const promise = new Promise((resolve) => {
17 | const observedPoints = [];
18 | const observer = new window.MutationObserver(() => {
19 | const innerHeight = window.innerHeight;
20 | function getDomMark(dom, level) {
21 | const length = dom.children ? dom.children.length : 0;
22 | let sum = 0;
23 | const tagName = dom.tagName;
24 | if (tagName !== 'SCRIPT' && tagName !== 'STYLE' && tagName !== 'META' && tagName !== 'HEAD') {
25 | if (dom.getBoundingClientRect && dom.getBoundingClientRect().top < innerHeight) {
26 | sum += (level * length);
27 | }
28 | if (length > 0) {
29 | const children = dom.children;
30 | for (let i = 0; i < length; i++) {
31 | sum += getDomMark(children[i], level + 1);
32 | }
33 | }
34 | }
35 | return sum;
36 | }
37 | window.requestAnimationFrame(() => {
38 | const timing = window.performance.timing;
39 | const startTime = timing.navigationStart || timing.fetchStart;
40 | const t = new Date().getTime() - startTime;
41 | const score = getDomMark(document, 1);
42 | observedPoints.push({
43 | score,
44 | t,
45 | });
46 | });
47 | });
48 | observer.observe(document, {
49 | childList: true,
50 | subtree: true,
51 | });
52 |
53 | setTimeout(() => {
54 | observer.disconnect();
55 | const rates = [];
56 | for (let i = 1; i < observedPoints.length; i++) {
57 | if (observedPoints[i].t !== observedPoints[i - 1].t) {
58 | rates.push({
59 | t: observedPoints[i].t,
60 | rate: observedPoints[i].score - observedPoints[i - 1].score,
61 | });
62 | }
63 | }
64 | rates.sort((a, b) => b.rate - a.rate);
65 | if (rates.length > 0) {
66 | resolve(rates[0].t);
67 | } else {
68 | resolve(observeTime);
69 | }
70 | }, observeTime);
71 | });
72 | return promise;
73 | }
74 | }
75 |
76 | /**
77 | * FMP.getFmp().then(fst => {
78 | console.log('首屏时间' + fst + 'ms')
79 | })
80 | */
--------------------------------------------------------------------------------