js拾遗

js 拾遗

ES6 与 ECMAScript 2015 的关系

ES6 是 ECMA 的为 JavaScript 制定的第 6 个版本的标准,标准委员会最终决定,标准在每年的 6 月份正式发布一次,作为当年的正式版本。ECMAscript 2015 是在 2015 年 6 月份发布的 ES6 的第一个版本。依次类推 ECMAscript 2016 是 ES6 的第二个版本、 ECMAscript 2017 是 ES6 的第三个版本……

块作用域

ECMAScript6 之前的 JavaScript 没有语句块作用域;相反,语句块中声明的变量将成为语句块所在函数(或全局作用域)的局部变量。例如,如下的代码将在控制台输出 5,因为 x 的作用域是声明了 x 的那个函数(或全局范围),而不是 if 语句块。

1
2
3
4
if (true) {
var x = 5;
}
console.log(x); // 5

如果使用 ECMAScript 6 中的 let 声明,上述行为将发生变化。

1
2
3
4
if (true) {
let y = 5;
}
console.log(y); // ReferenceError: y 没有被声明

变量提升

1
2
console.log(x === undefined); // true
var x = 3;
1
2
3
4
5
6
var myvar = "my value";

(function() {
console.log(myvar); // undefined
var myvar = "local value";
})();

在 ECMAScript 6 中,let(const)将不会提升变量到代码块的顶部。因此,在变量声明之前引用这个变量,将抛出引用错误(ReferenceError)。这个变量将从代码块一开始的时候就处在一个“暂时性死区”,直到这个变量被声明为止。

1
2
console.log(x); // ReferenceError
let x = 3;

函数提升

对于函数来说,只有函数声明会被提升到顶部,而函数表达式不会被提升。

1
2
3
4
5
6
7
/* 函数声明 */

foo(); // "bar"

function foo() {
console.log("bar");
}
1
2
3
4
5
6
7
/* 函数表达式 */

baz(); // 类型错误:baz 不是一个函数

var baz = function() {
console.log("bar2");
};

增强的对象字面量 (Enhanced Object literals)

在 ES2015,对象字面值扩展支持在创建时设置原型,简写了 foo: foo 形式的属性赋值,方法定义,支持父方法调用,以及使用表达式动态计算属性名。总之,这些也使对象字面值和类声明更加紧密地联系起来,让基于对象的设计从这些便利中更加受益。

label、break、continue

label

一个 label 提供了一个让你在程序中其他位置引用它的标识符。例如,你可以用 label 标识一个循环, 然后使用 break 或者 continue 来指出程序是否该停止循环还是继续循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var num = 0;
outPoint:
for (var i = 0 ; i < 3 ; i++){
for (var j = 0 ; j < 3 ; j++){
// 在 i = 1,j = 1 时,跳出所有循环,
if( i == 1 && j == 1 ){
// 返回到整个 outPoint 下方,继续执行
break outPoint;
}
num++;
}
}

alert(num); // 输出 4 i-j为 0-0,0-1,0-2,1-0,
1
2
3
4
5
6
7
8
9
10
11
12
var num = 0;
outPoint:
for (var i = 0 ; i < 3 ; i++){
for (var j = 0 ; j < 3 ; j++){
if( i == 1 && j == 1 ){
continue outPoint;
}
num++;
}
}

alert(num); // 输出 7 i-j为 0-0,0-1,0-2,1-0,2-0,2-1,2-2

break

使用 break 语句来终止循环,switch, 或者是链接到 label 语句。

  • 当你使用不带 label 的 break 时, 它会立即终止当前所在的 while,do-while,for,或者 switch 并把控制权交回这些结构后面的语句。
  • 当你使用带 label 的 break 时,它会终止指定的带标记(label)的语句。

continue

continue 语句可以用来继续执行(跳过代码块的剩余部分并进入下一循环)一个 while、do-while、for,或者 label 语句。

  • 当你使用不带 label 的 continue 时, 它终止当前 while,do-while,或者 for 语句到结尾的这次的循环并且继续执行下一次循环。
  • 当你使用带 label 的 continue 时, 它会应用被 label 标识的循环语句。

for…in、for…of

for…in 语句循环一个指定的变量来循环一个对象所有可枚举的属性
for…of 语句在可迭代对象(包括 Array、Map、Set、arguments 等等)上创建了一个循环,对值的每一个独特属性调用一次迭代。

1
2
3
4
5
6
7
8
9
10
let arr = [3, 5, 7];
arr.foo = "hello";

for (let i in arr) {
console.log(i); // 输出 "0", "1", "2", "foo"
}

for (let i of arr) {
console.log(i); // 输出 "3", "5", "7"
}

函数

当一个函数是一个对象的属性时,称之为方法

函数提升仅适用于函数声明,而不适用于函数表达式。

递归

1
2
3
4
5
6
7
const func = function loop(x) {
if (x >= 10)
// "x >= 10" 是退出条件(等同于 "!(x < 10)")
return x;
// 做些什么
return loop(x + 1); // 递归调用
};
1
2
3
4
5
6
7
const func = function loop(x) {
if (x >= 10)
// "x >= 10" 是退出条件(等同于 "!(x < 10)")
return x;
// 做些什么
return func(x + 1); // 递归调用
};
1
2
3
4
5
6
7
const func = function loop(x) {
if (x >= 10)
// "x >= 10" 是退出条件(等同于 "!(x < 10)")
return x;
// 做些什么
return arguments.callee(x + 1); // 递归调用
};

三个方法等阶

1
return loop(x + 1);
1
return func(x + 1)
1
return arguments.callee(x + 1)

递归函数使用了堆栈:函数堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function foo(i) {
if (i < 0)
return;
console.log('begin:' + i);
foo(i - 1);
console.log('end:' + i);
}
foo(3);

// 输出:

// begin:3
// begin:2
// begin:1
// begin:0
// end:0
// end:1
// end:2
// end:3

闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var createPet = function(name) {
var sex;

return {
setName: function(newName) {
name = newName;
},

getName: function() {
return name;
},

getSex: function() {
return sex;
},

setSex: function(newSex) {
if(typeof newSex == "string"
&& (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
sex = newSex;
}
}
}
}

var pet = createPet("Vivie");
pet.getName(); // Vivie

pet.setName("Oliver");
pet.setSex("male");
pet.getSex(); // male
pet.getName();

运算符

delete

1
2
3
4
5
6
7
8
9
x = 42;
var y = 43;
myobj = new Number();
myobj.h = 4; // create property h
delete x; // returns true (can delete if declared implicitly)
delete y; // returns false (cannot delete if declared with var)
delete Math.PI; // returns false (cannot delete predefined properties)
delete myobj.h; // returns true (can delete user-defined properties)
delete myobj; // returns true (can delete if declared implicitly)

delete 删除数组中的一个元素,这个元素就不在数组中了,数组长度没变。例如,trees[3]被删除,trees[3] 仍然可寻址并返回 undefined,但是数组已经没有这个下标了。

1
2
3
4
5
6
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
delete trees[3];
if (3 in trees) {
// 这里的3是下标
// 不会被执行
}
1
2
3
4
5
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
trees[3] = undefined;
if (3 in trees) {
// this gets executed(会被执行)
}

关系运算符 - in

1
propNameOrNumber in objectName

在这里 propNameOrNumber 可以是一个代表着属性名的字符串或者是一个代表着数组索引的数值表达式,而 objectName 则是一个对象名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Arrays
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees; // returns true
3 in trees; // returns true
6 in trees; // returns false
"bay" in trees; // returns false (you must specify the index number,
// not the value at that index)
"length" in trees; // returns true (length is an Array property)

// Predefined objects
"PI" in Math; // returns true
var myString = new String("coral");
"length" in myString; // returns true

// Custom objects
var mycar = {make: "Honda", model: "Accord", year: 1998};
"make" in mycar; // returns true
"model" in mycar; // returns true

国际化

Intl.NumberFormat格式化数字

Intl.DateTimeFormat格式化日期

对象

定义 getters 与 setters

es6123