Tips: 内容为知识梳理
目录
1. 基本类型和引用类型
JavaScript的变量值分为基本类型值和引用类型值,这两个值在进行操作时有着很大的不同。比如复制方式,传递方式等。
- 基本类型值一般指undefined、null、boolean、number、string的值,它们都是按值访问的,即操作实际变量中的值,而不需引用其他的变量。
- 引用类型值一般指对象,它是按引用访问的,即不能操作实际的对象,对象存在于一个堆内存的位置,JavaScript不允许直接访问,需要构建引用。同一个对象可以被多个变量引用。
1.1 动态属性
对于两个不同的值,定义则和正常的一样,创建变量,为变量赋值。对基本类型值不能新增属性,但对于引用类型值,则可以进行添加属性和方法。这时,为对象添加属性和方法则不是进行引用,而是直接对对象进行操作,操作后,其他所有对该对象的引用都会有这个新增的属性或方法。
var person =new object();
person.name="Jhon";
alert(person.name);
以上代码创建了一个对象并保存在变量person里面,然后再新增了name属性,并赋值,最后用alert()访问这个属性。
1.2 复制变量值
对两个值的复制方式也有不同
基本类型: 如
var num1=5;
var num2=num1;
这里的复制,是将原变量的值复制到新变量分配的位置上,然后两者独立,互不影响,也就是两个完全独立的变量了
引用类型:
var ob1=new object();
var ob2 =ob1;
ob1.name="er";
alert(ob2.name)
上面代码展示了对引用类型值的复制,其实质是复制指针,ob1是引用类型,指针指向堆内存里的一个对象,复制之后,将ob2也指向堆内存里的那个对象,也就是说他们现在引用同一个对象。再接着,ob1新增了name属性,这时是对对象的直接操作,ob2也有这个属性,所以alert就会打印出ob2.name的值。
1.3 传递参数
JavaScript中所有函数的参数都是按值传递,不会按引用传递。在这里,基本类型的传递和引用类型的传递又有很大的不同。虽说两者都是按值传递,但是,先来看看:
- 基本类型的传递
和基本类型复制一样,将值复制给函数的局部变量,两个变量互不认识,互不影响,这个很容易理解,就是对局部变量的理解,就不放代码解释了。
- 引用类型的传递
首先,它传递时是按照引用来复制的,就是把指针复制一份给局部变量,让它也指向堆内存中的那个对象,于是,局部变量对对象进行新增属性方法同样也会影响到其他引用这个对象的变量。但这不能说它是按引用传递,因为假如要在函数内部修改局部变量的引用地址,那么是不会影响传进来的那个变量的引用地址的,这里放一段代码:
function setname(obj){
obj.name="GOD";
obj =new object();
obj.name="tony";
}
var person = new object();
setname(person);
alert(person.name);
上面代码定义了person变量,并指向堆中一个对象,接着把值传给了obj,使obj也指向堆中那个对象,接着在函数中新增name属性,这时,也会让person新增name属性,值为“GOD”,因为他两都引用同一个对象。接着函数又为obj重新引用了新的对象,然后又为这个对象新增name属性,这时再看看alert(person.name),输出的仍然是GOD,而不是tony,这是因为,引用新的对象相当于赋了新的值,这个赋值不会影响person变量的值,即person变量原本对原本对象的引用,这是不会改变的,所以说这也是一种按值传递。
1.4 检测类型
JavaScript用instanceof 来检测它是基本类型还是引用类型 语法:result = variable instanceof constructor 其中constructor就是对象名。如果variable是基本类型则返回false,如果是引用类型则返回true。
2. 执行环境和作用域
- 执行环境使变量或函数有权访问其他的数据
- 每个执行环境都有一个变量对象,环境中所有的变量和函数都保存在这个对象中,我们无法访问,但解析器可以在后台访问
- 每个函数都有自己的执行环境,当执行流进入这个函数,环境就会推入一个环境栈里面,用完后,再弹出来,把控制权交给先前的执行环境
- 每一个执行环境都会有一个作用域链,这个链用于保证对变量和函数的有序访问
- 作用域链前端是当前环境的变量对象,如果环境是函数,则变量对象为arguments对象,下一个变量对象来自外部环境,下下个变量对象则来自下一个包含环境,这样一来,全局环境的变量对象始终是作用域链的最后一个对象
- 标识符解析是一个延作用域链搜索标识符的过程,从前端开始,若找不到则报错,即无法访问或不存在
- 每个环境可以向上搜索作用域链,而不能向下搜索,即在函数中可以使用全局变量,而在全局环境中却不能使用局部变量,因为在全局环境的作用域链中找不到这个局部变量
2.1 延长作用域链
使用try-catch的catch块或with语句可以延长作用域链
- 对于catch,会创建一个新的变量对象,其中包含的是被抛出错误对象的声明
- 对于with,如with(object),将指定的对象添加到作用域链的前端,这个变量对象就可以访问object的所有属性和方法
- 注意,假如在with中定义一个变量,那么这个变量是可以在它的外部环境中被使用和访问的
2.2 没有块级作用域
对于JavaScript来说,如with,for,if这种带有花括号的代码块,其实不会构建自己的作用域,它们还是属于当前的环境的,比方说,在if中定义一个变量,在c语言中这个变量会被当做是if的局部变量,而在JavaScript中不会,这个变量是可以供当前环境使用的
注意点!
- 若不适用var进行定义变量,而直接对未定义变量赋值,那么这个变量就成为了全局变量,不推荐这么做
- 对于标识符的搜索过程,是一级一级向上搜索的,如果搜索到了就会停止,从局部到全局,假如存在两个同名标识符,那么就要看他们在作用域链的位置,谁最先被搜索到就用谁的值
3. 垃圾收集
在编写JavaScript程序时,程序员不需要关心内存使用问题,因为JavaScript有自动垃圾收集机制,其原理是:找出不再使用的变量,释放其占用的内存,那么怎么找呢?具体有以下两个策略:标记清除和引用计数。
3.1 标记清除
当变量进入环境则将该变量标记为“进入环境”,当变量离开环境则标记为“离开环境”。而它是如何标记的我们不用管。 在垃圾收集器运行时,把内存中所有变量都加上标记,再把被引用的变量的标记去除,而在此之后又被加上标记的变量即为即将删除的变量。 到2008年为止,IE、Firefox、Opera、Chrome和 Safari都采用标记清除方式。
3.2 引用计数
这种方式不太常见,工作原理是,跟踪记录每个值被引用的次数,当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。 如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取 得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0时,就可以将其内存空间回收了。
3.3 管理内存
分配给 Web 浏览器的可用内存数量通常要比分配给桌面应用程序的少,这样做是为了放置Web耗尽系统全部内存而导致系统崩溃,因此确保占用最少的内存可以让页面有更好的性能,而优化内存占用的最佳方式,就是为执行 中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为 null 来释放其引用——这个 做法叫做解除引用