由于工作中遇到一些概念性问题觉得有些模糊不清,所以重新拿起来被誉为javascript经典教程的红宝书复读一边。摘抄部分内容以便以后使用以及加深记忆。
发表顺序按照读书顺序排列,最后统一整理归档。
本博客内容由
第三章 基本概念
小结
- ### ECMAScript中基本数据类型包括Undefined、Null、Boolean、Number和String。
- ### ECMAScript没有为整数和浮点数值定义不同的数据类型,Number类型可以用于表示所有的数值。
- ### ECMAScript中也有一种复杂的数据类型,即Object类型,该类型是这门语言中所有对象的基础类型。
ECMA中的函数与其他语言中的函数有诸多不同
无需指定函数的返回值,因为任何ECMAScript函数都可以在任何时候返回任何值
实际上未指定返回值的函数返回的是另一个特殊的Undefined
ECMAScript中也没有函数签名的概念,因为其函数参数是以包含一个零活多个值的数组的形式传递的。
可以向函数传递任意数量的参数,并且可以通过arguments对象来访问这些参数。
由于不存在函数签名,所以函数不能重载。
第四章 变量、作用域和内存问题
变量、作用域和内存问题。
4.1 基本类型和引用类型的值
在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。
5种基本数据类型:Undefined、Null、Boolean、String、Number
基本类型值是按值访问的,因此可以操作保存在变量中的实际的值。
引用类型的值是保存在内存中的对象,Javascript不允许直接访问内存中的位置。所以,操作对象时,实际上在操作对象的引用而不是实际的对象。
很多语言中,字符串以对象的形式来表示,因此被认为是引用类型。ECMAScript放弃了这一传统
对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法。
不能给基本类型的值添加属性,虽然这样做不会导致任何错误。
所以 只能给引用类型值动态的添加属性
4.1.2 复制变量值
从一个变量向另一个变量复制基本类型值和应用类型值时,存在不同。
var num1 = 5;
var num2 = num1;
num1中的值是5,num2中也保存了值5.但num2中的5和num1中的5是完全独立的,该值只是num1中5的一个副本。这两个变量可以参与任何操作而不会互相影响。
从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。
不同的事,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。两个变量实际上引用同一个对象。
因此,改变其中一个变量,就会影响另一个变量
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "皮皮虾";
console.log(obj2.name); //"皮皮虾"
4.1.3 传递参数
ECMAScript中给所有函数的参数都是按值传递的。
把外部函数的值复制给函数内不得参数,就和把一个值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,而引用类型值得传递,则如同引用类型变量的复制一样。
参数只能按值传递
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,或者说就是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 = "Nicolas";
}
var person = new Object();
setName(person);
alert(person.name) // 'Nicolas'
上面的代码创建了一个对象,并保存在变量person中。然后这个对象被传递到setName()函数之中后被复制给了obj。
在函数内部,obj和person引用的是同一个对象。即使这个对象时按值传递的,obj也会按引用来访问同一个对象。
于是,当函数内部为obj添加name属性后,函数外部的person也将有所反应。
因为person指向的对象在堆内存中只有一个,而且是全局对象。
错误:在局部作用域中修改的对象会在全局作用于中反映出来,就说明参数是按引用传递的。
为了证明对象是按值传递的的,下面例子:
function setName(obj) {
obj.name = "Nicolas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
console.log(person.name);
如果person是按引用传递的,那么person就会自动被修改为指向其name属性值为”greg”的新对象。
但是其实访问person.name时,现实的仍是”Nicolas”。
这说明即使在函数内部修改了参数的值,原始的引用仍然保持不变。
实际上,在函数内部重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即销毁。
4.1.4 检测类型
使用typeof操作符,如果变量的值是一个对象或者null,则typeof操作符会返回”Object”;
instanceof操作符:
result = variable instanceof constructor
如果变量时给定引用类型的实例,那么instanceof操作符就会返回true;
4.2执行环境及作用域
执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。
每个执行环境都有一个与之关联的变量对象。
全局执行环境是最外围的一个执行环境。
某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。
每个函数都有自己的执行环境。当执行流进入一个函数式,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
当代吗在一个环境中执行时,会创建变量对象的一个作用域链。
作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。
作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。
活动对象在最开始只包含一个变量,即arguments对象(这个对象在全局环境不存在)。
作用域链的下一个变量对象来自外部环境,而再下一个变量对象则是来自下一个包含环境。这样一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。
函数参数也被当做变量来对待,因此其访问规则与执行环境中的其他变量相同
4.2.1 延长作用域链
有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。
当执行流进入下列任何一个语句时,作用域链就会得到加长:
- try-catch语句的catch块
- with语句。
这两个语都会在作用域链的前端添加一个变量对象。
对于with语句来说,会将指定的对象添加到作用域链中。
对于cahtch语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
4.2.2 没有块级作用域
javascript 没有块级作用域。
if(true){
var color = "blue";
}
alert(color); // 'blue';
javascript中,if,for语句中变量声明会将变量添加到当前的执行环境中。
1.声明变量
使用var 生命的变量回自动被添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境;
在with语句中,最接近的环境是函数环境。
如果初始化变量时没有使用var声明,该变量回自动被添加到全局环境。
在JavaScript中,不声明而直接初始化变量是一个常见的错误做法,因为这样可能会导致意外。在严格模式下,初始化未经声明的变量会导致错误。
2.查询标识符。
如果局部环境中存在着同名标识符,就不会使用位于父环境中的标识符。
4.3 垃圾收集
JavaScript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。
函数中局部变量的正常生命周期。局部变量旨在函数执行的过程中存在。而在这个过程中,会为局部变量在栈(或堆)内存上分配相应的空间,以便存储他们的值。然后在函数中使用这些变量,直至函数执行结束。此时,局部变量就没有存在的必要了,因此可以释放他们的内存以供将来使用。
4.3.1 标记清楚
JavaScript最常用的辣鸡收集方式是标记清除(mark-and-sweep)。
当变量进入环境时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能回用到他们。
当变量离开环境时,则将其标记为“离开环境”。
4.3.2 引用计数
另一种不太常见的辣鸡收集策略叫引用计数。
引用计数的含义是跟踪记录每个值被引用的次数。
当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。
如果同一个值又被赋给另一个变量,则该值得引用次数加1.
相反,如果包含这个值引用的变量又取得了另外一个只,则将这个值的引用次数减1.
当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间收回来。
这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。
循环引用
var element = document.getElementById('some_element');
var myObject = new Object();
myObject.element = element;
element.someObject = myObject;
在一个DOM元素与一个javascript对象之间创建了循环引用,变量myObject有一个名为element的属性指向element对象;而变量element也有一个属性名交someObject回指myObject。由于存在这个循环引用,即使将DOM从页面移除,它也永远不会回收。
为了避免循环引用,最好在不使用他们的时候手动断开原生JavaScript与DOM元素之间的链接。
myObject.element = null;
element.someObject = null;
一旦数据不再游泳,最好通过将其值设置为null来释放其引用。这个方法叫做解除引用。
这个做法适合大多数全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用。
解除一个值得引用并不意味着自动回收该值所占用的内存。解除引用真正的作用是让值脱离执行环境,以便下次垃圾收集器运行时将其收回。