Meteor学习笔记之三——《JavaScript编程全解》读书笔记

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的结构,其中catchfinally子句不能同时省略

// 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调用 applycall的参数指定的对象
其他方式的调用 全局对象

原型继承

所有的函数(对象)都具有名为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的属性的时候,将首先查找自身的属性。如果没有找到,则会进一步查找对象MyClassprototype对象的属性。这就是原型链的基本原理。这样一来,在通过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

猜你喜欢

转载自blog.csdn.net/github_39273626/article/details/80242704
今日推荐