JavaScript高级编程设计(第三版)——第四章:变量作用域和内存问题

系列文章目录

第二章:在html中使用javaScript

第三章:基本概念

第四章:变量作用域和内存问题

第五章:引用类型


目录

系列文章目录

前言

一、基本数据类型和引用类型的值?

1.数据类型

2.赋值

3.传参

4.检测数据类型

二、执行环境及作用域

1.执行环境和作用域概念

2.延长作用域链

3.没有块级作用域

4.垃圾收集

4.1.标记清除法

4.2引用计数法

总结


前言

这本书是我刚入行的时候我师傅推荐给我的,当时只想获得实现的满足感,而一直没有深入学习。随着页面实现了更多后,发现学好javascript非常的有必要,这也是这个系列出现的缘由~

我将分章节学习并记录(第一章简介我就不记录了,有兴趣自己去看看),如果内容太多也会分为上下两小节。

学习过程中也会拓展一下相应内容,思考一些问题,感兴趣就继续往下看看吧~

这一章主要记录一些基本数据类型如何检测、作用域及垃圾回收的一些方法,这一块在面试的时候常常会被问道~


一、基本数据类型和引用类型的值?

1.数据类型

第三章提到基本数据类型有:null nudefined string boolean number(es6还有syblom),引用类型有object

2.赋值

这里我们要明白他们存放位置,基本数据类型存放在栈里面,引用数据类型在栈里面存放指针,指针指向队里面的内存。

所以,当我们进行赋值的时候,如下:

JavaScript高级编程设计(第三版)——第四章:变量作用域和内存问题_第1张图片

 根据上面的图我们可以看清区别:基本数据类型会开启一个新内存赋值,引用类型会开启一个新的内存存指针。

3.传参

传参最常见于函数,当我们将变量当作参数传递给函数的时候。

对基本数据类型来说,参数是函数的一个局部变量,他的变化不会改变全局变量,如下:

function add(a){
    a +=20
}
var age = 80
add(80)
console.log(age)//这里输出80

对引用类型来说,他的变化会改变原始引用值(因为引用地址都相同,所以会改变),如下:

function add(obj){
  obj.age="18"
}
var obj = {
name:"lily"
}
add(obj)
console.log(obj.age)//这里输出18

虽然这样会改变引用值,但是并不代表是引用的地址,如下:

function add(obj){
  obj.age="18"
  obj = new Object()
  obj.age="80"
}
var obj = {
name:"lily"
}
add(obj)
console.log(obj.age)//这里输出18,obj并不会将后面的内存赋值上面,后面的只是一个局部变量,函数的参数还是外面的obj

4.检测数据类型

如何检测类型呢,这里提供三种方法:

  • typeof(),适用于基本数据类型,当检测null和对象的时候返回object
  • instanceof,适用于引用类型,返回true false ,如:array instanceof Array,值得注意的是,因为引用类型都是object的实例,所以检测引用类型是否为object的时候始终返回object
  • object.protoype.tostring.call()来检测,适用于所有类型,返回值为[object 类型]

二、执行环境及作用域

1.执行环境和作用域概念

首先我们知道JavaScript是单线程的语言,我们的语言会按照编码顺序一步一步的执行,执行环境中有一个变量对象,变量对象包括函数和变量。

我们有全局执行变量,就是我们常说的window对象,可以访问他。在我们浏览器关闭或页签关闭的时候全局执行变量才会销毁。

当我们执行的时候遇见了函数,首先会将他放入环境栈里面,等待改函数执行完毕后弹出栈。

我们一步一步执行的时候,会创建变量对象的一个作用域链,我们可以理解为函数内部找不到该变量则向外层执行环境会寻找,直到windows变量。这样形成的一个链路叫作用域链。这里值得注意的是,内部变量可访问内部执行环境,但外部执行环境不能访问内部变量

2.延长作用域链

作用域分为全局作用域和局部(函数)作用域,通常我们执行完后就会移除。但有的时候我们需要延长作用域链,我们只需要在作用域前端添加一个变量对象就可以了啦,可以使用with或try catch中的catch块。

with在我们第三章就说过,它可以将指定对象添加到作用域链上,如下:

function add(){
    var name = "lily"
    with(location){//将location变量对象添加到作用域链前端
        var url = href
    }
    retrun url
}

try catch会创建一个 新的变量对象

try{
console.log("成功")
}
.catch(err){
    var a = "报错啦"
    console.log(a)
}
在catch块中将变量对象添加到作用域前端,从而延长作用域链

当然,除了这两种方法,我们还可以使用eval()这个内置对象来延迟那个作用域链,如下:

function add(str){
    eval(str)//eval会将字符串解析为js,从而将变量对象添加到作用链前端,延长作用域链
}
add("var a= 0")

3.没有块级作用域

这个标题很不理解哈,其他语言不是说{}里面的都是块级作用域吗?在JavaScript中我们陈伟执行环境。作用域也可以叫全局作用域和局部(函数)作用域,但不能说{}里面包裹的就是块级作用域,为什么这样说呢?如下:

//if语句
if(true){
var a ="你好"

}
console.log(a)//输出:你好

//for语句

for(var i=0;i<10;i++){
...

}
console.log(i) //输出10

看到上面这两个语句了嘛,我们都是在{}内部的,但是呢,在{}外部访问也是能访问的,所以这也证实了JavaScript没有块级作用域。

当然,我们在函数内部定义的变量不管是var let const ,在全局执行环境中式无法访问的,但是如果没有用标识符定义,则会将这个变量沿着作用域链查找,直到找个这个标识符的变量(定义这个变量的地方),如果查找到window还没找到,则将这个变量挂载到window上(全局变量)。

4.垃圾收集

我们前面说了,当代码一步一步的执行的时候,会将变量分配内存,当我们变量不在使用得时候,则弹出栈,让内存空出来,这就是垃圾回收机制,这样保证了我们性能和速度。

关于垃圾回收的方法有两种:标记清除法和引用计数法。

4.1.标记清除法

执行代码的时候,将变量进行标记,将正在执行环境中的变量和环境中的变量去除标记,将没有标记的变量清除掉,这就是标记清除法。

4.2引用计数法

当我们声明和一个变量或赋值的时候,计数为1,如果又被赋值给另一个变量计数为2,每次被赋值或引用则加1;如果包含这个值得变量被重新赋值,则减1,直到为0时,就会被清除。

但是这个方法如果存在于循环体内,被重复得引用,那他永远都不能被清除,而回导致内存得不到回收,所以不建议用这个方法。

最后,为了得到更好得性能,所以最好在不用该数据得时候将其设置为null,这样下次垃圾回收得时候,就会清除掉,释放内存。


总结

以上就是今天要讲的内容,本文简单介绍了基本数据和引用数据得值,作用域和作用域链,内存及垃圾回收机制。

你可能感兴趣的:(javasript高级程序设计,javascript,ecmascript,前端)