JavaScript学习——prototype原型和作用域(4)
好记性不如烂笔头,在写作的过程中自己消化吸收,所以写下这个JavaScript学习系列文章。文章中的文字都是自己一一敲字敲下来的,都是自己理解的内容,各位酌情观看。
上一讲说的作用域,这里还有两个知识点:作用域链、声明提升!
先看个很有意思的代码
a = 2;
var a;
console.log( a );
很多开发者会认为这个会输出undefined(我也是),但它会输出2.为什么呢?这就是变量提升。在上一章节我们在看的《你不知道的JavaScript》书中有说:JavaScript是一门编译型语言,在代码运行之前会先有个编译过程。编译的时候对声明(这里的声明包括变量声明以及函数声明)进行提升。如正常赋值一个变量是 var a=2;在编译器中处理就变成了var a;a=2;在此例子中,就会调整原有的代码顺序,先声明,然后其他代码顺序依次执行
var a;
a = 2;
console.log( a );
这样执行代码结果一眼便知。
除了变量声明,函数声明也是如此。
foo();
function foo() {
console.log( a ); // undefined
var a = 2;
}
第一行代码执行函数foo(),但foo的声明在第二行。依然是在编译环节对函数foo提升,实际上他会被理解成这种形式
function foo() {
var a;
console.log( a ); // undefined
a = 2;
}
foo();
而表达式赋值则不会
foo(); // 不是 ReferenceError, 而是 TypeError!
var foo = function bar() {
// ...
};
这里对foo进行变量提升,在执行第一句话的时候,foo是undefined,调用foo()便报错TypeError的错误。
如果一个名称又有函数声明,又有函数表达式怎么处理?函数优先
foo(); // 3
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() { //这里的函数声明覆盖之前的函数声明,并进行了提升、优先
console.log( 3 );
}
foo();// 2
最终会被处理成
function foo() {
console.log( 3 );
}
foo(); // 3
foo = function() {
console.log( 2 );
};
foo();// 2
除了这些细枝末节,作用域链更为重要!当前作用域范围查找某个变量a,a不在当前作用域,js引擎会找到他的上级作用域,并在他上级作用域中查找a变量,找到a则返回a值;否则继续往上级查找,直到全局作用域。全局作用域找不到,报错。
这里再次重复两个“公式”:
1.函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复 用(事实上在嵌套的作用域中也可以使用)。
2.函数的作用域在函数定义的时候就确定了。
此处应该有图,借用。
总结
我们习惯将 var a = 2; 看作一个声明,而实际上 JavaScript 引擎并不这么认为。它将 var a 和 a = 2 当作两个单独的声明,第一个是编译阶段的任务,而第二个则是执行阶段的任务。 这意味着无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理。可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升。
有了作用域的基础,再讲讲this,就能理解上下文了。所以下篇讲this。
推荐阅读
-
前端综合学习笔记---变量类型、原型链、作用域和闭包
-
js学习-4(函数和作用域)
-
前端学习笔记三:JavaScript(2)变量的分类和作用域+利用浏览器调试模式测试+HTML事件+表示特殊字符(+运算符+各种循环和条件语句【略】)
-
JavaScript学习——prototype原型和作用域(4)
-
JavaScript学习——prototype原型和作用域(3)
-
前端综合学习笔记---变量类型、原型链、作用域和闭包
-
跟我学习javascript的prototype原型和原型链_javascript技巧
-
js学习-4(函数和作用域)
-
跟我学习javascript的prototype原型和原型链_javascript技巧
-
前端学习笔记三:JavaScript(2)变量的分类和作用域+利用浏览器调试模式测试+HTML事件+表示特殊字符(+运算符+各种循环和条件语句【略】)