JS之变量、作用域及内存问题(一)

一、基本类型和引用类型的值

基本类型值:简单的数据段;
引用类型值:可能由多个值构成的函数对象,保存在内存中的对象;
复制保存着对象的某个变量时,操作的是对象的引用,但在为对象添加属性时,操作的是实际的对象(JS不允许直接访问内存中的位置,即不能直接操作对象的内存空间)

1. 动态的属性

只能给引用类型动态添加属性
引用类型添加属性:

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

基本类型添加属性:

var name = "Nicholas";
name.age = 27;
alert(name.age); //undefined

2. 复制变量值

基本类型的复制后得到的变量与原变量之间相互独立,而引用类型复制后得到的值与原变量之间互相影响
基本类型复制变量值:
在这里插入图片描述引用类型复制变量值:
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间,这个值的副本是一个指针(指向存储在堆中的一个对象)
在这里插入图片描述

3.传递参数(ES 中所有函数的参数都是按值传递的)

在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,也是 arguments对象中的一个元素

function addTen(num){
    num += 10;
    return num;
 }
 var count = 20;
 var result = addTen(count);
 alert(count); //20,无变化
 alert(result); //30

在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量(这个局部变量的变化会反应在函数函数的外部)

function setName(obj){
    obj.name = "Nicholas";
    }
    var person = new Object();
    setName(person);
    alert(person.name); //"Nicholas"
函数内部,obj 与 person 引用的是同一对象,在函数内部为 obj 添加 name 属性,
函数外部的 person 将会也有反应
因为 person 指向的对象在堆内存中只有一个,而且是全局对象

4. 检测类型

typeof操作符检测基本数据类型

var s = "Nicholas";
alert(typeof s); //string

instanceof操作符检测引用类型

alert(person instanceof Object);  //变量 person 是 Object 吗?

二、执行环境及作用域

执行环境,定义了变量或函数有权访问的其他数据,环境中定义的所有变量和函数都保存在变量对象中,每个函数都有自己的执行环境,执行流进入一个函数时,函数的环境会被推入一个环境栈中,而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境

作用域

一段程序代码中,所用到的变量和函数并不总是可用的,而限定这个变量和函数可用的范围就是它的作用域

var color = "blue";
function changeColor(){
	if(color === "blue"){
		color = "red";
	}else{
		color = "blue";
	}
}
changeColor();
alert("Color is now " + color);

此例中,changeColor()的作用域包含两个对象:自己的变量对象(定义着 arguments对象),全局环境的变量对象

var color = "blue";
function changeColor(){
	var anotherColor = "red";
	function swapColors(){
		var tempColor = anotherColor;
		anotherColor = color;
		color = tempColor;
		//这里可访问color,anotherColor,tempColor
	}
	//这里可访问 color,anotherColor,但不能访问tempColor
	swapColors();
}
changeColor();

全局环境,changeColor()的局部环境,swapColors()的局部环境

内部环境可通过作用域链访问所有的外部环境,但外部环境不能访问内部环境的任何变量和函数

无块级作用域(ES5)

if语句

if(true){
				var color = "blue"
			}
			alert(color);

若在 C、C++或Java中,color会在if语句执行完毕后销毁,而在 JS 中,if语句的变量声明会将变量添加到当前的执行环境,
for语句

			for(var i = 0; i<10; i++){
				doSomething(i);
			}
			alert(i);

对于有块级作用域的语言来说,for语句初始化的表达式所定义的变量,只会存在于循环的环境中,而对 JS 来说,由 for语句创建的变量 i即使在for语句循环结束后,也依旧会存在于循环外部的执行环境中

1. 声明变量

使用var声明的变量会被自动添加至最接近的环境中
函数内部,函数的局部环境;
with语句,函数环境

初始化变量使用var关键字

function add(num1,num2){
				var sum = num1 + num2;
				return sum;
			}
			var result = add(10,20);//30
			alert(sum); //错误

初始化变量未使用var关键字:

function add(num1,num2){
				sum = num1 + num2;
				return sum;
			}
			var result = add(10,20);//30
			alert(sum); //30

2. 查询标识符

在 js 中,如果在局部环境中存在同名的变量,则使用该变量;如果局部环境中不存在该变量,则会沿着作用域链向上一步一步的搜索,如果在全局环境中存在,则使用全局环境中的该变量,如果不存在则会导致错误,

  • 局部环境中的变量
var color = "blue";
			
			function getColor(){
				var color = "black";
				return color;
			}
			
			alert(getColor());

  • 全局环境中的变量
var color = "blue";
			
			function getColor(){
				return color;
			}
			
			alert(getColor());

  • 不存在的变量
function getColor(){
				return color;
			}
			
			alert(getColor());

延长作用域链

  • try-catch语句的catch块;将指定的对象添加到作用域链中
  • with语句;创建一个新的变量对象,其中包含的是被抛出的错误对象的声明
var name = "global";
function test(){
    var name = "sub";
    with(window){
        console.log(name);
    }
}

test(); // -- "global"

作用域链
在这里插入图片描述
检索变量时,会先在最前端的window变量对象中检索;当然,在严格模式下已经禁用了with语句,编程时,最好向后兼容,废弃使用with语句;

参考:
js 作用域链与执行环境

发布了26 篇原创文章 · 获赞 6 · 访问量 1445

猜你喜欢

转载自blog.csdn.net/qiao_xi/article/details/102770883
今日推荐