Javascript的变量及作用域,作用域链

变量命名同标识符的规则。
可以使用一个var声明多个变量,用逗号分开。(var year=2017,month=07;)
可以在声明变量的同时对它进行赋值,也就是初始化值(var year=2016;);没有初始化的变量则自动取值为undefined,表示未定义。
声明变量时不需要指定变量的类型,根据变量的值确定。
ECMAScript的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据.

var box=100;
alert(box);//var box声明变量
var box=100;//声明变量并初始化
alert(box);//以弹窗的方式输出box的值
var box;
alert(box);//box未定义,弹出undefined
alert(age);//报错,age不存在,但typeof返回undefined 

变量可以初始化后再次改变(可以同时改变为不同的类型),但对于后期维护带来困难,而且性能也不高,导致成本很高!

var box=”小米”;
box=100;//不需要重复地使用var来声明同一个变量
alert(box);//弹出100

变量包含的值的类型:

ECMAScript变量包含两种不同的类型的值:基本类型值和引用类型值。将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。基本类型的值指的是保存在栈内存中的简单数据,源自5种基本数据类型:undefined,null,string,number,boolean,基本类型值完全保存在内存中的一个位置,分别占有固定大小的空间,按值来访问(可以操作保存在变量中的实际的值)。引用类型值是指保存在堆内存中的对象,指object,变量中保存的实际上只是一个指针,指向内存中的另一个位置,该位置保存对象。也就是说,如果赋值的是引用类型的值,由于这种值的大小不固定,因此不能把他们保存在栈内存中,而必须在堆内存中为这个值分配空间,但内存地址大小是固定的,,因此可以将内存地址保存在栈内存中。当查询引用类型的变量时,按引用访问(javascript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间.在操作对象时,实际上在操作的是对象的引用而不是实际的对象)。先从栈中读取内存地址,再通过地址找到堆中的值。

  1. 动态属性:定义基本类型和引用类型都是创建一个变量并为该变量赋值。但是基本类型的值无法添加属性。
var box=new Object();//创建引用类型
box.name=’小米’;//添加一个属性
alert(box.name);//输出
 var box=’小米’;//创建基本类型
box.age=28;//添加一个属性
alert(box.age);//undefined
  1. 复制变量值:在变量复制方面,不管是基本类型还是引用类型,复制的都是栈内存的数据,堆内存的数据无法复制。基本类型复制的是值本身,引用类型复制的是地址。

从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本.从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象.

//如果从一个变量向另一个变量复制基本类型的值,会在旧变对象上创建一个新值,然后把该值复制到为新变量分配的位置上
var box=’小米’;
        var box2=box;
        alert(box2);//弹出小米
box2是box的一个副本,但是基本类型是完全独立的。box和box2两个变量分别操作时互不影响。
var box=’小米’;
var box2=box;
box=’kkk’;
alert(box2);//弹出小米
alert(box);//弹出kkk
var box=new Object();
box.name=’小米’;
var box2=box;//把引用地址复制给box2
box2.name=’kkk’;//因为指向的是同一个object,不管修改谁, 都会改变
alert(box2.name);//弹出Kkk
alert(box.name);//弹出kkk
  1. 传递参数:ECMAscript中所有函数的参数都是按值传递的。
function box(num){
       num+=10;
      return num;
}
var count=50;
alert(box(count));//60
alert(count);//50.在调用函数时,变量count作为参数被传递给函数,变量的值是50,于是数值50被复制给了参数num

如果存在按引用传递的话,那么函数里的变量将会是全局变量,在外部也可以访问。但ECMAscript函数的参数都是局部变量,没有按引用传递。

function box(obj){
  obj.name=’小米”;
  var obj=new Object();
  obj.name=’kkk’;
}
var person=new Object();
box( person);
alert( person.name);//弹出小米.创建一个对象,并将其保存在person变量中,person变量被传递到box()函数中之后被复制给obj,在这个函数内部,obj和person引用的是同一个对象.在函数内部,为obj重新定义了一个对象,如果是按引用传递的话,person.name就会被自动修改为新值,但并不是.
  1. 检测类型:
  • Typeof:要检测一个变量的类型,可以通过typeof运算符来判别。(在检测基本数据类型的时候好用,但检测引用类型的时候,并不能具体检测到到底是什么类型的object对象).字符串返回string类型字符串;数值返回number类型字符串;布尔值返回boolean类型字符串;未定义值返回undefined类型字符串;对象或null返回object类型字符串;函数返回function字符串。6种数据类型会返回5种类型字符串,函数是对象,会返回function字符串。

数据类型首字母大写,返回的类型字符串小写。

var box=’小米’;
alert(typeof box);//弹出string
var box={};//空的对象,表示这个对象已经创建,只是里面没有东西。或者var box=new Object();
alert(typeof box);//box是Object数据类型,值是[object Object],类型返回的字符串是object
var box=null;//空对象,表示没有创建,就是Null
alert(typeof box);//box是Null数据类型,值是null,类型返回的字符串是object

function box(){};
alert(typeof box);//box是Function函数,值是function box(){}本体,返回的字符串是function

typeof操作符可以操作变量,也可以操作字面量。

alert(typeof 200);//返回number
  • Instanceof: 采用instanceof运算符检测引用类型。(当使用instanceof运算符检测基本类型的时候,会返回false)
var box=[1,2,3];
alert(box instanceof Array);//弹出true
var box2=/g/;
alert(box2 instanceof RegExp);//弹出true
var box3={};
alert(box3 instanceof Object);//弹出true
var box=’小米’;
alert(box instanceof String);//弹出false
var box=new String(’小米’);//以对象的形式处理字符串
alert(box instanceof String);//弹出true
  1. 执行环境:每个函数运行时都会产生一个执行环境,js为每一个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中。
  • 全局执行环境:是最外围的执行环境。在web浏览器中,全局执行环境被认为是window对象,因此所有的全局变量和函数都是作为window对象的属性和方法创建的。
var box=’blue’;//声明一个全局变量
function setBox(){
       alert(window.box);//全局变量即window的属性
}
window.setBox();//全局函数即window的方法

js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中。而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。
当执行环境中的所有代码被执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。如果是全局环境下,需要程序执行完毕,或者网页被关闭才会销毁。(如果在函数中使用var定义一个变量,那么这个变量在函数退出后就会被销毁)每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。(全局的window对象;局部的环境也有一个类似于window的变量对象,我们无法访问这个对象,但解析器处理数据时后台会使用它)
在这里插入图片描述
在这里插入图片描述

  • 全局变量:定义在所有函数之外,作用于整个代码的变量。
  • 局部变量:定义在函数体内,只作用于函数体的变量。

使用var声明的变量会自动被添加到最接近的环境中.
如果初始化变量时没有使用var声明,该变量会自动被添加到全局环中.
不使用var声明直接赋值的变量,无论在函数体内还是函数体外都是全局变量(不推荐)。
访问局部变量要比访问全局变量更快.因为不用向上搜索作用域链.

var box=’blue’;
function setBox(){
        var box=’red’;//局部变量,范围在setBox()里
        alert(box);//弹出red               
 }
setBox();
alert(box);//仍然弹出blue
var box=’blue’;
function setBox(box){//通过传参,但也是局部变量,范围在//setBox()里
         alert(box);//弹出red 
}
setBox(‘red’);
alert(box);//仍然弹出blue

if语句、for循环语句的大括号没有封闭作用域的功能,都是全局作
用域。

if(true){
      var box=’blue’;
}
alert(box);//能够弹出blue
  • 作用域链:当代码在一个环境中执行时,会创建变量对象的一个作用域链.作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问.作用域链的前端,始终都是当前执行的代码所在环境的变量对象.作用域链中的下一个变量对象来自包含环境,再下一个变量对象则来自下一个包含环境,一直延续到全局执行环境.全局执行环境的变量对象始终都是作用域链中的最后一个对象.根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问。内部环境可以通过作用域链访问所有的外部环境,但是外部环境不能访问内部环境中的任何变量和函数.每个环境都可以向上搜索作用域链,以查询变量和函数名,但任何环境都不能通过向下搜索作用域链而进入另一个执行环境.
  • 标识符解析是沿着作用域链一级一级地搜索标识符的过程.搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直到找到标识符为止.(如果找不到标识符,通常会导致错误发生)
var color="blue";
function box(){
      var color="red";
     return color;
}
alert(box());//搜素过程首先从局部环境开始,在这里发现了一个名为color的变量.因为变量已经找到,所以搜索立即停止
  • 延长作用域链:有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移出.具体来说,就是当执行流进入try-catch的catch块或with语句时,作用域链就会得到加长.对with语句来说,会将指定的对象添加到作用域链中,并且保存在最上层;对catch语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明.

变量提升:

变量提升:把变量声明提升到当前执行环境的最顶端.

js引擎在读取js代码时会进行两个步骤,第一个步骤是解释,第二个步骤是执行。
所谓解释就是会先通篇扫描所有的Js代码,然后把所有声明提升到顶端,第二步是执行,执行就是操作一类的。

console.log(a);//输出 undefined
var a=10;
//相当于
var a; 
console.log(a);//由于未赋值 所以输出undefined 
a=10;

猜你喜欢

转载自blog.csdn.net/wsln_123456/article/details/84343889