概述
Micheal力荐的JS教程,写的还是挺不错的,记录一些有用的东西吧
比较时的注意事项
前面提到多次的一点是,在比较时注意比较的是对象还是值,举个例子
var sobj1 = new String('abc');
var sobj2 = new String('abc');
sobj1 === sobj2; // false, 虽然字符串的内容相同,但是并非引用了同一个对象
var sobj = new String('abc');
var s = 'abc';
sobj == s; // true, 隐式进行数据类型转换
sobj === s; // false, 未进行数据转换,一个是对象(object)一个是值(string)
对数字也是一样,要注意比较的是Number
对象还是数值
当然,浮点数仍然是需要注意的,我们知道浮点数并不能正确地表示小数点后面的部分,大多数情况下都只能得到近似值而已
0.1 + 0.2 // 0.30000000000000004
(0.1 + 0.2) === 0.3; // false
1/3 // 0.333333333333333
10/3 - 3 // 0.333333333333335
(10/3 - 3) === 1/3; // false
特殊数值
可以通过 Number 对象的属性值来获知 64 位浮点数所支持的最大正值和最小正值。如果在其之前添
加负号(运算符),就能够获得相应的最大负值和最小负值
Number.MAX_VALUE; // 1.7976931348623157e+308
Number.MIN_VALUE; // 5e-324
-Number.MAX_VALUE; // -1.7976931348623157e+308
-Number.MIN_VALUE; // -5e-324
Number.POSITIVE_INFINITY // 正无穷大
Number.NEGATIVE_INFINITY // 负无穷大
Number.NaN // Not a Number
var inf = Number.MAX_VALUE * 2; // 将最大正数值乘以 2 之后能够得到正无穷大
inf / 2; // Infinity,仍然为无穷大
inf * 0; // NaN,即使乘以0也不会得到0
NaN
比较特殊,对它进行单独说明。
首先,对NaN
进行任何运算,其结果都是NaN
。因此,如果在计算过程中出现了一次NaN
,最终的结果就一定会是NaN
NaN + 1; // NaN
NaN * 0; // NaN
NaN - NaN; // NaN
NaN
不与其他任何数值相等,即使两个NaN
等值判断,其结果也为假
NaN === 1; // false
NaN === NaN; // false
NaN > 1; // false
NaN > NaN; // false
虽然无法判断NaN
的数值,但可以使用isNaN
函数判断是否为NaN
数据类型转换
JavaScript
中实现数据转换的方式有很多中,具体哪种方法的运行速度更快和具体的实现有关,不能一概而论。此外,对于客户端 JavaScript
的情况,重要的不仅仅是需要考虑代码的运行速度,还应当尽可能地缩短源代码的长度。只有缩短代码长度才能够减少网络传输的时间。因此,以较短的代码长度实现数据类型转换的写法成为了首选。
下面是最为简短的数据类型转换写法。虽然使用String(n)
或Number(s)
,代码可读性可能会更高一些,不过这样的写法能够实现更好的性能。
// 惯用的数据类型转换方法(最简短的写法)
// 从数值转换为字符串值
var n = 3;
n+''; // 将数值 3 转换为字符串值 '3'(利用了字符串连接运算符)
// 从字符串值转换为数值
var s = '3';
+s; // 将字符串值 '3' 转换为了数值 3(利用了正号运算)
在字符串值和数值之间进行数据类型转换时需要注意的地方
转换的对象 | 数据类型转换 | 结果 |
---|---|---|
无法被转换为数值的字符串值 | 转换为数值类型 | 数值NaN |
空字符串值 | 转换为数值类型 | 数值0 |
数值NaN | 转换为字符串型 | 字符串"NaN" |
数值Infinity | 转换为字符串型 | 字符串"Infinity" |
数值-Infinity | 转换为字符串型 | 字符串"-Infinity" |
转换为布尔型时,只有以下列举的值在转换为结果为false,除此之外都为true
- 数值
0
- 数值
NaN
null
值undefined
值- 字符串值
''
(空字符串值)
通常通过!!
来进行隐式的数据类型转换,!
运算是用于布尔型操作数的逻辑非运算。在操作数不是布尔型的情况下会自动将其转换为布尔型。因此只要使用!!
这样的双重否定,就能够将值转换为布尔型。
!!1; // true
!!'x'; // true
!!0; // false
!!''; // false
!!null; //false
在进行布尔型的数据类型转换时,应当对Object
类型的情况多加注意。Object
类型在被转换为
布尔型之后结果必定为true
。
var b = new Boolean(false);
if (b) { print('T'); } else { print('F'); } // T
var z = new Number(0);
if (z) { print('T'); } else { print('F'); } // T
var s = new String('');
if (s) { print('T'); } else { print('F'); } // T
而像下面这样,通过函数调用方式获得的结果就不再是Object
类型,而是相应的内建类型了。这样
一来,转换的结果就会与直觉一致。
var b = Boolean(false);
if (b) { print('T'); } else { print('F'); } // F
var z = Number(0);
if (z) { print('T'); } else { print('F'); } // F
var s = String('');
if (s) { print('T'); } else { print('F'); } // F
异常
可以通过throw
语句来抛出异常对象,表达式可以是任意类型
若需要捕捉异常,则需要使用try-catch-finally
的结构,其中catch
和finally
子句不能同时省略
// try-catch-finally 结构的语法
try {
语句
语句
......
} catch ( 变量名 ) { // 该变量是一个引用了所捕捉到的异常对象的局部变量
语句
语句
......
} finally {
语句
语句
......
}
如果在try
子句中(以及在try
子句中调用的函数内)发生异常的话,运行就会中断,并开始执行catch
子句的部分。执行catch
子句被称为捕捉到了异常。在try
语句之外,或者没有catch
子句的try
语句,都是无法捕捉异常的。这时函数会中断并返回至调用该函数之处。
throw
语句和return
语句在中断函数的执行上是相似的,不过throw
语句并没有返回值。而且,如果没能在调用了该函数的函数内的catch
子句中捕捉异常的话,还会进一步返回到更上一层的调用函数(这种行为称为异常的传递)。
如果最终都没能成功捕捉异常,整个程序的执行就将被中断。
finally
子句必定会在跳出try
语句之时被执行。即使没有产生异常,finally
子句也会被执行。也就是说,如果没有产生异常的话,在执行完try
子句之后会继续执行finally
子句的代码;如果产生了异常,则会在执行finally
子句之前首先执行catch
子句。对于没有catch
子句的try
语句来说,异常将会被直接传递至上一层,但finally
子句仍然会被执行。
try {
print('1');
null.x; // 在此处强制产生一个 TypeError 异常
print('not here'); // 这条语句不会被执行
} catch (e) { // 对象 e 是 TypeError 对象的一个引用
print('2');
} finally {
print('3');
}
前置运算符和后置运算符的区别
// 前置运算符的行为
var n = 10;
var m = ++n;
print(m, n); // n 变为了 11。++n 的值是进行了加法之后的值,所以 m 为 11。
11 11
// 后置运算符的行为
var n = 10;
var m = n++;
print(m, n); // n 变为了 11。n++ 的值是进行了加法之前的值,所以 m 为 10。
10 11
this引用
- 在最外层代码中,
this
引用引用的是全局对象 - 在函数内,
this
引用根据函数调用方式的不同而有所不同
函数的调用方式 | this 引用的引用对象 |
---|---|
构造函数调用 | 所生成的对象 |
方法调用 | 接收方对象 |
apply 或是call 调用 | 由apply 或call 的参数指定的对象 |
其他方式的调用 | 全局对象 |
原型继承
所有的函数(对象)都具有名为prototype
的属性(prototype
属性所引用的对象则称为prototype
对象)。所有的对象都含有一个(隐藏的)链接,用以指向在对象生成过程中所使用的构造函数(Function
对象)的prototype
对象。
function MyClass() { this.x = 'x in MyClass'; }
var obj = new MyClass(); // 通过 MyClass 构造函数生成对象
print(obj.x);
x in MyClass // 访问对象 obj 的属性 x
print(obj.z); // 对象 obj 中没有属性 z
undefined
// Function 对象具有一个隐式的 prototype 属性
MyClass.prototype.z = 'z in MyClass.prototype'; // 在构造函数 prototype 对象新增属性 z
print(obj.z); // 这里的 obj.z 访问的是构造函数 prototype 对象的属性
z in MyClass.prototype
在读取对象obj
的属性的时候,将首先查找自身的属性。如果没有找到,则会进一步查找对象MyClass
的prototype
对象的属性。这就是原型链的基本原理。这样一来,在通过MyClass
构造函数生成的对象之间就实现了对MyClass.prototype
对象的属性的共享。
这种共享用面向对象的术语来说就是继承。通过继承可以生成具有同样执行方式的对象。不过请注
意,在上面的代码中,如果修改MyClass.prototype
,已经生成的对象也会发生相应的变化。
而属性的写入与删除则与原型链无关。
function MyClass() { this.x = 'x in MyClass'; }
MyClass.prototype.y = 'y in MyClass.prototype';
var obj = new MyClass(); // 通过 MyClass 构造函数来生成对象
print(obj.y); // 通过原型链读取属性
y in MyClass.prototype
obj.y = 'override'; // 在对象 obj 中新增直接属性 y
print(obj.y); // 读取直接属性
'override'
var obj2 = new MyClass();
print(obj2.y); // 在其他的对象中,属性 y 不会发生变化
y in MyClass.prototype
delete obj.y; // 删除属性 y
print(obj.y); // 该直接属性不存在,因此将搜索原型链
y in MyClass.prototype
delete obj.y; // 虽然 delete 运算的值为 true......
true
print(obj.y); // 但无法 delete 原型链中的属性
y in MyClass.prototype
最后
以上就是有魅力白昼为你收集整理的Meteor学习笔记之三——《JavaScript编程全解》读书笔记的全部内容,希望文章能够帮你解决Meteor学习笔记之三——《JavaScript编程全解》读书笔记所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复