前端開發面試總結(二)

深拷貝與淺拷貝

1、數據類型

JavaScript的數據類型分爲基本數據類型(String, Number, Boolean, Undefined, Null, Symbol)和引用數據類型。 引用類型在棧中存儲了指針,該指針指向的是堆中的該實體的起始地址,當解釋器尋找引用值的時候,會先檢索它在棧中的地址,而後根據地址獲取到堆中的實體。javascript

2、深拷貝和淺拷貝

深拷貝和淺拷貝主要是針對於Object和Array這樣的引用類型的。淺拷貝只賦值某一個對象的指針,而不復制對象自己,新舊對象會共享同一塊內存,可是深拷貝會建立一個一摸同樣的對象,新建立的對象不會和原來的對象共享同一塊內存,修改新對象的時候,原來對象的內容不會被修改。java

3、賦值和淺拷貝的區別

賦值:當把一個值賦給一個新變量的時候,其實是把這個對象在棧中的地址賦給變量,而不是棧中的數據。兩個變量會指向同一個存儲空間。 淺拷貝:淺拷貝是按位拷貝對象,它會建立一個新對象,這個對象有着原始對象屬性值的一份精確拷貝。若是屬性是基本類型,拷貝的就是基本類型的值;若是屬性是內存地址(引用類型),拷貝的就是內存地址 ,所以若是其中一個對象改變了這個地址,就會影響到另外一個對象。即默認拷貝構造函數只是對對象進行淺拷貝複製(逐個成員依次拷貝),即只複製對象空間而不復制資源。數組

和原數據是否指向同一個對象 第一層數據爲基本數據類型 原數據中包含子對象
賦值 改變會使原數據一塊兒改變 改變會使原數據一塊兒改變
淺拷貝 改變不會使原數據一塊兒改變 改變會使原數據一塊兒改變
深拷貝 改變不會使原數據一塊兒改變 改變不會使原數據一塊兒改變
4、淺拷貝實現的方式

一、Object.assign()異步

Object.assign()方法能夠把任意多個源對象自身的可枚舉的屬性拷貝給目標對象,而後返回目標對象。Object.assign()進行的是淺拷貝拷貝的是對象屬性的引用,並非對象自己。可是當obj只有一層的時候,Object.assign()進行的是深拷貝。async

console.log("當對象只有一層的時候:");

let obj1 = {
    a: 10
}

let obj2 = Object.assign({}, obj1);

console.log("改變以前:");
console.log(obj1.a);    // 10
console.log(obj2.a);    // 10

obj2.a = 20;

console.log("改變以後:");
console.log(obj1.a);    // 10
console.log(obj2.a);    // 20


console.log("當對象不止一層的時候:");
let obj3 = {
    a: {
        b: 10
    }
};

let obj4 = Object.assign({}, obj3);

console.log("改變以前:");
console.log(obj3.a.b);  // 10
console.log(obj4.a.b);  // 10

obj4.a.b = 20;
console.log("改變以後:");
console.log(obj3.a.b);  // 20
console.log(obj4.a.b);  // 20

複製代碼

二、Array.prototype.concat()函數

let arr = [1, 3, {
    username: 'kobe'
    }];
let arr2=arr.concat();    
arr2[2].username = 'wade';
console.log(arr);   // wade
複製代碼

三、Array.prototype.slice()ui

let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);   // wade
複製代碼

concat()和slice()方法不會修改原數組,只會返回一個淺複製了原數組元素的一個新數組。spa

5、深拷貝實現的方式

一、JSON.parse(JSON.stringify)prototype

原理: 用JSON.stringify將對象轉換成JSON字符串,再使用JSON.parse()方法將字符串轉換成JSON對象,這個過程是一個深拷貝的過程。指針

let arr = [1, 3, {
    username: ' kobe'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan'; 
console.log(arr, arr4)

複製代碼

這樣的方法雖然能夠實現數組或者對象的深拷貝,可是不能夠實現函數的深拷貝。

二、手寫的遞歸方法

//檢測類型
function checkedType(target){
    return Object.prototype.toString.call(target).slice(8, -1);
}

//實現深度拷貝
function clone(target){
    let result;
    let targetType = checkedType(target);
    if(targetType === 'Object'){
        result = {};
    }else if(targetType === 'Array'){
        result = [];
    }else{
        result = target;
    }

    //遍歷目標對象
    for(let i in target){
        let value = target[i];
        //判斷目標結構的每個值裏面是否存在對象或者數組
        if(checkedTypevalue === 'Object' || checkedType(value) === 'Array'){
            result[i] = clone(value);
        }else{
            result[i] = value;
        }
    }

    return result;
}

複製代碼

三、函數庫lodash

lodash函數庫也提供用於深拷貝的方法,程序示例:

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
複製代碼

async和await

async函數就是Generator函數的語法糖,async函數就是將Generator函數的星號(*)替換成async將yield替換成await.

async函數的優勢:

(1)內置執行器 Generator()函數的執行必需要靠執行器,async函數自帶函數執行器。因此async函數的執行與普通函數同樣,都只要一行。

(2)更好的語義化 async 和 await,比起星號和 yield,語義更清楚了。async 表示函數裏有異步操做,await 表示緊跟在後面的表達式須要等待結果。

(3)更廣的實用性 co 函數庫約定,yield 命令後面只能是 Thunk 函數或 Promise 對象,而 async 函數的 await 命令後面,能夠跟 Promise 對象和原始類型的值。

async函數的注意點
  • await 命令後面的 Promise 對象,運行結果多是 rejected,因此最好把 await 命令放在 try...catch 代碼塊中。
  • await 命令只能用在 async 函數之中,若是用在普通函數,就會報錯。
相關文章
相關標籤/搜索