instanceof的实现

  1. function myInstanceof(left, right) {
  2. let rightProto = right.prototype;
  3. let leftProto = left._proto_;
  4. // 判断实例对象的类型是否等于类型的原型
  5. while (true) {
  6. if (rightProto === null) return false;
  7. if (leftProto === rightProto) return true;
  8. leftProto = leftProto.prototype;
  9. }
  10. }

new的实现

  1. function New(Constructor, ...args){
  2. let obj = {}; // 创建一个新对象
  3. Object.setPrototypeOf(obj, Constructor.prototype); // 连接新对象与函数的原型
  4. return Constructor.apply(obj, args) || obj; // 执行函数,改变 this 指向新的对象
  5. }
  6. function Foo(a){
  7. this.a = a;
  8. }
  9. New(Foo, 1); // Foo { a: 1 }
  10. // 第二种
  11. function _new() {
  12. let target = {}; //创建的新对象
  13. //第一个参数是构造函数
  14. let [constructor, ...args] = [...arguments];
  15. //执行[[原型]]连接;target 是 constructor 的实例
  16. target.__proto__ = constructor.prototype;
  17. //执行构造函数,将属性或方法添加到创建的空对象上
  18. let result = constructor.apply(target, args);
  19. if (result && (typeof (result) == "object" || typeof (result) == "function")) {
  20. //如果构造函数执行的结构返回的是一个对象,那么返回这个对象
  21. return result;
  22. }
  23. //如果构造函数返回的不是一个对象,返回创建的新对象
  24. return target;
  25. }
  26. //第三种
  27. function _new2(constructor, ...args) {
  28. if (typeof constructor !== 'function') {
  29. throw new TypeError('Type Error')
  30. }
  31. let obj = Object.create(constructor.prototype);
  32. const res = constructor.apply(obj, args);
  33. const isObject = typeof res === 'object';
  34. const isFunction = typeof res === 'function';
  35. return isObject || isFunction ? res : obj;
  36. }

Ajax封装

  1. function ajax(type, url, data, success, fail) {
  2. var xhr = null;
  3. if (widnow.XMLHttpRequest) {
  4. xhr = new XMLHttpRequest();
  5. } else {
  6. xhr = new ActiveXObject();
  7. }
  8. if (typeof data === 'object') {
  9. var dataStr;
  10. for (var key in data) {
  11. dataStr += key + '=' + data[key] + '&';
  12. }
  13. data = dataStr.replace(/&$/, '');
  14. }
  15. type = type.toUpperCase();
  16. //随机数,清缓存
  17. var random = Math.random();
  18. if (type === 'GET') {
  19. if (data) {
  20. xhr.open('GET', url + '?' + data, true);
  21. } else {
  22. xhr.open('GET', url + '?t='+ random, true);
  23. }
  24. } else {
  25. xhr.open('POST', url, true);
  26. xhr.setRequestHeader("Content-type", 'application/x-www-form-urlencoded');
  27. xhr.send(dataStr);
  28. }
  29. xhr.onreadystatechange = function () {
  30. if (xhr.readyState === 4) {
  31. if(xhr.status === 200) {
  32. success && success(xhr.responseText);
  33. } else {
  34. fail && fail(xhr.status);
  35. }
  36. }
  37. }
  38. }
  39. // Promise版
  40. const getJSON = function(url) {
  41. return new Promise((resolve, reject) => {
  42. const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
  43. xhr.open('GET', url, false);
  44. xhr.setRequestHeader('Accept', 'application/json');
  45. xhr.onreadystatechange = function() {
  46. if (xhr.readyState !== 4) return;
  47. if (xhr.status === 200 || xhr.status === 304) {
  48. resolve(xhr.responseText);
  49. } else {
  50. reject(new Error(xhr.responseText));
  51. }
  52. }
  53. xhr.send();
  54. })
  55. }

浅拷贝与深拷贝

  • Object.assignslice() 都是浅拷贝,JSON.parse(JSON.stringify(obj)) 是深拷贝
  • JSON.stringify的几个缺点

1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象

3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失

4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null

5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor

6、如果对象中存在循环引用的情况也无法正确实现深拷贝

  • 浅拷贝 ```javascript // 简易例子 function cloneObject(source) { let target = {}; for(let prop in source) { if(source.hasOwnProperty(prop)){
    1. target[prop] = source[prop];
    } } return target; }

// 对象merge export default merge(target) { for( let i = 1; i < arguments; i++){ let source = arguments[i]; for( let prop in source){ if(source.hasOwnProperty(prop)) { let value = source[prop]; if(value != undefined) { target[prop] = value; } } } } }

// 实现Object.assign if (type of Object._assign != ‘function’) { Object.defineProperty(Object, ‘assign2’, { value: function (target) { if (target == null) { throw new TypeError(‘Cannot not undefined or null to object’); }

  1. target = Object(object);
  2. for (let i = 1; i < arguments.length; i++) {
  3. let source = arguments[i];
  4. if (source != null) {
  5. for (let prop in source) {
  6. if (source.hasOwnProperty(prop)) {
  7. let value = source[prop]
  8. if (value != undefined) {
  9. target[prop] = value
  10. }
  11. }
  12. }
  13. }
  14. }
  15. }
  16. })

}

  1. - 深拷贝
  2. ```javascript
  3. // 简易
  4. function deepClone(source) {
  5. let target = {};
  6. for (let prop in source) {
  7. if(typeof source[prop] === 'object') {
  8. target[prop] = deepClone(source[prop]);
  9. } else {
  10. target[prop] = source[prop];
  11. }
  12. }
  13. return target;
  14. }
  15. // 考虑数组与对象情况
  16. function clone(Obj) {
  17. var buf;
  18. if (Obj instanceof Array) {
  19. buf = []; // 创建一个空的数组
  20. let i = Obj.length;
  21. while (i--) {
  22. buf[i] = clone(Obj[i]);
  23. }
  24. return buf;
  25. } else if (Obj instanceof Object){
  26. buf = {}; // 创建一个空对象
  27. for (let k in Obj) { // 为这个对象添加新的属性
  28. buf[k] = clone(Obj[k]);
  29. }
  30. return buf;
  31. }else{
  32. return Obj;
  33. }
  34. }
  35. // 最完整示例,包括对象循环引用
  36. function deepClone(obj, hash = new WeakMap()) {
  37. if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
  38. if (obj instanceof Date) return new Date(obj);
  39. if (obj instanceof RegExp) return new RegExp(obj);
  40. // 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
  41. if (typeof obj !== "object") return obj;
  42. // 是对象的话就要进行深拷贝
  43. if (hash.get(obj)) return hash.get(obj);
  44. let cloneObj = new obj.constructor();
  45. // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
  46. hash.set(obj, cloneObj);
  47. for (let key in obj) {
  48. if (obj.hasOwnProperty(key)) {
  49. // 实现一个递归拷贝
  50. cloneObj[key] = deepClone(obj[key], hash);
  51. }
  52. }
  53. return cloneObj;
  54. }
  55. let obj = { name: 1, address: { x: 100 } };
  56. obj.o = obj; // 对象存在循环引用的情况
  57. let d = deepClone(obj);
  58. obj.address.x = 200;
  59. console.log(d);
  60. // 考虑symbol情况
  61. const cloneDeep1 = (target, hash = new WeakMap()) => {
  62. // 对于传入参数处理
  63. if (typeof target !== 'object' || target === null) {
  64. return target;
  65. }
  66. // 哈希表中存在直接返回
  67. if (hash.has(target)) return hash.get(target);
  68. const cloneTarget = Array.isArray(target) ? [] : {};
  69. hash.set(target, cloneTarget);
  70. // 针对Symbol属性
  71. const symKeys = Object.getOwnPropertySymbols(target);
  72. if (symKeys.length) {
  73. symKeys.forEach(symKey => {
  74. if (typeof target[symKey] === 'object' && target[symKey] !== null) {
  75. cloneTarget[symKey] = cloneDeep1(target[symKey]);
  76. } else {
  77. cloneTarget[symKey] = target[symKey];
  78. }
  79. })
  80. }
  81. for (const i in target) {
  82. if (Object.prototype.hasOwnProperty.call(target, i)) {
  83. cloneTarget[i] =
  84. typeof target[i] === 'object' && target[i] !== null
  85. ? cloneDeep1(target[i], hash)
  86. : target[i];
  87. }
  88. }
  89. return cloneTarget;
  90. }

参考资料
浅拷贝与深拷贝
如何写出一个惊艳面试官的深拷贝?

防抖与节流

  1. // 防抖
  2. function debounce(fn) {
  3. // 创建一个标记用来存放定时器的返回值
  4. let timeout = null;
  5. return function() {
  6. //每次当用户点击/输入的时候,把前一个定时器清除
  7. clearTimeout(timeout);
  8. // 然后创建一个新的 setTimeout,
  9. // 这样就能保证点击按钮后的 interval 间隔内
  10. // 如果用户还点击了的话,就不会执行 fn 函数
  11. timeout = setTimeout(() => {
  12. fn.apply(this, arguments);
  13. }, 1000);
  14. };
  15. }
  16. // 节流
  17. function throttle(fn) {
  18. // 通过闭包保存一个标记
  19. let canRun = true;
  20. return function() {
  21. // 在函数开头判断标志是否为 true,不为 true 则中断函数
  22. if(!canRun) {
  23. return;
  24. }
  25. // 将 canRun 设置为 false,防止执行之前再被执行
  26. canRun = false;
  27. // 定时器
  28. setTimeout( () => {
  29. fn.call(this, arguments);
  30. // 执行完事件(比如调用完接口)之后,重新将这个标志设置为 true
  31. canRun = true;
  32. }, 1000);
  33. };
  34. }

数组方法

扁平化

  1. const arr = [1, [2, [3, [4, 5]]], 6];
  2. // 1.使用flat
  3. const res1 = arr.flat(Infinity);
  4. // 2.使用正则匹配
  5. const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']');
  6. // 3.使用reduce
  7. const flatten = arr => {
  8. return arr.reduce((pre, cur) => {
  9. return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  10. }, [])
  11. }
  12. const res4 = flatten(arr);
  13. // 4. 递归
  14. const res5 = [];
  15. const fn = arr => {
  16. for (let i = 0; i < arr.length; i++) {
  17. if (Array.isArray(arr[i])) {
  18. fn(arr[i]);
  19. } else {
  20. res5.push(arr[i]);
  21. }
  22. }
  23. }
  24. fn(arr);

数组去重

  1. const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
  2. // 1.利用Set
  3. const res1 = Array.from(new Set(arr));
  4. // 2.利用Map
  5. const unique5 = arr => {
  6. const map = new Map();
  7. const res = [];
  8. for (let i = 0; i < arr.length; i++) {
  9. if (!map.has(arr[i])) {
  10. map.set(arr[i], true)
  11. res.push(arr[i]);
  12. }
  13. }
  14. return res;
  15. }
  16. // 3.两次for循环
  17. const unique1 = arr => {
  18. let len = arr.length;
  19. for (let i = 0; i < len; i++) {
  20. for (let j = i + 1; j < len; j++) {
  21. if (arr[i] === arr[j]) {
  22. arr.splice(j, 1);
  23. // 每删除一个树,j--保证j的值经过自加后不变。同时,len--,减少循环次数提升性能
  24. len--;
  25. j--;
  26. }
  27. }
  28. }
  29. return arr;
  30. }
  31. // 4.使用indexOf
  32. const unique2 = arr => {
  33. const res = [];
  34. for (let i = 0; i < arr.length; i++) {
  35. if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
  36. }
  37. return res;
  38. }
  39. // 5.使用includes
  40. const unique3 = arr => {
  41. const res = [];
  42. for (let i = 0; i < arr.length; i++) {
  43. if (!res.includes(arr[i])) res.push(arr[i]);
  44. }
  45. return res;
  46. }

filter实现

  1. Array.prototype.filter = function(callback, thisArg) {
  2. if (this == undefined) {
  3. throw new TypeError('this is null or not undefined');
  4. }
  5. if (typeof callback !== 'function') {
  6. throw new TypeError(callback + 'is not a function');
  7. }
  8. const res = [];
  9. // 让obj成为回调函数的对象传递(强制转换对象)
  10. const obj = Object(this);
  11. // >>>0 无符号移位0位,保证len为number,且为正整数
  12. const len = obj.length >>> 0;
  13. for (let i = 0; i < len; i++) {
  14. // 检查i是否在O的属性(会检查原型链)
  15. if (i in obj) {
  16. // 回调函数调用传参
  17. if (callback.call(thisArg, obj[i], i, obj)) {
  18. res.push(obj[i]);
  19. }
  20. }
  21. }
  22. return res;
  23. }

map实现

  1. Array.prototype.map = function(callback, thisArg) {
  2. if (this == undefined) {
  3. throw new TypeError('this is null or undefined');
  4. }
  5. if (typeof callback !== 'function') {
  6. throw new TypeError(callback + ' is not a function');
  7. }
  8. const res = [];
  9. const obj = Object(this);
  10. const len = obj.length >>> 0;
  11. for(let i = 0; i < len; i++) {
  12. if (i in obj) {
  13. res[i] = callback.call(thisArg, obj[i], i, this);
  14. }
  15. }
  16. return res;
  17. }

forEach实现

  1. Array.prototype.forEach = function(callback, thisArg) {
  2. if (this == null) {
  3. throw new TypeError('this is null or not defined');
  4. }
  5. if (typeof callback !== "function") {
  6. throw new TypeError(callback + ' is not a function');
  7. }
  8. const obj = Object(this);
  9. const len = obj.length >>> 0;
  10. let k = 0;
  11. while (k < len) {
  12. if (k in obj) {
  13. callback.call(thisArg, obj[k], k, obj);
  14. }
  15. k++;
  16. }
  17. }

reduce实现

  1. Array.prototype.reduce = function(callback, initialValue) {
  2. if (this === undefined) {
  3. throw new TypeError('this is null or not undefined');
  4. }
  5. if (typeof callback !== 'function') {
  6. throw new TypeError(callback + 'is not a function');
  7. }
  8. const obj = Object(this);
  9. const len = this.length;
  10. let accumulator = initialValue;
  11. let k = 0;
  12. // 如果无初始值则取第一个值
  13. if (accumulator === undefined) {
  14. while(k < len && !(k in obj)) {
  15. k++;
  16. }
  17. if(k >= len) {
  18. throw new TypeError('Reduce of empty array with no initial value');
  19. }
  20. accumulator = obj[k++];
  21. }
  22. while (k < len) {
  23. accumulator = callback.call(this, accumulator, obj[k], k, obj);
  24. k++;
  25. }
  26. return accumulator;
  27. }

apply与call的实现

  1. Function.prototype.apply = function(context = window, args) {
  2. if (typeof this !== 'function') {
  3. throw TypeError('this is not a function');
  4. }
  5. context.fn = this;
  6. const res = context.fn(...args);
  7. delete context.fn;
  8. return res;
  9. }
  10. Function.prototype.call = function(context = window, ...args) {
  11. if (typeof this !== 'function') {
  12. throw TypeError('this is not a function');
  13. }
  14. context.fn = this;
  15. const res = context.fn(args);
  16. delete context.fn;
  17. return res;
  18. }

bind的实现

  1. Function.prototype.bind = function(context, ...args) {
  2. if (typeof this !== 'function') {
  3. throw new Error('');
  4. }
  5. // 保存this
  6. const selfFn = this;
  7. return function() {
  8. // 考虑new情况
  9. if (this instanceof F) {
  10. return new self(...args, ...arguments)
  11. }
  12. return selfFn.apply(context, [...args, ...arguments]);
  13. }
  14. }

柯理化

  1. // 利用==判断会调用toString方法
  2. const arr = [1,2,3];
  3. function add() {
  4. const _args = [...arguments];
  5. function fn() {
  6. _args.push(...arguments);
  7. return fn;
  8. }
  9. fn.toString = function() {
  10. console.log(_args);
  11. return _args.reduce((sum, item) => sum + item);
  12. }
  13. return fn;
  14. }
  15. fn1 = add(1);
  16. console.log(fn1(2));
  17. // 固定参数个数的柯理化
  18. function sum(a, b, c) {
  19. return a + b + c;
  20. function curry(fn) {
  21. return function curried(...args) {
  22. // function的length属性指函数所需形参的个数,如果参数有默认值则为第一个有默认值之前的参数个数
  23. if (args.length >= fn.length) {
  24. return fn.apply(this, args);
  25. } else {
  26. return function(...args2) {
  27. return curried.apply(this, args.concat(args2));
  28. }
  29. }
  30. }
  31. }
  32. const sum2 = curry(sum);
  33. console.log(sum2(1, 2)(3)); // 6
  34. // 非固定参数个数的珂理化
  35. // 非固定个数入参
  36. function curry2(fn) {
  37. return function curried(...args) {
  38. //args,累计保存的参数
  39. return function(...args2) {
  40. // args2,每次执行传入的参数
  41. if (args2.length) {
  42. return curried.apply(this, args.concat(args2))
  43. } else {
  44. return fn.apply(this, args);
  45. }
  46. }
  47. }
  48. }

instanceof实现

  1. function _instanceof() {
  2. if (typeof left !== 'object' || left === null) return false;
  3. let proto = Object.getPrototypeOf(left);
  4. while(true) {
  5. if (proto === null) return false;
  6. if (proto === right.prototype) return true;
  7. proto = Object.getPrototypeOf(proto);
  8. }
  9. }

继承实现

  1. function Parent() {
  2. this.name = 'parent';
  3. }
  4. function Child() {
  5. Parent.call(this);
  6. this.type = 'children';
  7. }
  8. Child.prototype = Object.create(Parent.prototype);
  9. Child.prototype.constructor = Child;

Object.assign实现

  1. Object.defineProperty(Object, 'assign', {
  2. value: function(target, ...args) {
  3. if (target == null) {
  4. return new TypeError('Cannot convert undefined or null to object');
  5. }
  6. let obj = Object(target);
  7. for(let i = 0; i < args.length; i++) {
  8. if (args[i] !== null) {
  9. for(const key in args[i]) {
  10. if (Object.hasOwnProperty.call(obj, key)) {
  11. obj[key] = args[i][key];
  12. }
  13. }
  14. }
  15. }
  16. return obj;
  17. },
  18. enumerable: false, // 不可枚举
  19. writable: false, // 不可修改
  20. configurable: false, // 是否可使用delete,是否可重新定义属性特性
  21. })

Promise实现

  1. /**
  2. * Promise 实现
  3. */
  4. const PENDING = 'PENDING';
  5. const FULFILLED = 'FULFILLED';
  6. const REJECTED = 'REJECTED';
  7. class Promise {
  8. constructor(exector) {
  9. this.status = 'PENDING'; // 状态,初始等待状态
  10. this.value = undefined; // 值,便于后续then,catch访问
  11. this.reason = undefined; // 失败原因保存
  12. this.onFulfilledCallbacks = []; // 成功回调队列
  13. this.onRejectedCallbacks = []; // 失败回调队列
  14. const resolve = (value) => {
  15. // 等待中状态才能进行更改
  16. if (this.status === PENDING) {
  17. this.status = FULFILLED;
  18. this.value = value;
  19. this.onFulfilledCallbacks.forEach(fn => fn(this.value));
  20. }
  21. }
  22. const reject = (reason) => {
  23. // 等待中状态才能进行更改
  24. if (this.status === PENDING) {
  25. this.status = REJECTED;
  26. this.reason = reason;
  27. this.onFulfilledCallbacks.forEach(fn => fn(this.reason));
  28. }
  29. }
  30. try {
  31. // 立即执行并传入修改状态的两个方法
  32. exector(resolve, reject);
  33. } catch(e) {
  34. // 出现错误直接reject回去
  35. reject(e);
  36. }
  37. }
  38. then(onFulfilled, onRejected) {
  39. // 初始化两个回调
  40. onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  41. onRejected = typeof onRejected === 'function' ? onRejected :
  42. reason => {throw new TypeError(reason instanceof Error ? reason.message : reason)}
  43. // 保存this, 即当前Promise实例
  44. const self = this;
  45. // 返回一个新Promise
  46. return new Promise((resolve, reject) => {
  47. // 等待状态
  48. if (self.status === PENDING) {
  49. // 加入成功回调队列
  50. self.onFulfilledCallbacks.push(() => {
  51. try {
  52. setTimeout(() => {
  53. const result = onFulfilled(self.value);
  54. // 处理链式传递
  55. resolvePromise(result, resolve, reject);
  56. })
  57. } catch(e) {
  58. reject(e)
  59. }
  60. })
  61. // 加入失败回调队列
  62. self.onRejectedCallbacks.push(() => {
  63. try {
  64. setTimeout(() => {
  65. const result = onRejected(self.reason);
  66. // 处理链式传递
  67. resolvePromise(result, resolve, reject);
  68. })
  69. } catch(e) {
  70. reject(e)
  71. }
  72. })
  73. }
  74. })
  75. }
  76. catch(onRejected) {
  77. this.then(null, onRejected);
  78. }
  79. static resolve(value) {
  80. if (value instanceof Promise) {
  81. return value;
  82. } else {
  83. return new Promise((resolve, reject) => resolve(value));
  84. }
  85. }
  86. static reject(reason) {
  87. return new Promise((resolve, reject) => {
  88. reject(reason);
  89. })
  90. }
  91. static all(promiseArr) {
  92. const len = promiseArr.length;
  93. let values = new Array(len);
  94. let count = 0;
  95. return new Promise((resolve, reject) => {
  96. for(let i = 0; i < len; i++) {
  97. // 使用Promise.resolve处理,保证每个都是Promise实例
  98. Promise.resolve(promise[i]).then(value => {
  99. count++;
  100. values[i] = value;
  101. if (count === len) {
  102. resolve(values);
  103. }
  104. }, err => reject(err))
  105. }
  106. })
  107. }
  108. static race(promiseArr) {
  109. return new Promise((resolve, reject) => {
  110. promiseArr.forEach(promiseItem => {
  111. Promise.resolve(promiseItem).then(value => resolve(value), err => reject(err));
  112. })
  113. })
  114. }
  115. }
  116. // 处理链式调用then, 传递返回值result
  117. function resolvePromise(result, resolve, reject) {
  118. // 1. 返回是Promise类型,执行它的then
  119. // 2. 返回不是Promise, 执行当前新Promise的resolve, 将值传递下去
  120. if (result instanceof Promise) {
  121. result.then(resolve, reject);
  122. } else {
  123. resolve(result);
  124. }
  125. }

参考资料
promise实现—前端进阶之道
从零开始实现一个promise

参考

32个手写JS,巩固你的JS基础(面试高频)