js 作用域

来源:互联网 发布:双节棍 知乎 编辑:程序博客网 时间:2024/06/03 14:26

作用域的概念太抽象,而且书上或者网上讲解的时候大多以运行结果来解释作用域。
我打算以内存的方式来讲一下作用域的概念,这比较实体一些,容易理解。
js内存分为栈内存堆内存
栈内存用来保存变量以及基础类型的变量的值(变量声明 加上 5种基础类型包括null,undefined,string,number,Boolean),比如a=4,这整体都是保存在栈内存中的
堆内存用来保存对象,比如c={b:33},这个表达式里的{b:33}是保存在堆内存里的栈内存里保存c这个变量以及c所对应的对象的堆内存地址。

栈是一种特殊的线性表,先进后出,后进先出(类似只有一个前门的公交车)
队列也是一种运算受限的线性表,两个门,插入只能在表的一端进行(只进不出)即队头,删除只能在表的另一端进行(只出不进)即队尾
栈内存
队列
堆内存

堆内存只有一个(全局),而栈内存可以有多个

基本类型与引用类型最大的区别实际就是传值与传址的区别。测试用例:

var a = [1,2,3,4,5];var b = a;var c = a[0];alert(b);//1,2,3,4,5 alert(c);//1 //改变数值         b[4] = 6;c = 7;alert(a[4]);//6alert(a[0]);//1

从上面我们可以得知,当我改变b中的数据时,a中数据也发生了变化;但是当我改变c的数据值时,a却没有发生改变。
  这就是传值与传址的区别。因为a是数组,属于引用类型,所以它赋予给b的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。所以b修改的时候,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中。

下面涉及到拷贝(浅拷贝和深拷贝)
浅拷贝的特点是父子对象发生了关联,因为两者的属性值会指向同一内存空间
这里写图片描述

var a = {         key1:"11111"    }function Copy(p) {var c = {};for (var i in p) {            c[i] = p[i];        }return c;  }     a.key2 = ['小辉','小辉'];var b = Copy(a);    b.key3 = '33333';     alert(b.key1);     //1111111     alert(b.key3);    //33333     alert(a.key3);    //undefined
b.key2.push("大辉");alert(b.key2);    //小辉,小辉,大辉alert(a.key2);    //小辉,小辉,大辉

深拷贝的特点是父子对象之间不产生关联,因为两者的属性没有指向同一内存空间,方法是通过递归(把父对象中所有属于对象的属性类型都遍历赋给子对象即可)而不是直接拷贝(即new或者copy)

function Copy(p, c) {var c = c || {};for (var i in p) {if (typeof p[i] === 'object') {              c[i] = (p[i].constructor === Array) ? [] : {};             Copy(p[i], c[i]);           } else {              c[i] = p[i];          }        }return c;  }         a.key2 = ['小辉','小辉'];var b={};     b = Copy(a,b);             b.key2.push("大辉");     alert(b.key2);    //小辉,小辉,大辉     alert(a.key2);    //小辉,小辉

这里写图片描述

每个函数内部保存一个栈内存,在函数内部声明的对象依然保存在堆内存中,可以理解为
每声明一个函数,就开辟了一块新的栈内存,这块内存保存了函数内部声明的所有变量,
而且这块内存拒绝外部直接访问,但是允许在它内部嵌套函数开辟的内存去访问(闭包函数)。
!!所谓的作用域呢,可以理解为函数开辟的这块新内存,作用域链就是该函数能访问的所有内存,
因为函数能访问他上一级函数开辟内存,所以就有了他自己的作用域链。
作用域销毁指的是当函数内部声明的变量没有被嵌套在其内部函数引用时,函数开辟的这块
内存在运行完毕之后就被清空了,里面的变量也就不存在了。

函数参数的理解:
每个函数开辟的栈内存里自动保存了一个变量对象,这个对象呢,是隐式存在的,不能打印出来,
可以认为函数内部的栈内存里的所有变量都是保存在这个对象上的,就像window.a=8;你可以直接
alert(a),而不用挂载在这个对象上调用:alert(window.a=8;),所以函数内部声明的变量也是默认挂载在变量对象上的。这个变量对象默认保存了一个arguments对象,只要函数在调用时传入了参数,arguments对象就会以类似数组的方式依次保存这些参数,即使函数声明时没有定义参数,也依然会保存。
function aa(){console.log(arguments)} ;aa(3,4)
上面这个函数你会发现定义时没有写参数,但在调用时3和4依然被保存在arguments里,
所以function aa(a,b){alert(a)}这样的形式相当于函数被执行时在函数内部声明了一个变量a,b,
这个a和b与传入的参数是没有直接联系的,他们复制的是arguments里面的值。
可以认为上面的函数等价于function aa(){var a=arguments[0],b=arguments[1];alert(a)}

参考链接:http://www.w2bc.com/Article/74904

0 1