本文内容
本文主要是说明一些 Javascript 中的一些语法元素,包括语句、表达式、运算符等
6种循环(ES 6)
在 Javascript 中包含 6 种循环语句,for each in 语句在 ECMAScrupt 中并不存在,属于 Javascript 特有的增强功能。
while (条件表达式)do while (条件表达式)for(初始化表达式;条件表达式;更新表达式)for (变量 in 对象表达式)(属性名迭代)for each (变量 in 对象表达式)(非 ES 标准,属性值迭代)for (变量 of 对象表达式)(ES 6 标准,属性值迭代,具体内容后续 ES 6 内容中进行补充)
for 循环
唯一的注意点:ES 6 之前,JS 没有块级作用域,所以 for 循环中声明的循环变量,在循环结束后依然是可以访问的,例如:
for (var i = 0; i < 10; i++) {
; // 什么都不干
}
console.log(i); // 输出 10,这里我们依然能访问到 i
for in 语句
for in 语句是用于枚举对象属性名的循环语句。在对象表达式处所写的对象的属性名的字符串,会被依次赋值给循环变量,例如:
var obj = { x: 1, y: 3, z: 2 };
for (var k in obj) {
// 迭代的是属性名称而不是属性值
console.log(k);
}
// 依次输出 x y z (也可能不是这个顺序)
如果想要输出属性值,一般使用 obj[k] 的方式进行迭代,例如:
var obj = { x: 1, y: 3, z: 2 };
for (var k in obj) {
console.log(obj[k]);
}
// 依次输出 1 3 2 (也可能不是这个顺序)
处理数列
数列也是一种对象,而且下标值相当于一种属性名,所以可以像下面这样进行迭代(但是一般不推荐),例如:
/**
* 输出
* 0=>7
* 1=>1
* 2=>5
*/
var arr = [7, 1, 5];
for (var n in arr) {
console.log(n + '=>' + arr[n]);
}
如果我们给数列对象加上了别的属性,那么加上的属性也会被迭代,例如:
/**
* 输出
* 0=>7
* 1=>1
* 2=>5
* x=>3
*/
var arr = [7, 1, 5];
arr.x = 3; // 增加了一个 x 属性
for (var n in arr) {
console.log(n + '=>' + arr[n]);
}
注意:
for in 的属性是不确定的,不能认为它有某种顺序规则,我们不应该依靠它的任何顺序,把它当成随机顺序即可。(虽然大部分情况会有相同的顺序,但是不能认为这一定是成立的)
for in 可以枚举由原型继承而来的属性
for in 有些属性是不能进行枚举的(enumerable = false 的属性,我们添加的常规属性都是 true,数组的 length 是 false)。也就是说,属性本身也是有属性的(或者可以称为元属性?),就像在 Java 中,属性除了属性值外,还有可见性、是否 final 等一些其它的属性值之外的属性。在 Javascript 中,属性有 5 种元属性(在此只简单列举,后续会有详细分析):
属性的属性名称 含义 writable 可以改写属性值 enumerable 属性可以被 for in 语句枚举 configutable 元属性本身是否可以进行改变(就是说一个对象的某个属性的这 5 种元属性是否还能进行更改,configurable 一旦为 false,就再也不可改了) get 设置属性值的 getter 函数 set 设置属性值的 setter 函数
错误处理
我们可以用 throw 抛出异常,用 try...catch 捕获它(跟 Java 差不多)。
错误类型
ES 标准定义了 7 种异常类型:
ErrorEvalErrorRangeErrorReferenceErrorSyntaxErrorTypeErrorURIError
其中,Error 是以上所有异常的基类,包含 2 个属性:name 和 message,表示错误的名称和错误的信息。一般不会直接抛出 Error,而是会抛出子类型的错误。
EvalError类型的错误现在不再会被抛出了(新的 ES 标准中)RangeError会在数值超过相应范围时候抛出,例如定义数组时设置不支持的项数,例如:var items1 = new Array(-20); // 抛出 RangeError var items2 = new Array(Number.MAX_VALUE); // 抛出 RangeErrorReferenceError在使用不存在的变量时会抛出,例如:console.log(a); // a 从未声明过,会抛出 ReferenceErrorSyntaxError就是语法错误,这个不用多说明了TypeError在变量类型不符合要求时出现,例如下面 3 行都会出现该错误:var o = new 10; console.log("name" in true); Function.prototype.toString.call("name");URIError在使用encodeURI()或decodeURI(),而 URI 格式不正确时,就会导致URIError错误。
try...catch 语句
例如:
try {
console.log(foo);
} catch (someError) {
console.log('错误名称:' + someError.name);
console.log('错误信息:' + someError.message);
}
finally 块的代码一定会最后执行,所以如果 finally 块中有返回语句,那么会覆盖前面的返回值。
我们可以处理不同类型的错误,使用如下方式:
try {
someFunction();
} catch (error) {
if (error instanceof TypeError) {
//处理
} else if (error instanceof ReferenceError) {
//处理
} else {
//处理其它类型错误
}
}
throw 抛出错误
Javascript 可以抛出任何类型和值的错误,也就是说,下面的代码都是有效的:
throw 12345;
throw "Hello world!";
throw true;
throw { name: "JavaScript"};
throw 之后的语句都不会执行,直到被捕获后在捕获的那一块儿代码中继续执行,例如:
function test() {
console.log('before throw');
throw 100;
console.log('after throw'); // 永远不会执行到这里
}
try {
test();
} catch (error) {
console.log('捕获到:' + error);
}
// 输出
// before throw
// 捕获到:100
使用内置错误,每种错误都接收一个参数,就是错误的信息:
throw new Error("something wrong");
利用原型链可以创建自定义的错误类型,这个可以以后再研究
NEW 运算符
MDN 文档:NEW 运算符
new 运算符会进行如下操作:
- 创建一个空的简单
JavaScript对象,也就是{},假设起一个临时名字是tmp - 将新对象
tmp的原型对象指向构造函数(假设是Func()函数)的prototype属性,也就是说tmp.__proto__ = Func.prototype - 此时
tmp.constructor = Func.prototype.constructor = Func - 步骤 3 有两个关键,一个是
tmp.constructor = Func,一个是Func.prototype.constructor = Func- 也就是说新对象的
constructor是从原型链继承来的 - 函数对象的
prototype属性上,有一个属性prototype.constructor指向函数对象自身
- 也就是说新对象的
- 调用新对象的
constructor方法,也就是tmp.constructor(...) - 步骤 5 的接收对象是
tmp,因此构造函数中this指向tmp - 如果
tmp.constructor(...)没有返回新对象,则返回this,也就是tmp对象 new Func等同于new Func(),也就是进行没有任何参数的构造函数调用
示例:
function a(arg) { this.x = 1; console.log(arg); }
var b = new a(); // 输出 undefined
/**
* b.constructor = b.__proto__.constructor 且 b.__proto__ = a.prototype
* 因此 b.__proto__.constructor = a.prototype.constructor
* 而 a.prototype.constructor = a 自身
* 所以 b.constructor = a
* 这里输出 [Function: a]
*/
console.log(b.constructor);
b.__proto__ = Object.prototype;
/**
* 此时 b.constructor = b.__proto__.constructor = Object.prototype.constructor
* 因此 b.constructor = Object
* 所以输出 [Function: Object]
*/
console.log(b.constructor);
a.prototype.constructor(11111); // 等价于 a(11111)调用,因此输出 11111
INSTANCEOF 运算符
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。例如:
var d = new Date();
// true,Date.prototype = d.__proto__
console.log(d instanceof Date);
// true,Object.prototype = Date.prototype.__proto__ = d.__proto__.__proto__
console.log(d instanceof Object);
function Derived() {}
function Base() {}
Derived.prototype = new Base();
var obj = new Derived();
// true Derived.prototype = obj.__proto__
console.log(obj instanceof Derived);
// true Base.prototype = Derived.prototype.__proto__ = obj.__proto__.__proto__
console.log(obj instanceof Base);
//true 一样的道理
console.log(obj instanceof Object);