前端面试必会的手撕代码题,四天带你学完(第四天)

95 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情

  • 使用Promise封装AJAX请求
  • 实现浅拷贝
  • 实现深拷贝
  • 实现sleep函数(使用Promise封装setTimeout)
  • 实现Object.assign

1. 使用Promise封装AJAX请求

 // promise 封装实现:
 function getJSON(url) {
   // 创建一个 promise 对象
   let promise = new Promise(function(resolve, reject) {
     let xhr = new XMLHttpRequest();
     // 新建一个 http 请求
     xhr.open("GET", url, true);
     // 设置状态的监听函数
     xhr.onreadystatechange = function() {
       if (this.readyState !== 4) return;
       // 当请求成功或失败时,改变 promise 的状态
       if (this.status === 200) {
         resolve(this.response);
       } else {
         reject(new Error(this.statusText));
       }
     };
     // 设置错误监听函数
     xhr.onerror = function() {
       reject(new Error(this.statusText));
     };
     // 设置响应的数据类型
     xhr.responseType = "json";
     // 设置请求头信息
     xhr.setRequestHeader("Accept", "application/json");
     // 发送 http 请求
     xhr.send(null);
   });
   return promise;
 }

2. 实现浅拷贝

浅拷贝是指,一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值,如果是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。

(1)Object.assign()

Object.assign()是ES6中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target, source_1, ···),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。

注意:

  • 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。
  • 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
  • 因为nullundefined 不能转化为对象,所以第一个参数不能为nullundefined,会报错。
 let target = {a: 1};
 let object2 = {b: 2};
 let object3 = {c: 3};
 Object.assign(target,object2,object3);  
 console.log(target);  // {a: 1, b: 2, c: 3}

(2)扩展运算符

使用扩展运算符可以在构造字面量对象的时候,进行属性的拷贝。语法:let cloneObj = { ...obj };

 let obj1 = {a:1,b:{c:1}}
 let obj2 = {...obj1};
 obj1.a = 2;
 console.log(obj1); //{a:2,b:{c:1}}
 console.log(obj2); //{a:1,b:{c:1}}
 obj1.b.c = 2;
 console.log(obj1); //{a:2,b:{c:2}}
 console.log(obj2); //{a:1,b:{c:2}}

(3)数组方法实现数组浅拷贝

1)Array.prototype.slice
  • slice()方法是JavaScript数组的一个方法,这个方法可以从已有数组中返回选定的元素:用法:array.slice(start, end),该方法不会改变原始数组。
  • 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
 let arr = [1,2,3,4];
 console.log(arr.slice()); // [1,2,3,4]
 console.log(arr.slice() === arr); //false
2)Array.prototype.concat
  • concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
  • 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
 let arr = [1,2,3,4];
 console.log(arr.concat()); // [1,2,3,4]
 console.log(arr.concat() === arr); //false

(4)手写实现浅拷贝

 // 浅拷贝的实现;
 ​
 function shallowCopy(object) {
   // 只拷贝对象
   if (!object || typeof object !== "object") return;
 ​
   // 根据 object 的类型判断是新建一个数组还是对象
   let newObject = Array.isArray(object) ? [] : {};
 ​
   // 遍历 object,并且判断是 object 的属性才拷贝
   for (let key in object) {
     if (object.hasOwnProperty(key)) {
       newObject[key] = object[key];
     }
   }
 ​
   return newObject;
 }

3. 实现深拷贝

  • 浅拷贝: 浅拷贝指的是将一个对象的属性值复制到另一个对象,如果有的属性的值为引用类型的话,那么会将这个引用的地址复制给对象,因此两个对象会有同一个引用类型的引用。浅拷贝可以使用 Object.assign 和展开运算符来实现。
  • 深拷贝: 深拷贝相对浅拷贝而言,如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。深拷贝对于一些对象可以使用 JSON 的两个函数来实现,但是由于 JSON 的对象格式比 js 的对象格式更加严格,所以如果属性值里边出现函数或者 Symbol 类型的值时,会转换失败

(1)JSON.stringify()

  • JSON.parse(JSON.stringify(obj))是目前比较常用的深拷贝方法之一,它的原理就是利用JSON.stringifyjs对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象。
  • 这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过JSON.stringify()进行处理之后,都会消失。
 let obj1 = {  a: 0,
               b: {
                  c: 0
                  }
             };
 let obj2 = JSON.parse(JSON.stringify(obj1));
 obj1.a = 1;
 obj1.b.c = 1;
 console.log(obj1); // {a: 1, b: {c: 1}}
 console.log(obj2); // {a: 0, b: {c: 0}}

(2)函数库lodash的_.cloneDeep方法

该函数库也有提供_.cloneDeep用来做 Deep Copy

 var _ = require('lodash');
 var obj1 = {
     a: 1,
     b: { f: { g: 1 } },
     c: [1, 2, 3]
 };
 var obj2 = _.cloneDeep(obj1);
 console.log(obj1.b.f === obj2.b.f);// false

(3)手写实现深拷贝函数

 // 深拷贝的实现
 function deepCopy(object) {
   if (!object || typeof object !== "object") return;
 ​
   let newObject = Array.isArray(object) ? [] : {};
 ​
   for (let key in object) {
     if (object.hasOwnProperty(key)) {
       newObject[key] =
         typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
     }
   }
 ​
   return newObject;
 }

4. 实现sleep函数(使用Promise封装setTimeout)

 function timeout(delay) {
   return new Promise(resolve => {
     setTimeout(resolve, delay)
   })
 };

5. 实现Object.assign

 Object.myAssign = function(target, ...source) {
     if (target == null) {
         throw new TypeError('Cannot convert undefined or null to object')
     }
     let ret = Object(target) 
     source.forEach(function(obj) {
         if (obj != null) {
             for (let key in obj) {
                 if (obj.hasOwnProperty(key)) {
                     ret[key] = obj[key]
                 }
             }
         }
     })
     return ret
 }