在这里汇总一些 JS 常用技术的源码实现,通过源码了解其内部原理,可以加深对其的理解。
Function.prototype.call() call()
方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数。
语法:fun.call(thisArg, arg1, arg2, ...)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 const getGlobal = function ( ) { if (typeof self !== "undefined" ) return self; if (typeof window !== "undefined" ) return window ; if (typeof global !== "undefined" ) return global; }; Function .prototype.mcall = function (context ) { context = context ? Object (context) : getGlobal(); const fn = Symbol ("anything" ); context[fn] = this ; let args = [...arguments].slice(1 ); let r = context[fn](...args); delete context[fn]; return r; }; function Product (name, price ) { this .name = name; this .price = price; } function Fruit (name, price ) { Product.mcall(this , name, price); this .category = "fruit" ; } var apple = new Fruit("apple" , 5 );console .log(apple);
Function.prototype.apply() apply()
调用一个指定 this 值的函数, 接收作为一个数组或者类数组对象提供的参数.
语法: func.apply(thisArg, [argsArray])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 const getGlobal = function ( ) { if (typeof self !== "undefined" ) return self; if (typeof window !== "undefined" ) return window ; if (typeof global !== "undefined" ) return global; }; Function .prototype.apply = function (context, arr ) { context = context ? Object (context) : getGlobal(); console .log("context" , context); const fn = Symbol ("anything" ); context[fn] = this ; if (!arr) { let r = context[fn](); delete context[fn]; return r; } let r = context[fn](...arr); delete context[fn]; return r; }; var array = ["a" , "b" ];var elements = [0 , 1 , 2 ];array.push.apply(array, elements); console .log("array" , array); function A (x, y ) { console .log(this .b + " " + x + " " + y); } var B = { b : 1 };A.apply(B, [2 , 3 ]);
Function.prototype.bind() bind()
方法创建一个新函数, 在调用时设置 this 关键字为提供的值。
语法:fun.bind(thisArg, arg1, arg2, ...)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const getGlobal = function ( ) { if (typeof self !== "undefined" ) return self; if (typeof window !== "undefined" ) return window ; if (typeof global !== "undefined" ) return global; }; Function .prototype.bind = function (context ) { context = context ? Object (context) : getGlobal(); const me = this ; const args = [...arguments].slice(1 ); return function ( ) { me.apply(context, [...args, ...arguments]); }; }; function A (x, y ) { console .log(this .b + ", " + x + ", " + y); } var B = { b : 1 };let fnc = A.bind(B, 2 ); fnc(3 );
new 的原理 在了解 new 原理之前先看看 js 的内部机制图
我们需要知道当 new 的时候做了什么事情
创建一个新对象;
将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
执行构造函数中的代码(为这个新对象添加属性)
返回新对象。
new 没法重写,这里是有 mockNew 函数来模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 function mockNew ( ) { const obj = {}; const constructor = [].shift.call(arguments); //实现继承,实例可以访问构造器熟悉 obj.__proto__ = constructor .prototype; //调用构造器,改变this指向为实例 const res = constructor .apply(obj, arguments); //如果构造器函数返回值为对象,则返回这个对象,否则返回新的实例对象 return res instanceof Object ? res : obj; } /** * 使用例子 * @param {*} color * @param {*} name */ function Car(color, name) { this.color = color; return { name: name, }; } let car = mockNew(Car, "black", "BMW"); console.log(car.color); / / undefined console.log(car.name); / / "BMW"
reduce 实现原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 Array .prototype.mockReduce = function (callback ) { const length = this .length; let reducer = undefined , k = 0 , initVal = [...arguments].length > 1 ? [...arguments][1 ] : undefined ; if (typeof callback !== "function" ) { throw new TypeError (callback + " is not a function" ); } if (length === 0 && !initVal) { throw new TypeError ("Reduce of empty array with no initial value" ); } if (initVal) { reducer = initVal; } else { reducer = this [0 ]; k++; } while (k < length) { if (this .hasOwnProperty(k)) { const kValue = this [k]; reducer = callback(reducer, kValue); } k++; } return reducer; }; const rReduce = [].reduce((a, b ) => a + b, 3 );const mReduce = [].mockReduce((a, b ) => a + b);console .log(rReduce, mReduce);
双向绑定 defineProperty
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const data = { text: "hello" , }; const input = document .getElementById("input" );const p = document .getElementById("p" );Object .defineProperty(data, "text" , { set (newVal) { input.value = newVal; p.innerHTML = newVal; }, }); input.addEventListener("keyup" , function (e ) { data.text = e.target.value; });
proxy
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const data = { text: "default" , }; const input = document .getElementById("input" );const p = document .getElementById("p" );const handler = { set (target, key, value) { target[key] = value; input.value = value; p.innerHTML = value; return value; }, }; const proxy = new Proxy (data, handler);input.addEventListener("keyup" , function (e ) { proxy.text = e.target.value; });
继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function inheritPrototype (subType, superType ) { var protoType = Object .create(superType.prototype); protoType.constructor = subType; subType.prototype = protoType; } function SuperType (name ) { this .name = name; this .colors = ["red" , "blue" , "green" ]; } SuperType.prototype.sayName = function ( ) { alert(this .name); }; function SubType (name, age ) { SuperType.call(this , name); this .age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function ( ) { alert(this .age); }; var instance = new SubType("Bob" , 18 );instance.sayName(); instance.sayAge();
Object.create 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 if (typeof Object .create !== "function" ) { Object .create = function (prototype, properties ) { if (typeof prototype !== "object" ) { throw TypeError (); } function Ctor ( ) {} Ctor.prototype = prototype; var o = new Ctor(); if (prototype) { o.constructor = Ctor; } if (properties !== undefined ) { if (properties !== Object (properties)) { throw TypeError (); } Object .defineProperties(o, properties); } return o; }; }
instanceof 实现 原理: L 的 __proto__ 是不是等于 R.prototype,不等于再找 L.__proto__.__proto__ 直到 __proto__ 为 null
1 2 3 4 5 6 7 8 9 10 11 function instance_of (L, R ) { var O = R.prototype; L = L.__proto__; while (true ) { if (L === null ) return false ; if (O === L) return true ; L = L.__proto__; } }
Array.isArray 实现 1 2 3 4 5 Array .myIsArray = function (o ) { return Object .prototype.toString.call(Object (o)) === "[object Array]" ; }; console .log(Array .myIsArray([]));
getOwnPropertyNames 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if (typeof Object .getOwnPropertyNames !== "function" ) { Object .getOwnPropertyNames = function (o ) { if (o !== Object (o)) { throw TypeError ("Object.getOwnPropertyNames called on non-object" ); } var props = [], p; for (p in o) { if (Object .prototype.hasOwnProperty.call(o, p)) { props.push(p); } } return props; }; }
Promise 实现 实现原理:其实就是一个发布订阅者模式
构造函数接收一个 executor 函数,并会在 new Promise() 时立即执行该函数
then 时收集依赖,将回调函数收集到 成功/失败队列
executor 函数中调用 resolve/reject 函数
resolve/reject 函数被调用时会通知触发队列中的回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 const isFunction = variable => typeof variable === 'function' ;const PENDING = 'pending' ;const FULFILLED = 'fulfilled' ;const REJECTED = 'rejected' ;class MyPromise { constructor (handle: Function) { try { handle(this ._resolve, this ._reject); } catch (err) { this ._reject(err); } } private _status: string = PENDING; private _value: string | undefined = undefined ; private _rejectedQueues: any = []; private _fulfilledQueues: any = []; private _resolve = val => { const run = () => { if (this ._status !== PENDING) return ; this ._status = FULFILLED; const runFulfilled = value => { let cb; while ((cb = this ._fulfilledQueues.shift())) { cb(value); } }; const runRejected = error => { let cb; while ((cb = this ._rejectedQueues.shift())) { cb(error); } }; if (val instanceof MyPromise) { val.then( value => { this ._value = value; runFulfilled(value); }, err => { this ._value = err; runRejected(err); } ); } else { this ._value = val; runFulfilled(val); } }; setTimeout(run); }; private _reject = err => { if (this ._status !== PENDING) return ; const run = () => { this ._status = REJECTED; this ._value = err; let cb; while ((cb = this ._rejectedQueues.shift())) { cb(err); } }; setTimeout(run); }; then(onFulfilled?, onRejected?) { const { _value, _status } = this ; return new MyPromise((onFulfilledNext, onRejectedNext ) => { const fulfilled = value => { try { if (!isFunction(onFulfilled)) { onFulfilledNext(value); } else { const res = onFulfilled(value); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext); } else { onFulfilledNext(res); } } } catch (err) { onRejectedNext(err); } }; const rejected = error => { try { if (!isFunction(onRejected)) { onRejectedNext(error); } else { const res = onRejected(error); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext); } else { onFulfilledNext(res); } } } catch (err) { onRejectedNext(err); } }; switch (_status) { case PENDING: this ._fulfilledQueues.push(fulfilled); this ._rejectedQueues.push(rejected); break ; case FULFILLED: fulfilled(_value); break ; case REJECTED: rejected(_value); break ; } }); } catch (onRejected) { return this .then(undefined , onRejected); } finally (cb) { return this .then( value => MyPromise.resolve(cb()).then(() => value), reason => MyPromise.resolve(cb()).then(() => { throw reason; }) ); } static resolve(value) { if (value instanceof MyPromise) return value; return new MyPromise(resolve => resolve(value)); } static reject(value) { return new MyPromise((resolve, reject ) => reject(value)); } static all(list) { return new MyPromise((resolve, reject ) => { let values = []; let count = 0 ; for (let [i, p] of list.entries()) { this .resolve(p).then( res => { values[i] = res; count++; if (count === list.length) resolve(values); }, err => { reject(err); } ); } }); } static race(list) { return new MyPromise((resolve, reject ) => { for (let p of list) { this .resolve(p).then( res => { resolve(res); }, err => { reject(err); } ); } }); } }
防抖/节流 防抖函数 onscroll 结束时触发一次,延迟执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function debounce (callback, wait ) { let timeout; return function ( ) { let context = this ; let args = arguments ; if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { callback.apply(context, args); }, wait); }; } window .onscroll = debounce(function ( ) { console .log("debounce" ); }, 1000 );
节流函数 onscroll 时,每隔一段时间触发一次,像水滴一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function throttle (callback, delay ) { var prevTime = Date .now(); return function ( ) { var curTime = Date .now(); if (curTime - prevTime > delay) { callback.apply(this , arguments ); prevTime = curTime; } }; } window .onscroll = throttle(function ( ) { console .log("throtte" ); }, 1000 );
函数柯里化实现 其实我们无时无刻不在使用柯里化函数,只是没有将它总结出来而已。它的本质就是将一个参数很多的函数分解成单一参数的多个函数。
应用场景:
延迟计算 (用闭包把传入参数保存起来,当传入参数的数量足够执行函数时,开始执行函数)
动态创建函数 (参数不够时会返回接受剩下参数的函数)
参数复用(每个参数可以多次复用)
1 2 3 4 5 6 7 8 9 10 11 12 const curry = (fn ) => (judge = (...args ) => args.length >= fn.length ? fn(...args) : (...arg ) => judge(...args, ...arg)); const sum = (a, b, c, d ) => a + b + c + d;const currySum = curry(sum);currySum(1 )(2 )(3 )(4 ); currySum(1 , 2 )(3 )(4 ); currySum(1 )(2 , 3 )(4 );
实现简单深拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 function getType (obj ) { const str = Object .prototype.toString.call(obj); const map = { "[object Boolean]" : "boolean" , "[object Number]" : "number" , "[object String]" : "string" , "[object Function]" : "function" , "[object Array]" : "array" , "[object Date]" : "date" , "[object RegExp]" : "regExp" , "[object Undefined]" : "undefined" , "[object Null]" : "null" , "[object Object]" : "object" , }; if (obj instanceof Element) { return "element" ; } return map[str]; } function deepCopy (original ) { const type = getType(original); let copy; switch (type) { case "array" : return copyArray(original, copy); case "object" : return copyObject(original, copy); case "function" : return copyFunction(original, copy); default : return original; } } function copyArray (original, copy = [] ) { for (const [index, value] of original.entries()) { copy[index] = deepCopy(value); } return copy; } function copyObject (original, copy = {} ) { for (const [key, value] of Object .entries(original)) { copy[key] = deepCopy(value); } return copy; } function copyFunction (original, copy = ( ) => {}) { const fn = eval (original.toString()); fn.prototype = original.prototype; return fn; } const arr1 = [1 , 2 , [3 , 4 ], { i : 6 , j : 6 }, (k, m) => k + m];console .log(deepCopy(arr1));