├── .gitignore ├── LeetCode.md ├── README.md ├── SUMMARY.md └── src ├── apply.js ├── array2Object.js ├── before.js ├── binarySearch.js ├── bind.js ├── call.js ├── camel.js ├── city.js ├── compose.js ├── curry.js ├── debounce.js ├── deepclone.js ├── flatten.js ├── heapSort.js ├── insertionSort.js ├── instanceof.js ├── interval.js ├── jsonp.js ├── lis.js ├── longestCommonPrefix.js ├── mergeSort.js ├── missingNumber.js ├── new.js ├── pi.js ├── promise.js ├── promiseAll.js ├── promiseRace.js ├── proxy.js ├── quickSort.js ├── selectionSort.js ├── shellSort.js ├── subscribe.js ├── throttle.js ├── trim.js └── unique.js /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .DS_Store -------------------------------------------------------------------------------- /LeetCode.md: -------------------------------------------------------------------------------- 1 | - [ ] LeetCode 001 Two Sum 2 | - [ ] LeetCode 015 3Sum(可能会问 LeetCode18.4Sum思路) 3 | - [ ] LeetCode 020 Valid Parentheses 4 | - [ ] LeetCode 021 Merge Two Sorted Lists 5 | - [ ] LeetCode 025 Reverse Nodes in k-group 6 | - [ ] LeetCode 053 Maximum Subarra 7 | - [ ] LeetCode 066 Plus One(等介于:高精度加加法) 8 | - [ ] LeetCode 098 Validate Binary Search Tree 9 | - [ ] LeetCode 110 Balanced Binary Tree 10 | - [ ] LeetCode 134 Gas Station 11 | - [ ] LeetCode 136 Single Number 12 | - [ ] LeetCode 137 Single Number II 13 | - [ ] LeetCode 146 LRU Cache(变形题:带有过期时间的LRU缓存)(Map + 链表) 14 | - [ ] LeetCode 206 Reverse Linked List(递归、迭代) 15 | - [ ] LeetCode 215 Kth Largest Element in an Array(等价于:快速排序) 16 | - [ ] LeetCode 232 Implement Queue using Stacks(每次将新来的元素放到栈底,stack.push()) 17 | - [ ] LeetCode 328 Odd Even Linked List 18 | - [ ] LeetCode 415 Add Strings(等价于:大数加法) 19 | - [ ] LeetCode 470 rand70rand100 20 | - [ ] LeetCode 496 Next Greater Element I(时间复杂度O(n)(单调栈,使用 Map 映射 stack 的指定位置) 21 | - [ ] LeetCode 716 Max Stack(两个栈实现最大栈,要求pop,push, get max都为0(1) 22 | - [ ] LeetCode 860 Lemonade Change 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 前端面试手写题 2 | 3 | 整理前端面试常见的手写题,面试出现概率极高,建议每个都过自己过一遍。 4 | 5 | 你也可以提交 PR,提供你面试遇到的题目。 6 | 7 | 代码汇总请参照 [SUMMARY.md](./SUMMARY.md) 。 8 | 9 | **高频**:★★★
10 | **中频**:★★
11 | **低频**:★
12 | 13 | ## JavaScript 原理 14 | 15 | - [实现 bind()](./src/bind.js) ★★ 16 | - [实现 apply()](./src/apply.js) ★★ 17 | - [实现 call()](./src/call.js) ★★ 18 | - [实现 instanceof](./src/instanceof.js) ★ 19 | - [实现 new](./src/new.js) ★ 20 | - [实现 jsonp](./src/jsonp.js) ★★★ 21 | - [实现 Promise](./src/promise.js) ★★★ 22 | - [实现 Promise.all()](./src/promiseAll.js) ★★★ 23 | - [实现 Promise.race()](./src/promiseRace.js) ★ 24 | - [实现 Proxy](./src/proxy.js) ★★ 25 | - [实现 EventEmitter 订阅监听模式](./src/subscribe.js) ★★ 26 | - [setTimout 实现 setInterval](./src/interval.js) ★ 27 | - [深拷贝](./src/deepclone.js) ★★★ 28 | 29 | 30 | ### 函数 31 | 32 | - [函数防抖](./src/debounce.js) ★★★ 33 | - [函数节流](./src/throttle.js) ★★★ 34 | - [函数柯里化(实现 `sum(1,2)(3)()`)](./src/curry.js) ★★★ 35 | - [compose 函数](./src/compose.js) ★★ 36 | - [before 函数](./src/before.js) ★ 37 | 38 | 39 | ## 数组 40 | 41 | - [数组拍平](./src/flatten.js) ★★★ 42 | - [数组去重](./src/unique.js) ★★★ 43 | - [数组转对象](./src/array2Object.js) ★ 44 | 45 | ## 字符串 46 | 47 | - [去除字符串首尾空格](./src/trim.js) ★ 48 | - [下划线驼峰互转](./src/camel.js) ★ 49 | 50 | ## 数学 51 | 52 | - [计算圆周率](./src/pi.js) ★ 53 | 54 | ## 算法 55 | 56 | 算法需要掌握基本的数据结构,例如栈、队列、链表、树、排序算法等等,建议去 [LeetCode](https://leetcode-cn.com/) 上刷题。不过不要为了刷题而刷题,最重要的是归纳与总结,**刷十道不如一道刷十遍。** 57 | 58 | - [归并排序](./src/mergeSort.js) ★★ 59 | - [插入排序](./src/insertionSort.js) ★ 60 | - [快速排序](./src/quickSort.js) ★★★ 61 | - [选择排序](./src/selectionSort.js) ★ 62 | - [希尔排序](./src/shellSort.js) ★ 63 | - [堆排序](./src/heapSort.js) ★ 64 | - [二分查找](./src/binarySearch.js) ★ 65 | - [最长递增子序列](./src/lis.js) ★★★ 66 | - [最长公共子串](./src/longestCommonPrefix.js) ★★ 67 | - [城市数据找父节点](./src/city.js) ★ 68 | - [查找缺失的数](./src/missingNumber.js) ★ 69 | 70 | 71 | 分享一下自己整理的 LeetCode 上必刷的题,比较具有代表性。 72 | 73 | - [ ] LeetCode 001 Two Sum 74 | - [ ] LeetCode 015 3Sum(可能会问 LeetCode18.4Sum思路) 75 | - [ ] LeetCode 020 Valid Parentheses 76 | - [ ] LeetCode 021 Merge Two Sorted Lists 77 | - [ ] LeetCode 025 Reverse Nodes in k-group 78 | - [ ] LeetCode 053 Maximum Subarra 79 | - [ ] LeetCode 066 Plus One(等介于:高精度加加法) 80 | - [ ] LeetCode 098 Validate Binary Search Tree 81 | - [ ] LeetCode 110 Balanced Binary Tree 82 | - [ ] LeetCode 134 Gas Station 83 | - [ ] LeetCode 136 Single Number 84 | - [ ] LeetCode 137 Single Number II 85 | - [ ] LeetCode 146 LRU Cache(变形题:带有过期时间的LRU缓存)(Map + 链表) 86 | - [ ] LeetCode 206 Reverse Linked List(递归、迭代) 87 | - [ ] LeetCode 215 Kth Largest Element in an Array(等价于:快速排序) 88 | - [ ] LeetCode 232 Implement Queue using Stacks(每次将新来的元素放到栈底,stack.push()) 89 | - [ ] LeetCode 328 Odd Even Linked List 90 | - [ ] LeetCode 415 Add Strings(等价于:大数加法) 91 | - [ ] LeetCode 470 rand70rand100 92 | - [ ] LeetCode 496 Next Greater Element I(时间复杂度O(n)(单调栈,使用 Map 映射 stack 的指定位置) 93 | - [ ] LeetCode 716 Max Stack(两个栈实现最大栈,要求pop,push, get max都为0(1) 94 | - [ ] LeetCode 860 Lemonade Change 95 | 96 | 97 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | ### 实现 bind() 2 | 3 | ```javascript 4 | Function.prototype.bindNew = function (context, ...args) { 5 | return (...newArgs) => this.apply(context, [...args, ...newArgs]); 6 | }; 7 | 8 | // test 9 | const test = { 10 | name: "fy", 11 | showName: function (last: string) { 12 | console.log(this.name + " is " + last); 13 | }, 14 | }; 15 | test.showName("handsome"); // fy is handsome 16 | test.showName.bind({ name: "Mr.fy" })("handsome"); 17 | test.showName.bindNew({ name: "Mr.fy" })("handsome"); 18 | ``` 19 | 20 | ### 实现 apply() 21 | 22 | ```javascript 23 | Function.prototype.myApply = function (context, args) { 24 | context.fn = this; 25 | let res; 26 | if (!args){ 27 | res = context.fn(); 28 | } else { 29 | res = context.fn(...args) 30 | } 31 | return res; 32 | } 33 | 34 | // test 35 | let obj = { 36 | name: 'jack' 37 | } 38 | function test(arg1, arg2, arg3) { 39 | console.log(this.name) // jack 40 | console.log(arg1, arg2, arg3); // 1 2 3 41 | } 42 | test.myApply(obj, [1,2,3]); 43 | ``` 44 | 45 | ### 实现 call() 46 | 47 | ```javascript 48 | Function.prototype.myCall = function (context, ...rest) { 49 | context.fn = this; 50 | var result = context.fn(...rest); 51 | delete context.fn; 52 | return result; 53 | } 54 | 55 | // test 56 | let obj = { 57 | name: 'jack' 58 | } 59 | function test(arg1, arg2, arg3) { 60 | console.log(this.name) // jack 61 | console.log(arg1, arg2, arg3); // 1 2 3 62 | } 63 | test.myCall(obj, 1,2,3); 64 | ``` 65 | 66 | ### 实现 instanceof 67 | 68 | ```javascript 69 | function myInstanceOf(left, right) { 70 | let prototype = right.prototype; 71 | left = left.__proto__; 72 | while(true) { 73 | if (!left) return false; 74 | if (left == prototype) return true; 75 | left = left.__proto__; 76 | } 77 | } 78 | 79 | console.log(myInstanceOf([], Array)); // true 80 | ``` 81 | 82 | ### 实现 new 83 | 84 | ```javascript 85 | function myNew (fun, ...args) { 86 | let obj = {}; 87 | obj.__proto__ = fun.prototype; 88 | let res = fun.apply(obj, args); 89 | return res instanceof Object ? res : obj; 90 | } 91 | 92 | function Animal(name) { 93 | this.name = name; 94 | } 95 | let animal = myNew(Animal, 'dog'); 96 | console.log(animal.name) // dog 97 | ``` 98 | 99 | ### 实现 jsonp 100 | 101 | ```javascript 102 | var newscript = document.createElement('script'); 103 | newscript.src = 'https://www.adb.com?callback=fn' 104 | document.body.appendChild(newscript); 105 | function fn(data) { 106 | console.log(data); 107 | } 108 | ``` 109 | 110 | ### 实现 Promise 111 | 112 | ```javascript 113 | const PENDING = Symbol(); 114 | const REJECTED = Symbol(); 115 | const FULLFILLED = Symbol(); 116 | 117 | const MyPromise = function(fn) { 118 | this.state = PENDING; 119 | this.value = ''; 120 | 121 | const resolve = (value) => { 122 | this.state = FULLFILLED; 123 | this.value = value; 124 | } 125 | 126 | const reject = (error) => { 127 | this.state = REJECTED; 128 | this.value = error; 129 | } 130 | 131 | this.then = (onFullFill, onReject) => { 132 | if (this.state == FULLFILLED) { 133 | onFullFill(this.value); 134 | } else { 135 | onReject(this.value); 136 | } 137 | } 138 | 139 | try { 140 | fn(resolve, reject); 141 | } catch(error) { 142 | reject(error); 143 | } 144 | } 145 | 146 | // test 147 | let p = new MyPromise((resolve, reject) => { 148 | resolve('hello'); 149 | }) 150 | p.then(res => { 151 | console.log(res); // hello 152 | }) 153 | ``` 154 | 155 | ### 实现 Promise.all() 156 | 157 | ```javascript 158 | function isPromise(obj) { 159 | return !!obj && (typeof obj === 'function' || typeof obj === 'object') && typeof obj.then == 'function'; 160 | } 161 | 162 | function myPromiseAll(arr) { 163 | let res = [] 164 | return new Promise((resolve, reject) => { 165 | for (let i = 0; i < arr.length; i++) { 166 | if (isPromise(arr[i])) { 167 | arr[i].then(data => { 168 | res[i] = data; 169 | if (res.length === arr.length) { 170 | resolve(res) 171 | } 172 | }).catch(error => { 173 | reject(error) 174 | }) 175 | } else { 176 | res[i] = arr[i]; 177 | } 178 | } 179 | }) 180 | } 181 | ``` 182 | 183 | ### 实现 Promise.race() 184 | 185 | ```javascript 186 | function myPromiseRace(arr) { 187 | return new Promise((resolve, reject) => { 188 | for (let i = 0; i < arr.length; i++) { 189 | return arr[i].then(resolve, reject) 190 | } 191 | }) 192 | } 193 | ``` 194 | 195 | ### 实现 EventEmitter 订阅监听模式 196 | 197 | ```javascript 198 | class EventEmitter { 199 | constructor() { 200 | this.events = {}; 201 | } 202 | on (eventName, callback) { 203 | if(!this.events[eventName]) { 204 | this.events[eventName] = [callback]; 205 | } else { 206 | this.events[eventName].push(callback); 207 | } 208 | } 209 | 210 | emit(eventName, ...args) { 211 | this.events[eventName].forEach(fn => fn.apply(this, args)); 212 | } 213 | 214 | once(eventName, callback) { 215 | const fn = () => { 216 | callback(); 217 | this.remove(eventName, fn); 218 | } 219 | this.on(eventName, fn) 220 | } 221 | 222 | remove(eventName, callback) { 223 | this.events[eventName] = this.events[eventName].filter(fn => fn != callback); 224 | } 225 | } 226 | ``` 227 | 228 | ### setTimout 实现 setInterval 229 | 230 | ```javascript 231 | function myInterval(fn, time) { 232 | let context = this; 233 | setTimeout(() => { 234 | fn.call(context); 235 | myInterval(fn, time); 236 | }, time); 237 | } 238 | ``` 239 | 240 | ### 深拷贝 241 | 242 | ```javascript 243 | function deepClone(obj) { 244 | // 先判断是对象还是数组 245 | let copy = obj instanceof Array ? [] : {}; 246 | for (let key in obj) { 247 | // 判断是否是对象上的属性,而不是原型上的属性 248 | if (obj.hasOwnProperty(key)) { 249 | // obj[key] 是否是对象,如果是对象,递归遍历 250 | copy[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]; 251 | } 252 | } 253 | return copy; 254 | } 255 | 256 | // test 257 | console.log(deepClone({name: 'jack', birth: {year: '1997', month: '10'}})) // {name: 'jack', birth: {…}} 258 | ``` 259 | 260 | ### 数组拍平 261 | 262 | ```javascript 263 | var flatten = function(arr) { 264 | let res = []; 265 | for (let i = 0; i < arr.length; i++) { 266 | if (Array.isArray(arr[i])) { 267 | res = res.concat(flatten(arr[i])) 268 | } else { 269 | res.push(arr[i]) 270 | } 271 | } 272 | return res; 273 | } 274 | 275 | console.log(flatten([1,[1,2,[2,4]],3,5])); // [1, 1, 2, 2, 4, 3, 5] 276 | ``` 277 | 278 | ### 函数防抖 279 | 280 | ```javascript 281 | function debounce(fn, wait) { 282 | let timeout = null; 283 | return function() { 284 | let context = this; 285 | let args = arguments; 286 | if (timeout) clearTimeout(timeout); 287 | timeout = setTimeout(() => { 288 | fn.apply(context, args); 289 | }, wait); 290 | } 291 | } 292 | ``` 293 | 294 | ### 函数节流 295 | 296 | ```javascript 297 | function throttle(fn, wait) { 298 | let pre = new Date(); 299 | return function() { 300 | let context = this; 301 | let args = arguments; 302 | let now = new Date(); 303 | if (now - pre >= wait) { 304 | fn.apply(context, args); 305 | pre = now; 306 | } 307 | } 308 | } 309 | ``` 310 | 311 | ### 函数柯里化(实现 sum(1,2)(3)()) 312 | 313 | ```javascript 314 | function sum(...args1) { 315 | let x = args1.reduce((prev, next) => {return prev+next;}) 316 | return function(...args2) { 317 | if (args2.length == 0) return x; 318 | let y = args2.reduce((prev, next) => {return prev+next;}) 319 | return sum(x+y) 320 | } 321 | } 322 | 323 | console.log(sum(1,2,2,5)(7)()) // 17 324 | ``` 325 | 326 | ### 快速排序 327 | 328 | ```javascript 329 | function quicksort(arr) { 330 | if (arr.length <= 1) return arr; 331 | let pivotIndex = arr.length >> 1; 332 | let pivot = arr.splice(pivotIndex, 1)[0]; 333 | let left = []; 334 | let right = []; 335 | for (let i = 0; i < arr.length; i++) { 336 | if (arr[i] <= pivot) { 337 | left.push(arr[i]); 338 | } else { 339 | right.push(arr[i]); 340 | } 341 | } 342 | return quicksort(left).concat(pivot, quicksort(right)); 343 | 344 | } 345 | 346 | console.log(quicksort([4,3,5,2,1,6])); // [1, 2, 3, 4, 5, 6] 347 | ``` 348 | 349 | ### 归并排序 350 | 351 | ```javascript 352 | function merge(left, right) { 353 | let res = []; 354 | while(left.length > 0 && right.length > 0) { 355 | if (left[0] < right[0]) { 356 | res.push(left.shift()); 357 | } else { 358 | res.push(right.shift()); 359 | } 360 | } 361 | return res.concat(left).concat(right); 362 | } 363 | 364 | function mergeSort(arr) { 365 | if (arr.length == 1) return arr; 366 | var middle = Math.floor(arr.length / 2); 367 | var left = arr.slice(0, middle); 368 | var right = arr.slice(middle); 369 | return merge(mergeSort(left), mergeSort(right)); 370 | } 371 | 372 | console.log(mergeSort([3,2,4,5,1,6])); // [1, 2, 3, 4, 5, 6] 373 | ``` 374 | 375 | ### 插入排序 376 | 377 | ```javascript 378 | function insertionSort(array) { 379 | for (let i = 1; i < array.length; i++) { 380 | let j = i - 1; 381 | const temp = array[i]; 382 | while (j >= 0 && array[j] > temp) { 383 | array[j + 1] = array[j]; 384 | j--; 385 | } 386 | array[j + 1] = temp; 387 | } 388 | return array; 389 | } 390 | ``` 391 | 392 | ### 选择排序 393 | 394 | ```javascript 395 | function selectionSort(array) { 396 | for (let i = 0; i < array.length; i++) { 397 | let minIndex = i; 398 | for (let j = i + 1; j < array.length; j++) { 399 | if (array[j] < array[minIndex]) { 400 | minIndex = j; 401 | } 402 | } 403 | const temp = array[i]; 404 | array[i] = array[minIndex]; 405 | array[minIndex] = temp; 406 | } 407 | return array; 408 | } 409 | ``` 410 | 411 | ### 希尔排序 412 | 413 | ```javascript 414 | function shellSort(array) { 415 | const len = array.length; 416 | let gap = Math.floor(len / 2); 417 | for (gap; gap > 0; gap = Math.floor(gap / 2)) { 418 | for (let i = gap; i < len; i++) { 419 | let j = i - gap; 420 | const temp = array[i]; 421 | while (j >= 0 && array[j] > temp) { 422 | array[j + gap] = array[j]; 423 | j -= gap; 424 | } 425 | array[j + gap] = temp; 426 | } 427 | } 428 | return array; 429 | } 430 | ``` 431 | 432 | ### 堆排序 433 | 434 | ```javascript 435 | function heapSort(array) { 436 | for (let i = Math.floor(array.length / 2 - 1); i >= 0; i--) { 437 | adjustHeap(array, i, array.length); 438 | } 439 | for (let j = array.length - 1; j > 0; j--) { 440 | const temp = array[0]; 441 | array[0] = array[j]; 442 | array[j] = temp; 443 | adjustHeap(array, 0, j); 444 | } 445 | return array; 446 | } 447 | 448 | function adjustHeap(array, i, length) { 449 | for (let j = 2 * i + 1; j < length; j = 2 * j + 1) { 450 | const temp = array[i]; 451 | if (j + 1 < length && array[j] < array[j + 1]) { 452 | j++; 453 | } 454 | if (temp < array[j]) { 455 | array[i] = array[j]; 456 | array[j] = temp; 457 | i = j; 458 | } else break; 459 | } 460 | } 461 | ``` 462 | 463 | ### 二分查找 464 | 465 | ```javascript 466 | function binarySearch(array, target) { 467 | let low = 0; 468 | let height = array.length - 1; 469 | while (low <= height) { 470 | const middle = Math.floor((low + height) / 2); 471 | if (target === array[middle]) return middle; 472 | else if (target > array[middle]) low = middle + 1; 473 | else height = middle - 1; 474 | } 475 | return -1; 476 | } 477 | ``` 478 | 479 | ### 数组去重 480 | 481 | ```javascript 482 | function unique(array) { 483 | for (let i = 0; i < array.length - 1; i++) { 484 | for (let j = i + 1; j < array.length; j++) { 485 | if (array[j] === array[i]) { 486 | array.splice(j, 1); 487 | j--; 488 | } 489 | } 490 | } 491 | return array; 492 | } 493 | ``` 494 | 495 | ### 去除字符串首尾空格 496 | 497 | ```javascript 498 | //正则表达式 499 | function myTrim1(str){ 500 | return str.replace(/^\s+|\s+$/g,'') 501 | } 502 | 503 | //不使用正则表达式 504 | function myTrim2(str){ 505 | let head = 0 506 | foot = str.length 507 | for(let i =0;i0;j--){ 512 | if(str[j]===' ')foot-- 513 | else break 514 | } 515 | return str.substr(head,foot-head) 516 | } 517 | ``` 518 | 519 | ### 最长递增子序列 520 | 521 | ```javascript 522 | function lis(array) { 523 | if (array.length === 0) return 0; 524 | const arr = new Array(array.length).fill(1); 525 | for (let i = 1; i < array.length; i++) { 526 | for (let j = 0; j < i; j++) { 527 | if (array[i] > array[j]) { 528 | arr[i] = Math.max(arr[i], arr[j] + 1); 529 | } 530 | } 531 | } 532 | let result = 1; 533 | for (let i = 0; i < arr.length; i++) { 534 | result = Math.max(result, arr[i]); 535 | } 536 | return result; 537 | } 538 | ``` 539 | -------------------------------------------------------------------------------- /src/apply.js: -------------------------------------------------------------------------------- 1 | Function.prototype.myApply = function (context, args) { 2 | context.fn = this; 3 | let res; 4 | if (!args){ 5 | res = context.fn(); 6 | } else { 7 | res = context.fn(...args) 8 | } 9 | return res; 10 | } 11 | 12 | // test 13 | let obj = { 14 | name: 'jack' 15 | } 16 | function test(arg1, arg2, arg3) { 17 | console.log(this.name) // jack 18 | console.log(arg1, arg2, arg3); // 1 2 3 19 | } 20 | test.myApply(obj, [1,2,3]); -------------------------------------------------------------------------------- /src/array2Object.js: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { 3 | id: 1, 4 | parent: 2, 5 | }, 6 | { 7 | id: 2, 8 | parent: null, 9 | }, 10 | { 11 | id: 3, 12 | parent: 2, 13 | }, 14 | { 15 | id: 4, 16 | parent: 1, 17 | }, 18 | { 19 | id: 5, 20 | parent: 2, 21 | }, 22 | { 23 | id: 6, 24 | parent: 4, 25 | }, 26 | { 27 | id: 7, 28 | parent: 3, 29 | }, 30 | { 31 | id: 8, 32 | parent: 3, 33 | }, 34 | ]; 35 | 36 | /** 37 | * 将上面的数组转换为对象 38 | * @param {*} arr 39 | * @returns Object 40 | */ 41 | const array2Object = arr => { 42 | const findParent = node => { 43 | const [parent] = arr.filter(item => item.id === node.parent); 44 | return parent; 45 | }; 46 | const [root] = arr.filter(item => item.parent == null); 47 | const childNodes = arr.filter(item => item.parent != null); 48 | for (let item of childNodes) { 49 | const parent = findParent(item); 50 | if (!parent.children) { 51 | parent.children = [item]; 52 | } else { 53 | parent.children.push(item); 54 | } 55 | } 56 | return root; 57 | }; 58 | 59 | /** 60 | * 用 Map 优化事件复杂度 61 | * @param {*} arr 62 | */ 63 | const array2Object2 = arr => { 64 | const map = arr.reduce((prev, next) => Object.assign(prev, { [next.id]: next }), {}); 65 | const [root] = arr.filter(item => item.parent == null); 66 | const childNodes = arr.filter(item => item.parent != null); 67 | 68 | for (let item of childNodes) { 69 | const parent = map[item.parent]; 70 | if (!parent.children) { 71 | parent.children = [item]; 72 | } else { 73 | parent.children.push(item); 74 | } 75 | } 76 | return root; 77 | }; 78 | 79 | console.log(array2Object2(data)); 80 | -------------------------------------------------------------------------------- /src/before.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 传入任意一个函数,只能调用指定的次数 3 | * @param {*} count 调用次数 4 | * @param {*} func 传入函数 5 | * @returns 6 | */ 7 | function before(count, func) { 8 | var temp = count; 9 | return function() { 10 | if (temp > 0) { 11 | temp--; 12 | const args = [...arguments]; 13 | func.apply(this, args); 14 | } 15 | } 16 | } 17 | 18 | const log = a => console.log(a); 19 | 20 | const log3 = before(3, log); 21 | 22 | log3(2); 23 | log3(1); 24 | log3(3); 25 | -------------------------------------------------------------------------------- /src/binarySearch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 二分查找 3 | * 4 | * @param {(number | string)[]} array 5 | * @param {number | string} target 6 | * @returns {number} 7 | */ 8 | function binarySearch(array, target) { 9 | let low = 0; 10 | let height = array.length - 1; 11 | while (low <= height) { 12 | const middle = Math.floor((low + height) / 2); 13 | if (target === array[middle]) return middle; 14 | else if (target > array[middle]) low = middle + 1; 15 | else height = middle - 1; 16 | } 17 | return -1; 18 | } 19 | -------------------------------------------------------------------------------- /src/bind.js: -------------------------------------------------------------------------------- 1 | Function.prototype.bindNew = function (context, ...args) { 2 | return (...newArgs) => this.apply(context, [...args, ...newArgs]); 3 | }; 4 | 5 | // test 6 | const test = { 7 | name: "fy", 8 | showName: function (last: string) { 9 | console.log(this.name + " is " + last); 10 | }, 11 | }; 12 | test.showName("handsome"); // fy is handsome 13 | test.showName.bind({ name: "Mr.fy" })("handsome"); 14 | test.showName.bindNew({ name: "Mr.fy" })("handsome"); 15 | -------------------------------------------------------------------------------- /src/call.js: -------------------------------------------------------------------------------- 1 | Function.prototype.myCall = function (context, ...rest) { 2 | context.fn = this; 3 | var result = context.fn(...rest); 4 | delete context.fn; 5 | return result; 6 | } 7 | 8 | // test 9 | let obj = { 10 | name: 'jack' 11 | } 12 | function test(arg1, arg2, arg3) { 13 | console.log(this.name) // jack 14 | console.log(arg1, arg2, arg3); // 1 2 3 15 | } 16 | test.myCall(obj, 1,2,3); -------------------------------------------------------------------------------- /src/camel.js: -------------------------------------------------------------------------------- 1 | const underline2Camel = str => str.replace(/_(\w)/g, (p1, p2) => p2.toUpperCase()); 2 | 3 | const camel2Underline = str => str.replace(/([A-Z])/g, p1 => `_${p1.toLowerCase()}`); 4 | 5 | console.log(underline2Camel('hello_world')); 6 | console.log(camel2Underline('HelloWorld')); 7 | -------------------------------------------------------------------------------- /src/city.js: -------------------------------------------------------------------------------- 1 | const cityData = [ 2 | { 3 | id: '1', 4 | name: '广东省', 5 | children: [ 6 | { 7 | id: '11', 8 | name: '深圳市', 9 | children: [ 10 | { 11 | id: '111', 12 | name: '南山区', 13 | }, 14 | { 15 | id: '112', 16 | name: '福田区', 17 | children: [ 18 | { 19 | id: '1121', 20 | name: 'A街道', 21 | }, 22 | ], 23 | }, 24 | { 25 | id: '113', 26 | name: '福田区', 27 | children: [ 28 | { 29 | id: '1131', 30 | name: 'A街道', 31 | }, 32 | ], 33 | }, 34 | ], 35 | }, 36 | { 37 | id: '12', 38 | name: '东莞市', 39 | children: [ 40 | { 41 | id: '121', 42 | name: 'A区', 43 | }, 44 | { 45 | id: '122', 46 | name: 'B区', 47 | }, 48 | ], 49 | }, 50 | ], 51 | }, 52 | { 53 | id: '2', 54 | name: '湖北省', 55 | children: [ 56 | { 57 | id: '21', 58 | name: '武汉市', 59 | children: [ 60 | { 61 | id: '211', 62 | name: '洪山区', 63 | }, 64 | { 65 | id: '212', 66 | name: '江夏区', 67 | children: [ 68 | { 69 | id: '2121', 70 | name: 'A街道', 71 | }, 72 | ], 73 | }, 74 | ], 75 | }, 76 | { 77 | id: '22', 78 | name: '鄂州市', 79 | children: [ 80 | { 81 | id: '221', 82 | name: 'A区', 83 | }, 84 | { 85 | id: '222', 86 | name: 'B区', 87 | }, 88 | ], 89 | }, 90 | ], 91 | }, 92 | ]; 93 | 94 | function findFather(cities, id) { 95 | const stack = []; 96 | const findNode = (cities, id) => { 97 | let targetNode; 98 | for (let city of cities) { 99 | const { id: cityId, children } = city; 100 | stack.push(city); 101 | if (id === cityId) { 102 | return city; 103 | } else if (children) { 104 | targetNode = findNode(children, id); 105 | } 106 | stack.pop(); 107 | } 108 | return targetNode; 109 | }; 110 | findNode(cities, id); 111 | console.log(stack); 112 | } 113 | 114 | findFather(cityData, '2'); 115 | -------------------------------------------------------------------------------- /src/compose.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 接收若干个函数作为参数,每个函数执行后的输出作为下一个函数的输入。 3 | * 执行方向是自右向左的,初始函数的参数在最右边。 4 | * @param {...any} fns 5 | * @returns 6 | */ 7 | function compose(...fns) { 8 | return function(x) { 9 | return fns.reverse().reduce((arg, fn) => { 10 | return fn(arg); 11 | }, x); 12 | } 13 | } 14 | 15 | const add = x => x + 1; 16 | const multiply = x => x * 2; 17 | const minus = x => x - 1; 18 | 19 | console.log(compose(minus, multiply, add)(1)) // 3 -------------------------------------------------------------------------------- /src/curry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 通用的 curry 化 3 | * @param {*} targetfn 4 | * @returns 5 | */ 6 | function curry(targetfn) { 7 | var numOfArgs = targetfn.length; 8 | return function fn() { 9 | if (arguments.length < numOfArgs) { 10 | return fn.bind(null, ...arguments); 11 | } else { 12 | return targetfn.apply(null, arguments); 13 | } 14 | } 15 | } 16 | 17 | function sum(a, b, c) { 18 | return a + b + c; 19 | } 20 | 21 | const curried = curry(sum); 22 | console.log(curried(1)(2)(3)) // 6 23 | console.log(curried(1,2)(3)) // 6 24 | 25 | function sum2(...args1) { 26 | let x = args1.reduce((prev, next) => {return prev+next;}) 27 | return function(...args2) { 28 | if (args2.length == 0) return x; 29 | let y = args2.reduce((prev, next) => {return prev+next;}) 30 | return sum2(x+y) 31 | } 32 | } 33 | 34 | console.log(sum2(1,2,2,5)(7)()) // 17 35 | -------------------------------------------------------------------------------- /src/debounce.js: -------------------------------------------------------------------------------- 1 | function debounce(fn, wait) { 2 | let timeout = null; 3 | return function() { 4 | let context = this; 5 | let args = arguments; 6 | if (timeout) clearTimeout(timeout); 7 | timeout = setTimeout(() => { 8 | fn.apply(context, args); 9 | }, wait); 10 | } 11 | } -------------------------------------------------------------------------------- /src/deepclone.js: -------------------------------------------------------------------------------- 1 | function deepClone(obj) { 2 | // 先判断是对象还是数组 3 | let copy = obj instanceof Array ? [] : {}; 4 | for (let key in obj) { 5 | // 判断是否是对象上的属性,而不是原型上的属性 6 | if (obj.hasOwnProperty(key)) { 7 | // obj[key] 是否是对象,如果是对象,递归遍历 8 | copy[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]; 9 | } 10 | } 11 | return copy; 12 | } 13 | 14 | // test 15 | // console.log(deepClone({name: 'jack', birth: {year: '1997', month: '10'}})) // {name: 'jack', birth: {…}} 16 | 17 | module.exports = deepClone; -------------------------------------------------------------------------------- /src/flatten.js: -------------------------------------------------------------------------------- 1 | var flatten = function(arr) { 2 | let res = []; 3 | for (let i = 0; i < arr.length; i++) { 4 | if (Array.isArray(arr[i])) { 5 | res = res.concat(flatten(arr[i])) 6 | } else { 7 | res.push(arr[i]) 8 | } 9 | } 10 | return res; 11 | } 12 | 13 | console.log(flatten([1,[1,2,[2,4]],3,5])); // [1, 1, 2, 2, 4, 3, 5] -------------------------------------------------------------------------------- /src/heapSort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 堆排序 3 | * 4 | * @param {(number | string)[]} array 5 | * @returns {(number | string)[]} 6 | */ 7 | function heapSort(array) { 8 | // 初始化大顶堆,从第一个非叶子结点开始 9 | for (let i = Math.floor(array.length / 2 - 1); i >= 0; i--) { 10 | adjustHeap(array, i, array.length); 11 | } 12 | // 排序,每一次for循环找出一个当前最大值,数组长度减一 13 | for (let j = array.length - 1; j > 0; j--) { 14 | // 根节点与最后一个节点交换 15 | const temp = array[0]; 16 | array[0] = array[j]; 17 | array[j] = temp; 18 | adjustHeap(array, 0, j); 19 | } 20 | return array; 21 | } 22 | 23 | /** 24 | * 将 i 结点以下的堆整理为大顶堆 25 | * 26 | * @param {(number | string)[]} array 27 | * @param {number} i 28 | * @param {number} length 29 | */ 30 | function adjustHeap(array, i, length) { 31 | for (let j = 2 * i + 1; j < length; j = 2 * j + 1) { 32 | const temp = array[i]; 33 | // 找到两个孩子中较大的一个,再与父节点比较 34 | if (j + 1 < length && array[j] < array[j + 1]) { 35 | j++; 36 | } 37 | // 如果父节点小于子节点则交换;否则跳出 38 | if (temp < array[j]) { 39 | array[i] = array[j]; 40 | array[j] = temp; 41 | // 交换后,temp 的下标变为 j 42 | i = j; 43 | } else break; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/insertionSort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 插入排序 3 | * 4 | * @param {(number | string)[]} array 5 | * @returns {(number | string)[]} 6 | */ 7 | function insertionSort(array) { 8 | for (let i = 1; i < array.length; i++) { 9 | let j = i - 1; 10 | const temp = array[i]; 11 | while (j >= 0 && array[j] > temp) { 12 | array[j + 1] = array[j]; 13 | j--; 14 | } 15 | array[j + 1] = temp; 16 | } 17 | return array; 18 | } 19 | -------------------------------------------------------------------------------- /src/instanceof.js: -------------------------------------------------------------------------------- 1 | function myInstanceOf(left, right) { 2 | let prototype = right.prototype; 3 | left = left.__proto__; 4 | while(true) { 5 | if (!left) return false; 6 | if (left == prototype) return true; 7 | left = left.__proto__; 8 | } 9 | } 10 | 11 | console.log(myInstanceOf([], Array)); // true -------------------------------------------------------------------------------- /src/interval.js: -------------------------------------------------------------------------------- 1 | function myInterval(fn, time) { 2 | let context = this; 3 | setTimeout(() => { 4 | fn.call(context); 5 | myInterval(fn, time); 6 | }, time); 7 | } -------------------------------------------------------------------------------- /src/jsonp.js: -------------------------------------------------------------------------------- 1 | var newscript = document.createElement('script'); 2 | newscript.src = 'https://www.adb.com?callback=fn' 3 | document.body.appendChild(newscript); 4 | function fn(data) { 5 | console.log(data); 6 | } 7 | -------------------------------------------------------------------------------- /src/lis.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 最长递增子序列 3 | * 4 | * @param {number[]} array 5 | * @returns {number[]} 6 | */ 7 | function lis(array) { 8 | if (array.length === 0) return 0; 9 | const arr = new Array(array.length).fill(1); 10 | for (let i = 1; i < array.length; i++) { 11 | for (let j = 0; j < i; j++) { 12 | if (array[i] > array[j]) { 13 | arr[i] = Math.max(arr[i], arr[j] + 1); 14 | } 15 | } 16 | } 17 | let result = 1; 18 | for (let i = 0; i < arr.length; i++) { 19 | result = Math.max(result, arr[i]); 20 | } 21 | return result; 22 | } 23 | -------------------------------------------------------------------------------- /src/longestCommonPrefix.js: -------------------------------------------------------------------------------- 1 | function longestPrefix(arr) { 2 | if (arr.length === 0) { 3 | return ''; 4 | } 5 | let prefix = arr[0]; 6 | for (let i = 1; i < arr.length; i++) { 7 | while (arr[i].indexOf(prefix) !== 0) { 8 | prefix = prefix.substring(0, prefix.length - 1); 9 | if (prefix.length === 0) { 10 | return ''; 11 | } 12 | } 13 | } 14 | return prefix; 15 | } 16 | 17 | // test 18 | let strs = ['helly', 'hellow', 'hell']; 19 | 20 | console.log(longestPrefix(strs)); // hell 21 | -------------------------------------------------------------------------------- /src/mergeSort.js: -------------------------------------------------------------------------------- 1 | function merge(left, right) { 2 | let res = []; 3 | while(left.length > 0 && right.length > 0) { 4 | if (left[0] < right[0]) { 5 | res.push(left.shift()); 6 | } else { 7 | res.push(right.shift()); 8 | } 9 | } 10 | return res.concat(left).concat(right); 11 | } 12 | 13 | function mergeSort(arr) { 14 | if (arr.length == 1) return arr; 15 | var middle = Math.floor(arr.length / 2); 16 | var left = arr.slice(0, middle); 17 | var right = arr.slice(middle); 18 | return merge(mergeSort(left), mergeSort(right)); 19 | } 20 | 21 | console.log(mergeSort([3,2,4,5,1,6])); // [1, 2, 3, 4, 5, 6] -------------------------------------------------------------------------------- /src/missingNumber.js: -------------------------------------------------------------------------------- 1 | const missingNumber = (arr) => { 2 | console.log('原数组:', arr); 3 | let index = 1; 4 | let ans = []; 5 | for (let i = 0; i < arr.length; i++) { 6 | if (index !== arr[i]) { 7 | ans.push(index); 8 | arr.splice(i, 0, 0); 9 | } 10 | index++; 11 | } 12 | return ans; 13 | }; 14 | 15 | 16 | const generateArray = limit => { 17 | let arr = []; 18 | for (let i = 1; i <= limit; i++) { 19 | arr.push(i); 20 | } 21 | // 随机删除两个数 22 | for (let i = 1; i <= 2; i++) { 23 | const rand = Math.round(Math.random() * limit); 24 | arr.splice(rand, 1); 25 | } 26 | return arr; 27 | }; 28 | 29 | const arr = generateArray(10); 30 | console.log('缺失的数:', missingNumber(arr)) 31 | -------------------------------------------------------------------------------- /src/new.js: -------------------------------------------------------------------------------- 1 | function myNew (fun, ...args) { 2 | let obj = {}; 3 | obj.__proto__ = fun.prototype; 4 | let res = fun.apply(obj, args); 5 | return res instanceof Object ? res : obj; 6 | } 7 | 8 | function Animal(name) { 9 | this.name = name; 10 | } 11 | 12 | let animal = myNew(Animal, 'dog'); 13 | console.log(animal.name) // dog -------------------------------------------------------------------------------- /src/pi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 快手面试手写 3 | * 计算圆周率,割圆迭代 4 | * 圆的半径为 1,从 6 边形状开始迭代,则六边形的边长也为 1 5 | * 6 -> 12 -> 24 -> 48 6 | */ 7 | function pi(iteration) { 8 | let side = 6; 9 | let length = 1; 10 | for (let i = 1; i <= iteration; i++) { 11 | side *= 2; 12 | length = Math.sqrt(2 - 2 * Math.sqrt(1 - Math.pow(length / 2, 2))); 13 | } 14 | return (side * length) / 2; 15 | } 16 | 17 | console.log(pi(12)); // 迭代12 次: 3.1415926453212157 18 | -------------------------------------------------------------------------------- /src/promise.js: -------------------------------------------------------------------------------- 1 | const PENDING = Symbol(); 2 | const REJECTED = Symbol(); 3 | const FULLFILLED = Symbol(); 4 | 5 | const MyPromise = function(fn) { 6 | this.state = PENDING; 7 | this.value = ''; 8 | 9 | const resolve = (value) => { 10 | this.state = FULLFILLED; 11 | this.value = value; 12 | } 13 | 14 | const reject = (error) => { 15 | this.state = REJECTED; 16 | this.value = error; 17 | } 18 | 19 | this.then = (onFullFill, onReject) => { 20 | if (this.state == FULLFILLED) { 21 | onFullFill(this.value); 22 | } else { 23 | onReject(this.value); 24 | } 25 | } 26 | 27 | try { 28 | fn(resolve, reject); 29 | } catch(error) { 30 | reject(error); 31 | } 32 | } 33 | 34 | // test 35 | let p = new MyPromise((resolve, reject) => { 36 | resolve('hello'); 37 | }) 38 | p.then(res => { 39 | console.log(res); // hello 40 | }) -------------------------------------------------------------------------------- /src/promiseAll.js: -------------------------------------------------------------------------------- 1 | function isPromise(obj) { 2 | return ( 3 | !!obj && (typeof obj === 'function' || typeof obj === 'object') && typeof obj.then == 'function' 4 | ); 5 | } 6 | 7 | function isPromise(object) { 8 | return ( 9 | !!object && (typeof obj === 'function' || typeof obj === 'object') && object instanceof Promise 10 | ); 11 | } 12 | function PromiseAll(arr) { 13 | return new Promise((resolve, reject) => { 14 | const len = arr.length, 15 | result = []; 16 | let succeed = 0; 17 | for (let i = 0; i < len; ++i) { 18 | const cur = arr[i]; 19 | if (isPromise(cur)) { 20 | arr[i].then((res) => { 21 | process(i, res); 22 | }, reject); 23 | } else { 24 | process(i, cur); 25 | } 26 | } 27 | function process(index, value) { 28 | result[index] = value; 29 | if (++succeed === len) { 30 | resolve(result); 31 | } 32 | } 33 | }); 34 | } 35 | 36 | function myPromiseAll(arr) { 37 | let res = []; 38 | let containPromise = false; 39 | return new Promise((resolve, reject) => { 40 | for (let i = 0; i < arr.length; i++) { 41 | if (isPromise(arr[i])) { 42 | containPromise = true; 43 | arr[i] 44 | .then((data) => { 45 | res[i] = data; 46 | if (res.length === arr.length) { 47 | resolve(res); 48 | } 49 | }) 50 | .catch((error) => { 51 | reject(error); 52 | }); 53 | } else { 54 | res[i] = arr[i]; 55 | } 56 | } 57 | if (!containPromise) resolve(res); 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /src/promiseRace.js: -------------------------------------------------------------------------------- 1 | function myPromiseRace(arr) { 2 | return new Promise((resolve, reject) => { 3 | for (let i = 0; i < arr.length; i++) { 4 | return arr[i].then(resolve, reject) 5 | } 6 | }) 7 | } -------------------------------------------------------------------------------- /src/proxy.js: -------------------------------------------------------------------------------- 1 | const deepClone = require('./deepclone'); 2 | 3 | function MyProxy(obj, handler) { 4 | console.log(handler); 5 | let _target = deepClone(obj); 6 | Object.keys(_target).forEach(key => { 7 | Object.defineProperty(_target, key, { 8 | get: () => handler.get && handler.get(obj, key), 9 | set: newVal => handler.set && handler.set(obj, key, newVal), 10 | }); 11 | }); 12 | return _target; 13 | } 14 | 15 | let person = { 16 | name: 'jack', 17 | city: 'Beijing', 18 | }; 19 | 20 | let proxy = new MyProxy(person, { 21 | get: (target, propKey) => target[propKey], 22 | set: (target, propKey, value) => { 23 | target[propKey] = value; 24 | }, 25 | }); 26 | 27 | // test 28 | console.log(proxy.name); // jack 29 | proxy.city = 'Nanjing'; 30 | console.log(proxy.city); // Nanjing 31 | console.log(person); // { name: 'jack', city: 'Nanjing' } 32 | -------------------------------------------------------------------------------- /src/quickSort.js: -------------------------------------------------------------------------------- 1 | function quicksort(arr) { 2 | if (arr.length <= 1) return arr; 3 | let pivotIndex = arr.length >> 1; 4 | let pivot = arr.splice(pivotIndex, 1)[0]; 5 | let left = []; 6 | let right = []; 7 | for (let i = 0; i < arr.length; i++) { 8 | if (arr[i] <= pivot) { 9 | left.push(arr[i]); 10 | } else { 11 | right.push(arr[i]); 12 | } 13 | } 14 | return quicksort(left).concat(pivot, quicksort(right)); 15 | 16 | } 17 | 18 | console.log(quicksort([4,3,5,2,1,6])); // [1, 2, 3, 4, 5, 6] -------------------------------------------------------------------------------- /src/selectionSort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 选择排序 3 | * 4 | * @param {(number | string)[]} array 5 | * @returns {(number | string)[]} 6 | */ 7 | function selectionSort(array) { 8 | for (let i = 0; i < array.length; i++) { 9 | let minIndex = i; 10 | for (let j = i + 1; j < array.length; j++) { 11 | if (array[j] < array[minIndex]) { 12 | minIndex = j; 13 | } 14 | } 15 | const temp = array[i]; 16 | array[i] = array[minIndex]; 17 | array[minIndex] = temp; 18 | } 19 | return array; 20 | } 21 | -------------------------------------------------------------------------------- /src/shellSort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 希尔排序 3 | * 4 | * @param {(number | string)[]} array 5 | * @returns {(number | string)[]} 6 | */ 7 | function shellSort(array) { 8 | const len = array.length; 9 | let gap = Math.floor(len / 2); 10 | for (gap; gap > 0; gap = Math.floor(gap / 2)) { 11 | for (let i = gap; i < len; i++) { 12 | let j = i - gap; 13 | const temp = array[i]; 14 | while (j >= 0 && array[j] > temp) { 15 | array[j + gap] = array[j]; 16 | j -= gap; 17 | } 18 | array[j + gap] = temp; 19 | } 20 | } 21 | return array; 22 | } 23 | -------------------------------------------------------------------------------- /src/subscribe.js: -------------------------------------------------------------------------------- 1 | class EventEmitter { 2 | constructor() { 3 | this.events = {}; 4 | } 5 | on (eventName, callback) { 6 | if(!this.events[eventName]) { 7 | this.events[eventName] = [callback]; 8 | } else { 9 | this.events[eventName].push(callback); 10 | } 11 | } 12 | 13 | emit(eventName, ...args) { 14 | this.events[eventName].forEach(fn => fn.apply(this, args)); 15 | } 16 | 17 | once(eventName, callback) { 18 | const fn = () => { 19 | callback(); 20 | this.remove(eventName, fn); 21 | } 22 | this.on(eventName, fn) 23 | } 24 | 25 | remove(eventName, callback) { 26 | this.events[eventName] = this.events[eventName].filter(fn => fn != callback); 27 | } 28 | } -------------------------------------------------------------------------------- /src/throttle.js: -------------------------------------------------------------------------------- 1 | function throttle(fn, wait) { 2 | let pre = new Date(); 3 | return function() { 4 | let context = this; 5 | let args = arguments; 6 | let now = new Date(); 7 | if (now - pre >= wait) { 8 | fn.apply(context, args); 9 | pre = now; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/trim.js: -------------------------------------------------------------------------------- 1 | //正则表达式 2 | function myTrim1(str){ 3 | return str.replace(/^\s+|\s+$/g,'') 4 | } 5 | 6 | //不使用正则表达式 7 | function myTrim2(str){ 8 | let head = 0 9 | foot = str.length 10 | for(let i =0;i0;j--){ 15 | if(str[j]===' ')foot-- 16 | else break 17 | } 18 | return str.substr(head,foot-head) 19 | } -------------------------------------------------------------------------------- /src/unique.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 数组去重 3 | * 4 | * @param {(number | string)[]} array 5 | * @returns {(number | string)[]} 6 | */ 7 | function unique(array) { 8 | for (let i = 0; i < array.length - 1; i++) { 9 | for (let j = i + 1; j < array.length; j++) { 10 | if (array[j] === array[i]) { 11 | array.splice(j, 1); 12 | j--; 13 | } 14 | } 15 | } 16 | return array; 17 | } 18 | --------------------------------------------------------------------------------