Javascript的原型与闭包

转摘自 http://www.cnblogs.com/wangfupeng1988/p/3986420.html

 

1.变量声明会提前

第一句报错,a未定义,很正常。第二句、第三句输出都是undefined,说明浏览器在执行console.log(a)时,已经知道了a是undefined,但却不知道a是10(第三句中)。

在一段js代码拿过来真正一句一句运行之前,浏览器已经做了一些“准备工作”,其中就包括对变量的声明,而不是赋值。变量赋值是在赋值语句执行的时候进行的。可用下图模拟:

 

2.this——赋值

此种情况直接给this赋值。这也是“准备工作”情况要做的事情之一。

 

3.函数表达式和函数声明

注意代码注释中的两个名词——“函数表达式”和“函数声明”。虽然两者都很常用,但是这两者在“准备工作”时,却是两种待遇。

在“准备工作”中,对待函数表达式就像对待“ var a = 10 ”这样的变量一样,只是声明。而对待函数声明时,却把函数整个赋值了。

 

总结:

  • 变量、函数表达式——变量声明,默认赋值为undefined;
  • this——赋值;
  • 函数声明——赋值;

这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。

 

4.函数体

如果在函数中,除了以上数据之外,还会有其他数据。先看以下代码:

以上代码展示了在函数体的语句执行之前,arguments变量和函数的参数都已经被赋值。从这里可以看出,函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。

 

另外一点不同在于,函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。至于“自由变量”和“作用域”是后面要专门拿出来讲述的重点,这里就先点到为止。用一个例子说明一下:

 

总结:

如果代码段是函数体,那么在此基础上需要附加:

参数

赋值

arguments

赋值

自由变量的取值作用域

赋值

 

 

 

 

 

执行上下文环境下一个通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空

作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。所以,如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值。

 

5.自由变量

在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个自由变量。如下图

如上程序中,在调用fn()函数时,函数体中第6行。取b的值就直接可以在fn作用域中取,因为b就是在这里定义的。而取x的值时,就需要到另一个作用域中取。要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”。

作用域链:

取自由变量时的这个“作用域链” 过程:(假设a是自由量)

第一步,先在当前作用域查找a,如果有则获取并结束。如果没有则继续;

第二步,如果当前作用域是全局作用域,则证明a未定义,结束;否则继续;

第三步,(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域;

第四步,跳转到第一步。

 

以上代码中:第13行,fn()返回的是bar函数,赋值给x。执行x(),即执行bar函数代码。取b的值时,直接在fn作用域取出。取a的值时,试图在fn作用域取,但是取不到,只能转向创建fn的那个作用域中去查找,结果找到了。

 

6.闭包

两种情况——函数作为返回值,函数作为参数传递。

第一,函数作为返回值

如上代码,bar函数作为返回值,赋值给f1变量。执行f1(15)时,用到了fn作用域下的max变量的值。至于如何跨作用域取值,可以参考上一节。

 

第二,函数作为参数被传递

如上代码中,fn函数作为一个参数被传递进入另一个函数,赋值给f参数。执行f(15)时,max变量的取值是10,而不是100。

 

7.this

情况1:构造函数

所谓构造函数就是用来new对象的函数。其实严格来说,所有的函数都可以new一个对象,但是有些函数的定义是为了new一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)。例如:Object、Array、Function等。

以上代码中,如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。

 

注意,以上仅限new Foo()的情况,即Foo函数作为构造函数的情况。如果直接调用Foo函数,而不是new Foo(),情况就大不一样了。

这种情况下this是window

 

情况2:函数作为对象的一个属性

如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象。

以上代码中,fn不仅作为一个对象的一个属性,而且的确是作为对象的一个属性被调用。结果this就是obj对象。

 

注意,如果fn函数不作为obj的一个属性被调用,会是什么结果呢?

如上代码,如果fn函数被赋值到了另一个变量中,并没有作为obj的一个属性被调用,那么this的值就是window,this.x为undefined。

 

情况3:函数用call或者apply调用

当一个函数被call和apply调用时,this的值就取传入的对象的值。

 

情况4:全局 & 调用普通函数

在全局环境下,this永远是window,这个应该没有非议。

 

普通函数在调用时,其中的this也都是window。

以上代码很好理解。

不过下面的情况你需要注意一下:

函数f虽然是在obj.fn内部定义的,但是它仍然是一个普通的函数,this仍然指向window。

 

参考资料:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

 

posted on 2016-12-02 16:45  three-fish  阅读(161)  评论(0编辑  收藏  举报