资讯专栏INFORMATION COLUMN

重温JS基础--变量、作用域和内存问题

huaixiaoz / 2934人阅读

摘要:作用域链是保证对执行环境有权访问的所有变量和函数的有序访问。如上,包含的作用域链包含它自己的变量对象和全局环境的变量对象,为什么能在函数内访问,这就是通过作用域链找的。

前言
JavaScript的变量类型是弱类型的,在特定的时间内保存一个特定的值,变量的值和数据类型可以在脚本的生命周期内随意改变。

1. 基本类型和引用类型的值

JavaScript包含两种不同类型的值:基本类型和引用类型。基本类型指的是简单的数据段,而引用类型指的是那些由多个值构成的对象。
基本数据类型Number,String,Boolean,Null,Undefined这五种基本类型数据是按值访问的,因为可以操作在变量中实际的值。

引用类型是保存到内存中的对象,不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象的时候,实际上操作的是对象的引用。所以,引用类型是按引用访问的。(但是在为对象添加属性的时候,实际上操作的是实际的对象)

动态添加属性
定义基本类型和引用类型值得方式是类似的,都是创建一个变量然后为该变量赋值。但是当值存入变量后,不同类型的值执行的操作则有些区别。先看下对引用类型的值得操作:

var person = new Object()
person.name = "nicholas"
alert(person.name) //"nicholas"

如上,创建一个对象并保存到变量person中,然后为该对象添加一个name属性且赋值为“nicholas”,最后可以访问这个新属性。

假如我们给基本类型的值添加属性,如下:

var person = "good job"
person.name = "Nike"
alert(person.name) //"undefined"

虽然我们看到没有出现任何报错,但是这是不符合规范的。

复制变量值
上面说的是给基本类型和引用类型分别添加属性的区别。接下来,我们从一个变量向另外一个变量复制基本类型值和引用类型值看看有什么区别。

先看基本类型的,从一个变量向另外一个变量复制:

var a = "China NO.1"
var b = a
alert(b) //"China NO.1"

如上,输出b的时候的值和a是一模一样的,但是两者是完全独立的。这是因为从a复制到b的时候,会在变量对象上创建一个新值,然后把这个值赋值给b

在看引用类型,当从一个变量向另一个变量复制引用类型值得时候,同样会将存在变量a中的值复制一份放到b中,但是不同的是,这个值得副本是一个指针,这个指针指向存在堆内存中的一个对象。当结束复制后,这两个变量实际上引用的是一个对象。

var a = new Object()
var b = a
a.name = "nike"
alert(b.name) //"nike"

如上,因为a和b都是引用同一个堆内存中的地址,所以当给a添加一个name属性后,b也可以访问到这个属性。

传递参数
JavaScript中所有函数的参数都是按值传递的。也就是说把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。

向参数传递基本类型值得时候,被传递的值会被复制给一个局部变量。在向参数传递引用类型值得时候,会将这个值在内存中的引用地址复制给这个局部变量,因此这个局部变量的变化会反映在函数的外部,

先看一个基本类型值传递给函数参数的例子:

function add(num){
    num += 10
    return num
}

var count = 20
var result = add(count)
alert(count) //20
alert(result) //30

接下来用引用类型的值举个例子:

function setName(obj){
    obj.name = "nike"
}
var person = new Object()
setName(person)
alert(person.name) //"nike"

上面的例子中,首先创建一个对象保存到person中,然后将person传到setName中,person的值会被复制给obj。所以obj现在和person引用的是同一个对象(即这个变量是按值传递的),obj也会按引用访问同一个对象。在来看一个例子:

function setName(obj){
    obj.name = "nike"
    obj = new Object()
    obj.name = "green"
}
var person = new Object()
setName(person)
alert(person.name)//"nike"

这个例子和上面的区别就是为obj又重新定义了一个对象,然后为这个新对象定义了一个不同值得name属性。如果person是按引用传递的,nameperson就会被修改为指向name属性值为’green‘的新对象。但是并没有这样,实际上当函数在内部重写obj的时候,这个比那里引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后销毁。

检测类型
typeof操作符是一个确定变量是String,Number,Boolean,Null,Undefined的最佳工具。

var s = "abc" 
var b = true
var n = 22
var nu = null
var u = "undefined"
typeof s //string
typeof b //boolean
typeof n //number
typeof nu //object
typeof u //undefined

还有一个instanceof操作符,通过它我们可以判断是什么类型的对象:

//格式
变量 instanceof constructor

如果constructor在该对象的原型链上,那么就会返回true。

2.执行环境和作用域

执行环境定义了变量或者函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之相关的变量对象变量对象中保存该执行环境中的所有变量和函数。在代码层面,我们是无法访问到这个变量对象,但是JS引擎在处理的时候会用到它。

全局执行环境是最外层的一个执行环境,在浏览器中,全局执行环境被认为是window对象,因为所有全局变量和函数都是作为window对象的属性和方法创建的。

注意:某个执行环境中所有代码执行完毕后,该执行环境会被销毁,保存在其中的所有变量和函数定义也会被销毁。全局环境是在应用退出后才被销毁。

每个函数都会有自己的执行环境,当执行流进入一个函数时,该执行环境被推入到一个环境栈中,在执行完毕后会弹出,将控制权返回给之前的执行环境,JavaScript中的执行流正是由这个机制控制。

当代码在一个执行环境中运行时,会创建变量对象的一个作用域链作用域链是保证对执行环境有权访问的所有变量和函数的有序访问。(注意这里说的有权访问)作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个执行环境是一个函数,则将其活动对象作为变量对象。活动对象在最开始的时候只包含一个arguments对象。作用域链的下一个变量对象是来自包含(外部)的执行环境。再下一个变量对象则来自于下一个包含环境,以此类推,最外层的变量对象来自于全局执行环境。

标识符的解析过程也就是沿着作用域链一级一级的搜索,直到找到标识符为止,否则就会发生错误。

var color = "blue"

function changeColor() {
    if(color == "blue"){
        color = "red"
    }else {
        color = "blue"
    }
}

changeColor()

如上,changeColor包含的作用域链包含它自己的变量对象和全局环境的变量对象,为什么能在函数内访问color,这就是通过作用域链找的。

3. JavaScript中的垃圾收集

在JavaScript中,执行环境会负责管理代码执行过程中使用的内存。当不再使用的变量,就会释放其占用的内存,垃圾收集器会按照固定的时间间隔去执行这个操作。

JavaScript中函数的局部变量只有在函数执行的过程中存在,在执行的过程中会为局部变量在栈或者堆内存中分配响应的空间,以便存储它们的值,然后在函数中使用这些变量,直至函数执行结束。

关于垃圾回收的方法有标记清除,引用计数这两个方法,对于其原理有兴趣的同学可以去研究研究~

总结:今天主要讲了基本数据类型和引用数据类型的区别;关于函数参数是值传递的证明;函数的执行环境,变量对象以及作用域链的形成。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91966.html

相关文章

  • 重温基础】22.内存管理

    摘要:内存泄露内存泄露概念在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。判断内存泄漏,以字段为准。 本文是 重温基础 系列文章的第二十二篇。 今日感受:优化学习方法。 系列目录: 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理) 【重温基础】1-14篇 【重温基础】15.JS对象介绍 【重温基础】16.JSON对象介绍 【重温基础】1...

    Pandaaa 评论0 收藏0
  • 重温基础】19.闭包

    摘要:系列目录复习资料资料整理个人整理重温基础篇重温基础对象介绍重温基础对象介绍重温基础介绍重温基础相等性判断本章节复习的是中的关于闭包,这个小哥哥呀,看看。这里随着闭包函数的结束,执行环境销毁,变量回收。 本文是 重温基础 系列文章的第十九篇。今日感受:将混乱的事情找出之间的联系,也是种能力。 系列目录: 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理) 【重温基础】...

    nanfeiyan 评论0 收藏0
  • 夯实JS系列--变量作用域和内存问题

    摘要:作用域链的用途,是保证对执行环境有权访问的变量和函数的有序访问。全局执行环境始终是作用域链的最后一个对象。延长作用域链虽然执行环境的类型只有两种。 最近在忙于写一个react+node的全栈博客demo,没有时间更新文章。但是还是觉得这样一忙起来不更新是不应该的。正好在空闲上下班地铁上都会再去细读js原生知识。所以打算整理、总结、系统性的分享给大家。 基本类型和引用类型 在ECMASc...

    sihai 评论0 收藏0
  • JS脚丫系列】重温闭包

    摘要:内部的称为内部函数或闭包函数。过度使用闭包会导致性能下降。,闭包函数分为定义时,和运行时。循环会先运行完毕,此时,闭包函数并没有运行。闭包只能取得外部函数中的最后一个值。事件绑定种的匿名函数也是闭包函数。而对象中的闭包函数,指向。 闭包概念解释: 闭包(也叫词法闭包或者函数闭包)。 在一个函数parent内声明另一个函数child,形成了嵌套。函数child使用了函数parent的参数...

    MartinDai 评论0 收藏0
  • 重温基础】4.函数

    摘要:本文是重温基础系列文章的第四篇。系列目录复习资料资料整理个人整理重温基础语法和数据类型重温基础流程控制和错误处理重温基础循环和迭代本章节复习的是中的基础组件之一,函数,用来复用特定执行逻辑。箭头函数不能使用命令,即不能用作函数。 本文是 重温基础 系列文章的第四篇。今日感受:常怀感恩之心,对人对己。 系列目录: 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理) 【重温基...

    maxmin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<