前端开发面试总结(二)

深拷贝与浅拷贝

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 函数之中,若是用在普通函数,就会报错。
相关文章
相关标签/搜索