先附上图书在线链接:http://www.chinastor.org/upload/2014-12/14122310427265.pdf
记录内容:
近来阅读红皮书,做的些许笔记,因为第一次整理,可能稍显琐碎,望谅解,如发现错误,希望能提出指正,谢谢。2018-05-27
正文:
3.1.5 语句
ECMAScript中的语句以一个分号结尾;
1 var sum = a + b // 即使没有分号也是有效的语句——不推荐 2 var diff = a - b; // 有效的语句——推荐
加上;的好处:
①加上这个分号可以避免很多错误(例如不完整的输入),②开发人员也可以放心地通过删除多的空格来压缩ECMAScript代码(代码行结尾处没有分号会导致压缩错误)。另外,③加上分号也在某些情况下增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了。(利于压缩、避免不完整输入、某些情况下会提供代码性能)
条件控制语句(如if语句)只在执行多条语句的情况下才要求使用代码块,但最佳实践是始终在控制语句中使用代码块——即使代码块中只有一条语句
var bool = true; if(bool){ console.log("ok");//ok }
3.3 变量
ECMAScript的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。
typeof是一个操作符而不是函数,因此例子中的圆括号尽管可以使用,但不是必需的。
console.log(typeof null)//object,从逻辑角度来看,null值表示一个空对象指针,而这也正是typeof操作符检测null值会返回object的原因
使用var声明变量但未对其加以初始化时,这个变量的值就是undefined,未初始化的变量,不能执行真正的操作
如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null而不是其他值。这样一来,只要直接检查null值就可以知道相应的变量是否已经保存了一个对象的引用,如下面的例子所示:
var car = null; if(car != null){ } else{ alert("变量为空") } alert(null == undefined); //true
布尔值转换规则
Boolean | true | false |
String | 任何非空字符串 | ""(空字符串) |
Number | 任何非零数字值(包括无穷大) | 0和NaN(参见本章后面有关NaN的内容) |
Object | 任何对象 | null |
Undefined | n/a① | undefined |
3.4.5 Number类型
var octalNum1 = 070; // 八进制的56 var octalNum2 = 079; // 无效的八进制数值——解析为79 var octalNum3 = 08; // 无效的八进制数值——解析为8 var hexNum1 = 0xA; // 十六进制的10 var hexNum2 = 0x1f; // 十六进制的31 var floatNum1 = 1.1; //浮点数值 var floatNum2 = 0.1; var floatNum3 = .1; // 有效,但不推荐 var floatNum = 3.125e7; // 等于31250000
浮点数值的最高精度是17位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1加0.2的结果不是0.3,而是0.30000000000000004 。(ps:涉及到对浮点数运算的,一般都要进行,如Math.round()、Math.floor()、Math.ceil(),分别对应四舍五入,向下以及向上取整)
console.log(Number.MAX_VALUE,Number.MIN_VALUE) console.log(isFinite(0.0000000000000321))//判值是否属于最小到最大的范围内
NaN本身有两个非同寻常的特点。首先,任何涉及NaN的操作(例如NaN/10)都会返回NaN,这个特点在多步计算中有可能导致问题。其次,
NaN与任何值都不相等,包括NaN本身。例如,下面的代码会返回false:
alert(NaN == NaN); //false
4. 数值转换
有3个函数可以把非数值转换为数值:Number()、parseInt()和parseFloat()。第一个函数,即转型函数Number()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。这3个函数对于同样的输入会有返回不同的结果。
Number()函数转换注意
传入的是布尔值,true1 false0
null值为0 undefined 返回NaN
undefined 返回NaN
字符串,空字符返回0,①011返回11 ②11asdfasdf 返回11 ③hello返回NaN
parseInt()函数转换注意
var num2 = parseInt(""); // NaN var num1 = parseInt("10", 2); //2 (按二进制解析) var num2 = parseInt("10", 8); //8 (按八进制解析) var num3 = parseInt("10", 10); //10 (按十进制解析) var num4 = parseInt("10", 16); //16 (按十六进制解析)
parseFloat()只解析十进制值
var num1 = parseFloat("1234blue"); //1234 (整数) var num2 = parseFloat("0xA"); //0 var num3 = parseFloat("22.5"); //22.5 var num4 = parseFloat("22.34.5"); //22.34 var num5 = parseFloat("0908.5"); //908.5 var num6 = parseFloat("3.125e7"); //31250000
转换为字符串
var age = 11; var ageAsString = age.toString();// 字符串"11" var found = true; var foundAsString = found.toString(); // 字符串"true"
数值、布尔值、对象和字符串值(没错,每个字符串也都有一个toString()方法,该方法返回字符串的一个副本)都有toString()方法。但null和undefined值没有这个方法。.toString(2)里面参数默认是10,为十进制
转型函数String(),这个函数能够将任何类型的值转换为字符串
var value1 = 10; console.log(String(value1)); // "10" null和undefined没有toString()方法,所以String()函数就返回了这两个值的字面量。
Object对象
创建Object类型的实例并为其添加属性和(或)方法,就可以创建自定义对象,如下所示:
var o = new Object();
Object类型是所有它的实例的基础。换句话说,Object类型所具有的任何属性和方法也同样存在于更具体的对象中。
Object的每个实例都具有下列属性和方法。
①constructor:保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor)就是Object()。
②hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例如:o.hasOwnProperty("name"))。
③isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型(第5章将讨论原型)。
④propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in语句(本章后面将会讨论)来枚举。与hasOwnProperty()方法一样,作为参数的属性名必须以字符串形式指定。
⑤toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
⑥toString():返回对象的字符串表示。
⑦valueOf():返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同。
var test = new Object(); test.name = "dading"; console.log(test);//name:dading console.log(test.constructor == Object);//true console.log(test.hasOwnProperty("name"));//true,属性存在当前对象实例中 console.log(test.isPrototypeOf("name"));//false,传入的对象不是是传入对象的原型 console.log(test.propertyIsEnumerable("name"));//true,可循环 console.log(test.name.toLocaleString());//dading console.log(test.name.toString());//dading console.log(test.name.valueOf());//dading
3.5.2 位操作符
0正1负
例如,数值18的二进制表示是00000000000000000000000000010010,或者更简洁的10010
负数,取反加1
1. 按位非(NOT)
var num1 = 25; // 二进制00000000000000000000000000011001 var num2 = ~num1; // 二进制11111111111111111111111111100110 alert(num2); // -26
2. 按位与(AND)
var result = 25 & 3; //按位与操作只在两个数值的对应位都是1时才返回1 alert(result); //1
3. 按位或(OR) | 有一个位是1的情况下就返回1
4. 按位异或(XOR)^ 只有一个1时才返回1
5. 左移
var oldValue = 2; // 等于二进制的10 var newValue = oldValue << 5; // 等于二进制的1000000,十进制的64
6.有符号的右移
var oldValue = 64; // 等于二进制的1000000 var newValue = oldValue >> 5; // 等于二进制的10,十进制的2
7.无符号的右移
var oldValue = -64; // 等于二进制的11111111111111111111111111000000 var newValue = oldValue >>> 5; // 等于十进制的00000111111111111111111111111110(134217726)
3.5.3 布尔操作符
1.逻辑非操作符由一个叹号(!)表示
alert(!NaN); // true
2. 逻辑与 && 第一个条件错误,后面不执行
3. 逻辑或 || 第一个条件对的,后面不执行
3.5.4 乘性操作符
与没有带数值的值相乘,返回NaN
3.5.5 加性操作符
+会拼串
3.5.6 关系操作符
如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
var result = "Brick" < "alphabet"; //true,比较的是两个字符串中对应位置的每个字符的字符编码值。经过这么一番比较之后,再返回一个布尔值。由于大写字母的字符编码全部小于小写字母的字符编码 var result = "23" < "3"; //true 当比较字符串"23"是否小于"3"时,结果居然是true。这是因为两个操作数都是字符串,而字符串比较的是字符编码("2"的字符编码是50,而"3"的字符编码是51) var result = "a" < 3; // false,因为"a"被转换成了NaN由于字母"a"不能转换成合理的数值,因此就被转换成了NaN。根据规则,任何操作数与NaN进行关系比较,结果都是false。
3.5.10 逗号操作符
var num1=1, num2=2, num3=3;
3.6.5 for-in语句
var arr = [1,2,3,4,5,6,]; for(var prop in arr){ console.log(prop,arr[prop]) }
3.6.7 break和continue语句
var num = Math.floor(Math.random()*5 +1); console.log(num) switch (num) { case 1: console.log("1") break; case 2: console.log("2") break; default: console.log("不是1和2"); } }
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 = "Nicholas"; obj = new Object(); obj.name = "Greg"; } var person = new Object(); setName(person); alert(person.name); //"Nicholas"
在setName()函数中添加了两行代码:一行代码为obj重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name属性。在把person传递给setName()后,其name属性被设置为"Nicholas"。然后,又将一个新对象赋给变量obj,同时将其name属性设置为"Greg"。如果person是按引用传递的,那么person就会自动被修改为指向其name属性值为"Greg"的新对象。但是,当接下来再访问person.name时,显示的 值仍然是"Nicholas"。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。
可以把ECMAScript函数的参数想象成局部变量。
4.2 执行环境及作用域
某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出——例如关闭网页或浏览器——时才会被销毁)。
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。
对于这个例子中的swapColors()而言,其作用域链中包含3个对象:swapColors()的变量对象、changeColor()的变量对象和全局变量对象。swapColors()的局部环境开始时会先在自己的变量对象中搜索变量和函数名,如果搜索不到则再搜索上一级作用域链。changeColor()的作用域链中只包含两个对象:它自己的变量对象和全局变量对象。这也就是说,它不能访问swapColors()的环境。(从内往外找,外不能访问内(环境))
4.2.2 没有块级作用域
if(true){ var color = "blue"; let color1 = "blue"; } console.log(color,color1) //undefined,blue
2. 查询标识符
var color = "blue"; function getColor(){ return color; } alert(getColor()); //"blue"
调用本例中的函数getColor()时会引用变量color。为了确定变量color的值,将开始一个两步的搜索过程。首先,搜索getColor()的变量对象,查找其中是否包含一个名为color的标识符。在没有找到的情况下,搜索继续到下一个变量对象(全局环境的变量对象),然后在那里找到了名为color的标识符。因为搜索到了定义这个变量的变量对象,搜索过程宣告结束。图4-4形象地展示了上述搜索过程。
访问局部变量要比访问全局变量更快,因为不用向上搜索作用域链
第4章 变量、作用域和内存问题
var color = "blue"; function getColor(){ var color = "red"; return color; } alert(getColor()); //"red"
getColor()函数中声明了一个名为color的局部变量。调用函数时,该变量就会被声明。而当函数中的第二行代码执行时,意味着必须找到并返回变量color的值。搜索过程首先从局部环境中开始,而且在这里发现了一个名为color的变量,其值为"red"。因为变量已经找到了,所以搜索即行停止,return语句就使用这个局部变量,并为函数会返回"red"。也就是说,任何位于局部变量color的声明之后的代码,如果不使用window.color都无法访问全局color变量。变量查询也不是没有代价的。很明显,访问局部变量要比访问全局变量更快,因为不用向上搜索作用域链。
4.3 垃圾收集
找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期
性地执行这一操作。
4.3.1 标记清除
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被
环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量 了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
var element = document.getElementById("some_element"); var myObject = new Object(); myObject.element = element; element.someObject = myObject;
在一个DOM元素(element)与一个原生JavaScript对象(myObject)之间创建了循环引用。其中,变量myObject有一个名为element的属性指 向element对象;而变量element也有一个属性名叫someObject回指myObject。由于存在这个循环引用,即使将例子中的DOM从页面中移除,它 也永远不会被回收。
myObject.element = null; element.someObject = null;
4.3.4 管理内存
为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为null来释放其引用——这个做法叫做解除引用
(dereferencing)
function createPerson(name){ var localPerson = new Object(); localPerson.name = name; return localPerson; } var globalPerson = createPerson("Nicholas"); // 手工解除globalPerson的引用globalPerson = null;
在这个例子中,变量globalPerson取得了createPerson()函数返回的值。在createPerson()函数内部,我们创建了一个对象并将其赋给局部 变量localPerson,然后又为该对象添加了一个名为name的属性。最后,当调用这个函数时,localPerson以函数值的形式返回并赋给全局变 量globalPerson。由于localPerson在createPerson()函数执行完毕后就离开了其执行环境,因此无需我们显式地去为它解除引用。但是对于 全局变量globalPerson而言,则需要我们在不使用它的时候手工为它解除引用,这也正是上面例子中最后一行代码的目的。不过,解除一个 值的引用并不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。
引用类型
引用类型的值(对象)是引用类型的一个实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。,以便垃圾收
集器下次运行时将其回收。它也常被称为类,但这种称呼并不妥当。尽管ECMAScript从技术上讲是一门面向对象的语言,但它不具备传统的 面向对象语言所支持的类和接口等基本结构。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。