第三章 变量、作用域和内存

1.原始值和引用值的区别

1.1 什么是原始值和引用值

在ES中,值的类型分为两大类:原始值和引用值。故名思意,原始值,就是最简单的数据,又分为6种基本数据类型:Undefined、Null、Boolean、Number、String、Symbol。而引用值,就是对象。
注意:在很多语言中,字符串都是使用对象表示的,因此被认为是引用类型,但是ES打破了这个惯例。

1.2 原始值和引用值的根本区别

其实要区分原始值和引用值,就需要了解原始值和引用值在内存中的存储方式。其中,原始值是按栈存储的;
在这里插入图片描述
而引用值则是按堆存储的,
在这里插入图片描述
让我们通过2个实例来理解一下上面的内容:
(1)理解原始值
let a = 5
那么 a 这个变量指向的值就是5这个值本身,如果再执行
let b = a
那么就是将a指向的值5赋值给了b,相当于细胞分裂,一分为二。两者虽然从数值上来说相等,但是互不影响,相当于双胞胎。如果此时执行
b = 6
那么b的值变成了6,但是 a 的值仍然是5,不受影响。相当于哥哥长高了,和弟弟没有关系。
在这里插入图片描述

(2)理解引用值

let obj1 = {
    name:'Sheldon',
    age:24
}

此时变量obj1存储的实际上是指向下面对象的指针

 {
    name:'Sheldon',
    age:24
}

,而不是对象本身。
这样,我们可以把对象看作一个房间,里面的属性(比如name属性)就是房间里装的东西,那么obj1就是这个房间的房间号。执行

let obj2 = obj1

就是把obj1指向的对象位置(房间号)复制(告诉)给obj2,但是房间仍然只有一个。此时再执行

obj2.name = 'Faker'

修改了对象的name属性(改变了这个房间里某个东西的样子),但是对象的地址并没有改变(房间号还是没变)。再执行

console.log(obj1.name) 

你会发现输出的正是修改后的值'Faker'
由此可以说明,obj1和obj2所指向的是存储对象的地址,也就是存储的是引用值。

在这里插入图片描述

1.3 深拷贝和浅拷贝的区别

原始值和引用值的区别,在进行复制操作的时候就会出现深拷贝和浅拷贝的区别。
(1)深拷贝和浅拷贝是只针对Object和Array这样的引用类型的
浅拷贝是指复制对象的指针,而不是复制对象本身,因此新旧对象仍然是共用一个对象和内存的。而深拷贝则是直接创造一个与就对象一模一样的对象,修改这个新的对象,并不会影响到原来的旧对象。
在1.2中

let obj2 = obj1

就是属于浅拷贝。
(2)如何使用深拷贝

下面是经过我的验证后,确实能够无差别深拷贝的函数
第一种:

function deepClone4(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(typeof obj !== 'object' || obj === null || obj === undefined){
        return obj
    }
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone4(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}  

验证结果:
(1)基本类型(有效):
在这里插入图片描述
(2)数组(有效):
在这里插入图片描述
(3)对象(有效):
在这里插入图片描述

第二种:

// 识别数据类型
function checkedType(target){
    return Object.prototype.toString.call(target).slice(8,-1)
}
// 深克隆函数
function clone(target){
    let result, targetType = checkedType(target)
    if(targetType === 'object'){
        result = {}
    } else if(targetType === 'Array'){
        result = []
    } else{
        return target
    }
    for(let i in target){
        let value = target[i]
        if(checkedType(value) === 'Object' || checkedType(value) === 'Array'){
            result[i] = clone(value)
        }else{
            result[i] = value
        }
    }
    return result
}

如果有更加简洁有效的函数,欢迎交流

2.作用域(执行上下文)

任何变量都存在于某个执行上下文中(也称为作用域)。这个作用域决定了变量的生命周期,以及能够访问代码的哪些部分。

2.1 上下文类型

(1)全局上下文:只能够访问全局上下文的变量和函数,不能直接访问局部上下文的任何数据
(2)函数上下文:不仅可以访问自己作用域内的变量和函数,也可以访问任何包含上下文乃至全局上下文中的变量
(3)块级上下文:不仅可以访问自己作用域内的变量和函数,也可以访问任何包含上下文乃至全局上下文中的变量

3.内存的垃圾回收策略(和浏览器有关)

3.1 标记清理(常用,主流浏览器使用)

给当前不使用的值加上标记,再回来回收它们的内存。

3.2 引用计数(少用,容易出bug,旧版的IE使用)

记录所有变量被引用的次数总和,达到阈值,就执行垃圾回收。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sheldon一蓑烟雨任平生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值