01 Jul 2017 21:26 +0000
JavaScript 属于描述型脚本语言, 在运行时由浏览器进行动态解析和执行, 在此过程中有一些特殊的行为模式, 合格的 JavaScript 程序员应该对此有清楚的了解.
预编译
预编译指: 变量和声明式函数的声明会在执行之前进行预编译,
变量提升
// 因为函数 testFunction 会被预编译, 所以以下语句可以正常运行
test('...');
function test(text) {
console.log(text);
}
以上代码在执行前被预编译为:
function test(text) {
console.log(text);
}
test('...');
要注意的是, 预编译只进行变量声明, 而不进行变量初始化, 因此:
/*
* 因为变量 test 会被预编译, 所以以下语句可以正常运行, 但是
* 变量被使用时尚未被初始化! 所以输出是 'undefined'
*/
console.log(test);
var test = '...';
// 正常输出 '...'
console.log(test);
以上代码在执行前被预编译为:
var test;
console.log(test); // 'undefined'
test = '...';
console.log(test); // '...'
另一个要注意的是, ES6 新引入的 let 和 const 限制了变量提升, 即:
// 出错, 因为 let 和 const 禁止在声明前使用
console.log(test);
let test = '...';
函数提升 (声明式函数与赋值式函数)
声明式函数与赋值式函数在使用时没有差别, 他们的区别在于: 只有声明式函数会被预编译.
// 正常运行, 因为函数提升
Function1();
// 无法运行, 因为赋值式函数虽然提升但未初始化
Function2();
// 声明式函数, function declaration
function Function1() {
console.log('...');
}
// 赋值式函数, function expression
let Function2 = function() {
console.log('...');
}
以上代码在执行前被预编译为:
// 仅提升了变量声明, 未初始化
let Function2;
// 提升了整个函数定义
function Function1() {
console.log('...');
}
Function1();
Function2();
Function2 = function() {
console.log('...');
}
这里要留心声明式函数提升的特点: 在严格模式下, 语句块中的函数不会被提升代码块顶部, 如:
'use strict';
if (condition) {
function test() {
console.log('...');
}
} else {
function test() {
console.log('...');
}
}
// Throws 'ReferenceError: ok is not defined'
test();
代码块的执行顺序
代码块是一个页面中由<script>
标签包裹的代码段, 如:
<script>
console.log('Block One');
</script>
一个页面中的 JavaScript 代码由若干代码块组成, 简单的页面通常只含一个代码块.
引入的外部 js 文件视为一个单独的代码块.
代码块的执行顺序和代码块出现顺序一致, 代码块间保持相互独立, 但不同代码块的全局变量和方法互相共享. 如:
<script>
// 变量声明是预编译的, 所以此处可以正常运行
console.log(test);
</script>
<script>
// notExist 未定义, 运行出错
console.log(notExist);
// 这部分代码到达不了
console.log('...');
// 变量声明会被预编译, 所以是有效的, 而且全局变量被共享了
let test = 'Defined in block one';
</script>
<script>
// 代码块间是相互独立, 但又共享全局变量的, 所以此处也可以正常运行
console.log(test);
</script>
REFERENCE
Loading comments...