本部分参考文献较多, 而且应该会不断更新:
Strict mode(严格模式)
- Javascript 严格模式详解
- What does “use strict” do in JavaScript, and what is the reasoning behind it?
- ECMAScript 5 Strict Mode, JSON, and More
严格模式是ES 5添加了第二种运行模式, 它让JS运行在更严格的环境中,严格模式有下列几点作用:
- 捕捉Javascript奇葩代码并发出警告;
- 避免代码不安全动作,或者报错(比如访问全局对象);
- 提高编译器效率,增加运行速度;
- 禁止Javascript不完善的特性;
- 为未来新版本的Javascript做好铺垫。
可以使用: 'use strict'
进入严格模式. 老版本浏览器会把它当成一个字符串而已, 但是新版本浏览器可以识别出其作用. 它可以全局生效, 也可以局部生效, 我们稍后结合其他内容来看.
分号
JS行尾分号相加就加, 想不加就不加, 看自己了.
变量和常量
- What’s the difference between using “let” and “var” to declare a variable?
- JavaScript ES6+: var, let, or const?
- ES6, var vs let
- Is there any reason to use the “var” keyword in ES6?
- For and against let
变量
在之前的介绍中, 我们已经大量使用了变量, 通过var
声明一个变量即可. 但是JS奇葩的特点很多啊,我们一个一个看:
没有var
在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明:
name = 'wanwu.tech'
'wanwu.tech'
'use strict'
age = 19
ReferenceError: age is not defined
at evalmachine.<anonymous>:2:5
at ContextifyScript.Script.runInThisContext (vm.js:23:33)
at Object.runInThisContext (vm.js:95:38)
at run ([eval]:617:19)
at onRunRequest ([eval]:388:22)
at onMessage ([eval]:356:17)
at emitTwo (events.js:106:13)
at process.emit (events.js:194:7)
at process.nextTick (internal/child_process.js:766:12)
at _combinedTickCallback (internal/process/next_tick.js:73:7)
观察上面两个例子可以看到, 第一个变量name
在正常模式中, 没有声明直接赋值, 没有任何问题. 但是age
因为工作在严格模式, 禁止不声明就使用, 所以报错.
注意我们仅仅在
age = 19
前面增加了'use strict'
便进入了严格模式
作用域
一般来说, 变量如果在一个函数内声明, 那么它的作用域就是这个函数. 但是JS同学偏偏是’二班’的:
function foo() {
var x = 1
y = x + 1
}
foo()
y = y + 2
console.log('y: ' + y)
x
y: 4
ReferenceError: x is not defined
at evalmachine.<anonymous>:10:1
at ContextifyScript.Script.runInThisContext (vm.js:23:33)
at Object.runInThisContext (vm.js:95:38)
at run ([eval]:617:19)
at onRunRequest ([eval]:388:22)
at onMessage ([eval]:356:17)
at emitTwo (events.js:106:13)
at process.emit (events.js:194:7)
at process.nextTick (internal/child_process.js:766:12)
at _combinedTickCallback (internal/process/next_tick.js:73:7)
根据上面没有var部分的介绍, 在正常模式中, var x = 1
把x
的作用域限制在foo()
函数中, 而y
因为没有用var
声明, 直接变成了全局变量.
全局对象说法不是很准确, 但是其中故事太多了, 说多了都是泪, 有余力可以研究顶层对象的属性
let与var
ES6新增了let
命令,用来声明变量。它的用法类似于var
,但let
声明的变量只存在于所在的代码块, 相对的var
声明的变量存在于所在的函数. 而且, let
声明的变量不存在变量提升.
变量提升: 变量可以在声明之前使用,值为undefined
查看下面代码理解上面的内容:
console.log(iAmVar) // 声明前使用输出undefined
try {
console.log(iAmLet) // 声明前使用抛出异常
}
catch(exception){
console.log('iAmLet: exception')
}
// 声明
var iAmVar = 'by var'
let iAmLet = 'by let'
// 声明后使用
console.log(iAmVar)
console.log(iAmLet)
function foo() {
// *i*可见, *j*不可见
for (var i = 0; i < 10; i++) {
// *i*在foo()内都可见
}
for (let j = 0; i < 10; i++) {
// *j*只在此for()循环代码块中可见
// 每个循环都是不同的*j*, 当前的*j*只在本轮循环有效, 所以每一次循环的*j*其实都是一个新的变量
}
console.log(i) // for循环外部可见
try {
console.log(j) // for循环外部不可见
}
catch(exception){
console.log('j: exception')
}
}
foo()
undefined
iAmLet: exception
by var
by let
10
j: exception
undefined
不管是var
, 还是let
, 它们声明的都是变量, 值是可以改变的.
iAmVar = 'another value'
iAmLet = 'also another value'
console.log(iAmVar)
console.log(iAmLet)
another value
also another value
undefined
全局中的var和let
看代码:
var byVar = 1;
let byLet = 2;
console.log(`${byVar} is by var`);
console.log(`${byLet} is by let`);
console.log(`${global.byVar} is by var`);
console.log(`${global.byLet} is by let`);
1 is by var
2 is by let
1 is by var
undefined is by let
有没有发现全局中没有byLet
.
常量
ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域.
const PI = 3.14;
PI = 3;
SyntaxError: Identifier 'PI' has already been declared
at evalmachine.<anonymous>:1:1
at ContextifyScript.Script.runInThisContext (vm.js:23:33)
at Object.runInThisContext (vm.js:95:38)
at run ([eval]:617:19)
at onRunRequest ([eval]:388:22)
at onMessage ([eval]:356:17)
at emitTwo (events.js:106:13)
at process.emit (events.js:194:7)
at process.nextTick (internal/child_process.js:766:12)
at _combinedTickCallback (internal/process/next_tick.js:73:7)
可见, 试图改变const
声明的PI
是会报错的.
const? let? var?
我不是JS专家, 目前只能根据大牛们的意见给出一个总结: 能用const就用const, 不行就是用let, 还是不行考虑var.
多维数组
多维数组可以如下创建:
var items = [
[1, 2],
[3, 4],
[5, 6]
];
console.log(items[1][1])
items[1][1] = 100
console.log(items[1][1])
console.log(items)
4
100
[ [ 1, 2 ], [ 3, 100 ], [ 5, 6 ] ]
undefined
但是如果这么做的话, 再看看效果:
var items = Array(3).fill(Array(2).fill(0));
console.log(items[1][1])
items[1][1] = 100
console.log(items[1][1])
console.log(items)
0
100
[ [ 0, 100 ], [ 0, 100 ], [ 0, 100 ] ]
undefined
第一行全都变成了100, 而不仅仅是第一行第一列。
Can you please explain to me why it is that var arr2D = new Array(5).fill(new Array(3));
虽然在形式上, 上面的两步 fill()
创建了一个二维数组, 但是它创建的数组, 其实都引用 Array(2).fill(0)
创建的数组. 那么其实每行都会一样
看看手册:
When the
fill
method gets passed an object, it will copy the passed object, and fill the array with a reference to the copy.