欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaScript学习——prototype原型和作用域(4)

程序员文章站 2022-05-24 10:55:44
...

        好记性不如烂笔头,在写作的过程中自己消化吸收,所以写下这个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.函数的作用域在函数定义的时候就确定了。

此处应该有图,借用。

JavaScript学习——prototype原型和作用域(4)

 

总结

        我们习惯将 var a = 2; 看作一个声明,而实际上 JavaScript 引擎并不这么认为。它将 var a 和 a = 2 当作两个单独的声明,第一个是编译阶段的任务,而第二个则是执行阶段的任务。 这意味着无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理。可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升。

有了作用域的基础,再讲讲this,就能理解上下文了。所以下篇讲this。