多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] 1\. 数组扁平化 2\. 数组去重 3\. 类数组转化为数组 (类数组是具有 length 属性,但不具有数组原型上的方法) 4\. array.prototype.filter() 5\. Array.prototype.map() 6\. Array.prototype.forEach() 7\. Array.prototype.reduce() 8\. Apple() (function.prototype.apple()) 9\. call 10\. Bind() 11\. Debounce () 防抖 (频率高频时间后 n 秒内函数只会执行一次,如果 n 秒内高频时间再次触发,则重新计算时间) 12\. throttle 节流 () 13\. 函数珂里化 14\. 模拟 new 操作 15\. Instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某一个实例对象的原型链上 16\. 原型继承 17\. Object.is 18\. Object.assign () 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象 (它返回目标对象 这个操作是浅拷贝) 19\. 深拷贝 20\. promise 21\. Promise.all 22\. Promise.race 23\. Promise 并行限制 24\. JSONP 25\. AJAX 26\. event 模块 实现 node 中回调函数的机制,node 中回调函数其实是内部使用了观察者模式 27\. 图片懒加载 28\. 滚动加载 29\. 渲染几万条数据不卡住页面 (渲染大数据时,合理利用 createDocumentFragment 和 requestAnimationFrame,将操作切分为一小段一小段执行) 30\. 打印出当前页面使用了多少种 HTML 元素 31\. 将 virtualDom 转化为真实 DOM 结构 32\. 字符串解析问题 () # 深拷贝 简单版: ``` function deepCopy(obj){ var cloneObj; // 当输入数据为简单数据类型时直接复制 if (obj && typeof obj!=='object'){ return obj;} // 当输入数据为对象或数组时 else if (obj && typeof obj==='object'){ // 检测输入数据是数组还是对象 cloneObj = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)){ if(obj[key]&&typeof obj[key] === 'object') { // 若当前元素类型为对象时,递归调用 cloneObj[key] = deepCopy(obj[key]); } // 若当前元素类型为基本数据类型 else{ cloneObj[key] = obj[key]; } } } } return cloneObj; } ``` 第二种: ~~~js /** * Get the first item that pass the test * by second argument function * * @param {Array} list * @param {Function} f * @return {*} */ export function find (list, f) { return list.filter(f)[0] } /** * Deep copy the given object considering circular structure. * This function caches all nested objects and its copies. * If it detects circular structure, use cached copy to avoid infinite loop. * * @param {*} obj * @param {Array<Object>} cache * @return {*} */ export function deepCopy (obj, cache = []) { // just return if obj is immutable value if (obj === null || typeof obj !== 'object') { return obj } // if obj is hit, it is in circular structure const hit = find(cache, c => c.original === obj) if (hit) { return hit.copy } const copy = Array.isArray(obj) ? [] : {} // put the copy into cache at first // because we want to refer it in recursive deepCopy cache.push({ original: obj, copy }) Object.keys(obj).forEach(key => { copy[key] = deepCopy(obj[key], cache) }) return copy } ~~~ # 自定义柯里化函数: 实现 1: ``` function curry(func) { return function curried(...args) { console.log(args); if (args.length >= func.length) { return func.apply(this, args); } else { console.log('calling else'); return function(...args2) { return curried.apply(this, args.concat(args2)); } } }; } // 测试 function multiply(a, b, c) { return a * b * c; } let curried = curry(multiply); console.log(curried(1, 2, 3)); console.log(curried(1)(2, 3)); console.log(curried(1)(2)(3)); ``` 实现 2: ``` 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); // 10 currySum(1, 2)(3)(4); // 10 currySum(1)(2, 3)(4); // 10 ``` > http://zetcode.com/javascript/currying/ # call 实现 原理:在方法调用模式下,`this`总是指向调用它所在方法的对象,`this`的指向与所在方法的调用位置有关,而与方法的声明位置无关(箭头函数特殊)。 ``` const foo = { name: 'foo' }; foo.fn = function() { // 这里的 this 指向了 foo // 因为 foo 调用了 fn, // fn 的 this 就指向了调用它所在方法的对象 foo 上 console.log(this.name); // foo }; ``` 实现: ```js Function.prototype.mycall = function(thisArg) { // this指向调用call的对象 if (typeof this !== 'function') { // 调用call的若不是函数则报错 throw new TypeError('Error'); } const args = [...arguments].slice(1); thisArg = thisArg || window; // 将调用call函数的对象添加到 thisArg 的属性中 thisArg.fn = this; // 执行该属性 const result = thisArg.fn(...arg); // 删除该属性 delete thisArg.fn; // 返回函数执行结果 return result; }; ``` # 实现 new 方法 ``` function myNew(){ var obj = {} var Constructor = Array.prototype.shift.call(arguments) obj.__proto__ = Constructor.prototype // 把实例与构造函数它们的关系建立起来 var res = Constructor.apply(obj, arguments) return typeof res === 'object' ? res : obj; } ``` # 数组扁平化 ``` // 方案 1 function recursionFlat(ary = []) { const res = [] ary.forEach(item => { if (Array.isArray(item)) { res.push(...recursionFlat(item)) } else { res.push(item) } }) return res } // 方案 2 function reduceFlat(ary = []) { return ary.reduce((res, item) => res.concat(Array.isArray(item) ? reduceFlat(item) : item), []) } // 测试 const source = [1, 2, [3, 4, [5, 6]], '7'] console.log(recursionFlat(source)) console.log(reduceFlat(source)) ``` # 对象扁平化 ``` function objectFlat(obj = {}) { const res = {} function flat(item, preKey = '') { Object.entries(item).forEach(([key, val]) => { const newKey = preKey ? `${preKey}.${key}` : key if (val && typeof val === 'object') { flat(val, newKey) } else { res[newKey] = val } }) } flat(obj) return res } // 测试 const source = { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } } console.log(objectFlat(source)); ``` # ES 5、ES 6继承 主要使用 ES 5 跟 ES 6 对比看下`class`继承的原理 使用`es6`语法 ~~~ class B { constructor(opt) { this.BName = opt.name; } } class A extends B { constructor () { // 向父类传参 super({ name: 'B' }); // this 必须在 super() 下面使用 console.log(this); } } ~~~ 使用`es5`语法 使用寄生组合继承的方式 1. 原型链继承,使子类可以调用父类原型上的方法和属性 2. 借用构造函数继承,可以实现向父类传参 3. 寄生继承,创造干净的没有构造方法的函数,用来寄生父类的 prototype ~~~ // 实现继承,通过继承父类 prototype function __extends(child, parent) { // 修改对象原型 Object.setPrototypeOf(child, parent); // 寄生继承,创建一个干净的构造函数,用于继承父类的 prototype // 这样做的好处是,修改子类的 prototype 不会影响父类的 prototype function __() { // 修正 constructor 指向子类 this.constructor = child; } // 原型继承,继承父类原型属性,但是无法向父类构造函数传参 child.prototype = parent === null ? Object.create(parent) : ((__.prototype = parent.prototype), new __()); } var B = (function() { function B(opt) { this.name = opt.name; } return B; })(); var A = (function(_super) { __extends(A, _super); function A() { // 借用继承,可以实现向父类传参, 使用 super 可以向父类传参 return (_super !== null && _super.apply( this, { name: 'B' })) || this; } return A; })(B); ~~~ 测试 class ~~~ const a = new A(); console.log(a.BName, a.constructor); // B ,ƒ A() {} ~~~ # Object.create 的实现 ~~~ function create(obj) { function F() {} F.prototype = obj; return new F(); } ~~~ # 参考 [JS 基础手写代码](https://hub.fastgit.org/Godiswill/blog/issues/20) [前端面试中常见的手写功能](https://www.h5w3.com/44219.html)