今天学习了javascript 的变量和作用域的基本知识,对于以前在开发中遇到的一些不懂的小问题也有了系统的认识,收获还是比较多的。

【基本类型和引用类型】

ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。我们常见的五种基本类型的值:Undefined、Null、Boolean、Number 和 String ,这五种基本数据类型是按值访问的,因此可以操作保存在变量中的实际的值。引用类型的值是保存在内存中的对象,也就是说不能够直接操作对象的内存空间,引用类型的值是按引用访问的。注意:我们不能给基本类型的值添加属性,例如以下代码:

var name = 'name1';
name.age = 22;
console.log(name.age); // undefined

【复制变量值】

从一个变量向另一个变量复制基本类型值很引用类型值时存在不同的情况,如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上,例如:

var num1 = 5;
var num2 = num1;

通过以上的复制方式,num1 中的 5 和 num2 中的 5 是完全独立的,也就是说修改 num1 或者 num2 是不会影响到另外一个值的,我们参考如下的代码:

var num1 = 5;
var num2 = num1;
console.log(num1,num2); // 5 5
num1 = 6;
console.log(num1,num2); // 6 5

下面的表格形象的展示的复制基本类型值的一个过程:

复制前的变量对象 复制后的变量对象
       
    num2

5

(Number类型)

num1

5

(Number类型)

num1

5

(Number类型)

当从一个变量向另一个变量复制引用类型的值的时候,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。但是这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量引用的其实是一个值。因此,改变任意一个变量都会影响到另外一个变量。例如以下代码:

var per1 = new Object();
var per2 = per1;
per1.name = 'name1';
console.log(per1.name,per2.name); // name1 name1
per1.name = 'name2';
console.log(per1.name,per2.name); // name2 name2
per2.name = 'name3';
console.log(per1.name,per2.name); // name3 name3

下图详细的展示了保存在变量对象中和保存在堆中的对象之间的关系:

【传递参数】

ECMAScript 中所有函数的参数都是按值传递的,也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另外一个变量一样。基本类型值的传递如同基本类型变量的复制一样,引用类型的传递如同引用类型变量的复制一样。例如以下代码:

function addNum(num){
num += 10;
return num;
}
var count = 20;
var result = addNum(count);
console.log(count,result); // 20 30

这里的函数 addNum() 有一个参数 num ,而参数实际上是函数的局部变量。在调用这个函数时,变量 count 作为参数被传递给函数,这个变量的值是20。于是,数值20被复制给参数 num 。但是 num 的改变并不能影响 count 的值,所以 count 输出的值仍然是20 。再举一个例子:

function setName(obj){
obj.name = 'name5';
}
var newObj = new Object();
setName(newObj);
console.log(newObj.name); // name5

这段代码看起来是在局部作用域中修改了 newObj 的 name 的值,在全局作用域也反映出来了,这样的理解是错误的。再看一段代码:

function setName(obj){
obj.name = 'name6';
var obj = new Object();
obj.name = 'name7';
}
var newObj = new Object();
setName(newObj);
console.log(newObj.name); // name6

对比两段代码可以看出,如果 newObj 是按引用传递的,那么 newObj 的 name 属性应该是 name7 才对,但是 name 属性是 name6,这说明及时在函数内部修改了参数的值,但原始的引用仍然保持未变。

【执行环境和作用域】

执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。全局执行环境是最外围的一个执行环境,在Web浏览器中,全局执行环境被认为是 window 对象,因此所有的全局变量和函数都是作为 window 对象的属性和方法创建的。每个函数都有自己的执行环境。当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,时钟都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。作用域链中的下一个变量对象来自包含(外部)环境,再下一个变量对象则来自下一个包含环境。全局执行环境的变量对象始终都是作用域链的最后一个对象。标识符解析是沿着作用域链一级一级的搜索标识符的过程。请看如下代码:

var color = 'blue';
function changeColor(){
if(color == 'blue'){
color = 'red';
}
else{
color = 'blue';
}
}
changeColor();
console.log(color); // red

函数 changeColor() 的作用域包含两个对象:它自己的变量对象(其中定义着 arguments 对象)和全局环境的变量对象。当 changeColor 在执行的时候,在自己的作用域中并没有找到 color ,于是便到全局环境中找,找到了 color 的值为 blue ,然后按照 changeColor() 函数的规则将 color 的值设置为 red 。再看一段更加详细的代码:

//这里只能访问 color
var color = 'blue';
function changeColor(){ //这里可以访问 color 、newColor ,但是不能访问 temColor
var newColor = 'red';
function swapColor(){ //这里可以访问 color 、newColor 和 temColor
var temColor = newColor;
newColor = color;
color = temColor;
}
swapColor();
console.log(color,newColor); //red blue
}
function showColor(){
console.log(color); //blue
}
showColor();
changeColor();

代码的内容自己体会一下,这里不做详细的解释。

【没有块级作用域】

看如下的代码:

for(var i = 0;i < 10;i ++){
i += 1;
}
console.log(i); //

对于有块级作用域的语言来说, for 语句初始化变量的表达式所定义的变量,只会存在于循环的环境之中。在 javascript 中, i 并会在 for 循环执行结束后被销毁,反而被添加到了当前的执行环境(全局环境)中。

1.声明变量

使用 var 声明的变量会自动被添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境。请看如下代码:

function addNum(num1,num2){
var num = num1 + num2;
return num;
}
var result = addNum(10,20);
console.log(result); //
console.log(num); // num is not defined

以上代码如果不使用 var 声明 num 的话是不会导致错误的,例如:

function addNum(num1,num2){
num = num1 + num2;
return num;
}
var result = addNum(10,20);
console.log(result); //
console.log(num); //

2.查询标识符

当在某个环境中为了读取或写入而引用一个标识符时,必须通过搜索来确定该标识符实际代表什么。搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。如果在局部环境中找到该标识符,搜索过程停止,变量就绪。如果在局部变量中没有找到该变量名,则继续沿作用域链向上搜索。搜索过程会一致追溯到全局环境变量。如果在全局环境变量也没有找到该标识符,说明该变量尚未定义。请查看以下代码:

var color = 'blue';
function getColor(){
return color;
}
console.log(getColor()); // blue

getaColor() 在搜索局部变量的时候没有找到 color ,而函数执行语句是一定要返回一个 color ,与是便到全局环境变量中去搜索,找到了 color 。需要注意的是,搜索的过程中如果存在一个局部变量的定义,则搜索会自动停止,不再进入另一个变量对象。也就是说,如果局部环境存在着同名标识符,就不会使用位于父环境的标识符,例如以下代码:

var color = 'blue';
function getColor(){
var color = 'red';
return color;
}
console.log(getColor()); // red

作用域链对于理解闭包的概念至关重要,还望能够加深理解。

【javascript 变量和作用域】的更多相关文章

  1. javascript变量的作用域

    javascript变量的作用域 基本类型和引用类型 基本类型值指的是简单的数据段,而引用类型值指的是那个可能由多个值组成的对象  讲一个值赋值给变量时,javascript解析器首先要确定是基本类型 ...

  2. javascript 函数初探 (三)--- javascript 变量的作用域

    javascript 变量的作用域: 这是一个至关重要的问题.特别是当我们从别的语言转向javascript时,必须要明白一点,即在javascript中,变量的定义并不是以代码块作为作用域的,而是以 ...

  3. 深入理解 JavaScript 变量的作用域和作用域链

    一个变量的作用域(scope)是程序源代码中定义这个变量的区域.简单的说,作用域就是变量与函数的可访问范围.全局变量拥有全局作用域,在JavaScript代码中的任何地方都有定义.局部变量是在函数体内 ...

  4. JavaScript变量和作用域

    认识JavaScript中的变量 JavaScript中的变量有两种类型,一种是基本类型.一种是引用类型. 基本数据类型:Defined,Null,Boolean,Number,String.注意St ...

  5. JavaScript变量的作用域和函数的作用域的区别

    变量作用域和函数作用域都涉及到变量值的变化,本文旨在让大家明白他们之间的区别 变量的作用域: 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接 ...

  6. JavaScript - 变量,作用域,内存

    JavaScript 变量可以用来保存两种类型的值:基本类型值和应用类型值.基本类型的值源自以下5种基本数据类型:Undefined.Null.Bollean.Number和String. 所有变量都 ...

  7. 深入浅出 JavaScript 变量、作用域和内存 v 0.5

    本文主要从原理入手分享变量和作用域的相关知识,最后结合本文所分享知识,再次深入了解下闭包的运行原理. 主要参考<JS高级程序设计> <JS权威指南> <高性能 JS> ...

  8. [从jQuery看JavaScript]-变量与作用域链

    jQuery片段: var // Will speed up references to window, and allows munging its name. window = this, //  ...

  9. [label][JavaScript][The Defined Guide of JavaScript] 变量的作用域

    变量的作用域 一个变量的作用域(scope)是程序中定义这个变量的区域. 全局(global)变量的作用域(scope)是全局性的,即在JavaScript代码中,它处处都有定义.    而在函数之内 ...

随机推荐

  1. 删除mysql服务

    当我们卸载完MySQL后重新装MySQL提示 The service already exists! 是因为服务名称还残留在系统里 打开cmd 输入下面 sc delete mysql 这个mysql ...

  2. Mysql性能优化之缓存参数优化

    数据库属于 IO 密集型的应用程序,其主要职责就是数据的管理及存储工作.而我们知道,从内存中读取一个数据库的时间是微秒级别,而从一块普通硬盘上读取一个IO是在毫秒级别,二者相差3个数量级.所以,要优化 ...

  3. linux 清空文件内容命令

    清空文件内容命令 $ echo "" >log.log > 是重写,覆盖式 >>是尾部追加

  4. Android_Intent_passValueForResult

    当SecondActivity需要回传参数时:1. MainActivity启动SecondActivity时需要调用startActivityForResult() 并定义请求码2. SecondA ...

  5. CentOs6.8安装Git并安装oh my zsh

    (一)git安装 1.下载git2.4.9或其他版本 Index of /pub/software/scm/git git各个版本下载链接: https://www.kernel.org/pub/so ...

  6. css实现基础几何图形

    我们知道,css3通过border-radius.animation.transform等“新”特性可以绘制很多精致的图形.比如腾讯企鹅Logo.超能陆战队中的大白机器人.太阳系.小黄人.叮当猫.安卓 ...

  7. ASP.NET MVC(一) 什么是Razor

    Razor 是一种向网页添加基于服务器的代码的标记语法 Razor 不是编程与语言.它是服务端标记语言. 当网页被写入浏览器时,基于服务器的代码能够创建动态内容.在网页加载时,服务器在向浏览器返回页面 ...

  8. WPF窗体视图中绑定Resources文件中字符串时,抛出:System.Windows.Markup.StaticExtension

    问题描述: 在Resources.resx定义了一个静态字符串字段Title,并在WPF窗体视图中绑定为窗体的标题: Title="{x:Static local:Resources.Tit ...

  9. rc4加密

    function RC4(Expression, Password: string): string; var RB : array[0..255] of Integer; X, Y, Z: long ...

  10. 最近整理的一些行列转换sql(有自己的,有别人的),留作记录

    --case when 经典用法SELECT * FROM        (SELECT 1 NUM,              '奖项金额',              SUM(CASE WHEN ...