前端面试考点---拷贝篇

144 阅读4分钟

等号赋值三步

  • 堆内存创建值
  • 堆内存创建变量
  • 让变量和值关联

const 类型变量与值的关联只能执行一次

//同一个值可与多个变量关联
var a = 10
var b = a
console.log(a,b) -> 10 20

//同一个变量只会与最后一次赋值的值关联
//10并没有改变,而是新建了一个值20与a变量关联了
var a = 10
a = 20
console.log(a) -> 20

const

  • const是 ES6 新增的命令
  • 声明后必须马上赋值
  • 声明并赋值后,不能修改关联的值,也不能修改地址
  • 可以去通过地址修改堆内存中引用对象的值
  • 应用:常量、声明匿名函数、箭头函数的时候。
  • 不允许重复声明变量
const a = { a1: 1}
a.a1 = 2
console.log(a) -> { a1: 2}

let

  • let和 ES6 新增的命令
  • 范围:块级作用域
  • 作用:let常用以替代var,它是家能让变量的作用范围更精确,可以轻松自己添加大括号使代码分割成块了
  • 应用:for循环的计数器,很合适使用let命令,只在循环内生效
  • let声明的变量只在它所在的代码块有效
  • 不允许重复声明变量

块级作用域是es6新增的,es5只有全局作用域和函数作用域 囊括while/for/if 这些具有大括号的块级,当然包括函数的大括号

  • 避免变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

JS内存机制

  • 内存空间分为:栈内存、堆内存
  • 栈内存(放基础数据类型、自动分配),
  • 堆内存(放引用基本数据类型、动态分配)

变量提升

  • 定义:将当前作用域的所有变量声明和函数声明提升到程序顶部
  • 注意:变量提升之后提升变量的声明,而不是提升变量赋值
console.log(a);
var a = 2;  => a is undefined

//上面代码等同于
var a
console.log(a);
a = 2;

关键词

  • 词法环境: 用于定义特定变量和函数标识符在 ECMAScript 代码的词法嵌套结构上关联关系的规范类型
  • EC( Execution Context):执行上下文
  • ECS( Execution Context stack):执行上下文栈,简称执行栈
  • VO(variable object):变量对象
  • AO (active object):活动对象
  • GO(global object)

基本数据类型:

  • boolean,number,string,null,undefined
  • 值放在栈内存中
  • 数据大小确定,自动分配内存
  • 数据类型值不可变,赋值是新值的引用替换,而不会修改旧值
var a = 10
var b = a
b = 20
console.log(a,b) -> 10  20

引用数据类型

  • Object、Array、RegExp、Date、Function
  • 保存在栈中的只是一个堆引用地址,值存放在堆内存中
  • 值比较是比较堆内存地址,地址一样则相同
var a = { key: 1}
var b = { key: 1}
a === b -> false 

首先区分数据类型的存放

浅拷贝

  • 直接在堆中创建内存,拷贝前后对象数据类型不变
  • 只拷贝第一层,对象中的引用对象拷贝的是其堆内存地址
  • 如果修改引用对象内部的引用对象,也会同步到拷贝之后的变量中
function copy(obj){
	let newObj = {}
	for(let key in obj){
		newObj[key] = obj[key]
	}
	return newObj
}

// 以下都是浅拷贝
const arr1 = ['a','b']
const arr2 = ['c']
const arr3 = ['d']
// es5合并数组
arr1.concat(arr2,arr3)
// es6合并数组
[...arr1, ...arr2, ...arr3]

// Object.assign()也是浅拷贝


深拷贝

  • 对对象中的子对象进行递归拷贝
  • 拷贝完后两个对象互不影响
var objects = [{ 'a': 1 }, { 'b': 2 }];

// 深拷贝方法1,子对象不能为function
var deep = JSON.parse(JSON.stringify(objects));

// 深拷贝方法2 ,用lodash库函数
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
# 递归拷贝
 function deepCopy(source) {
    var result = {};
    for(var key in source) {
        if(typeof source[key] === 'object') {
            result[key] = deepCopy(source[key])
        } else {
            result[key] = source[key]
        }
    }
    return result;
}

应用场景

  • JS原生的方法中,map就很函数式,他会返回一个新的数组,不会改变原数组。
  • 而pop这种方法就很不好,它在操作了数组之后,也改变数组本身。
  • 当我们要使用那些有副作用的方法写纯函数的时候,记得做一次深拷贝:
const myPop = x => {
  let [...y] = x; //假定x内部都是基本数据类型
  return y.pop();
}

->>> 博客原文 <<<-