JavaScript之扑朔迷离的this

  JavaScript这门语言中,最使人迷惑的地方有三个,闭包、this、原型。针对大多数人,能够利用词法做用域等避开this的坑,可是咱们不能一直生活在温馨区,要勇于打破砂锅问到底,对咱们来讲也是一种提高。设计模式

  1、通常对this关键字的误解:数组

    一、this指向函数自身闭包

    二、this指向函数声明的词法做用域app

  咱们能够看如下一段代码:函数

 1 function test() {
 2             test.a = 1;
 3             this.a = 2;
 4             console.log(test.a);
 5             console.log(this.a);
 6             console.log(test.a === this.a);
 7         }
 8 
 9         test();
10         console.dir(test);

  在上面这段代码中,咱们在全局声明一个方法test,给test中的a属性赋值1,当前方法中的this中的a属性赋值2,加入this指向函数自身,那么test.a === this,a而且都等于2.this

下面咱们来看下这段代码的运行结果:spa

  

  从上能够看出,scopes为全局做用域window,this也指向这里,虽然函数自己也是一个对象,可是this并不指向这里。debug

  有一点咱们必定要记住,this是在运行时进行绑定的,并非在编写时绑定的,它的上下文取决于函数调用时的各类条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。设计

  既然this是在运行时绑定的,那咱们有没有办法改变当前this的绑定,使其不指向window,而指向方法test呢?答案是确定的,咱们能够借助一些强制绑定方法,如call、apply、bind来改变this的指向,咱们能够将代码改为下面这种方式:code

 1 function test() {
 2             test.a = 1;
 3             this.a = 2;
 4             console.log(test.a);
 5             console.log(this.a);
 6             console.log(test.a === this.a);
 7         }
 8 
 9         test.call(test);
10         console.dir(test);

  运行结果以下:

   接下来咱们看下this是否指向函数声明的词法做用域,如下有段比较有意思的代码:

 1 function parent() {
 2             var a = 2;
 3 
 4             function child() {
 5                 console.log(this.a)
 6             }
 7             child();
 8 
 9         }
10         parent();

  假如this指向函数的词法做用域,那么child方法中的this.a应该是存在,实际上的执行结果以下,child中的this指向仍为window:

 

  实际上,在JavaScript内部,做用域确实和对象相似,可见的标识符都是它的属性。可是做用域“对象”没法经过JavaScript代码访问,它存在JavaScript引擎内部。因此每当你想要把this和词法做用域的查找混合使用时,必定要提醒本身,这是没法实现的。

   2、this的绑定规则

    this的绑定规则大体分为如下几类:

      2.1 默认绑定

      2.2 隐式绑定

      2.3 显式绑定

      2.4 new绑定

    2.1 默认绑定

      上述示例中this的指向是指向window的,他们都有一个共同的特征,不带任何修饰的函数引用进行调用的,所以只能使用默认绑定,没法应用其余规则

    2.2 隐式绑定

      隐式绑定首先须要考虑的规则就是调用位置是否有上下文对象,或者说是否被某个对象拥有或包含,例如:

  这里的this就是指向对象obj。还有相似一些DOM事件的绑定,document.getElementById('xxx').addEventListener('click', function(){xxx});回调方法中的this是指向选择器选中的元素的。这种状况下能够简单的理解为this指向调用方法.前面的那个对象。

  2.3 显式绑定

   显示绑定在开发过程当中运用的比较多,借助于这些显式绑定方法,能够直接改变当前方法的this指向,使得js语言很是的灵活。主要有call、apply和bind三种,基本使用以下:

 1  function sum() {
 2             console.log(this.a + this.b);
 3         }
 4         var obj1 = {
 5             a: 1,
 6             b: 2
 7         };
 8         var test = sum.bind(obj1);
 9         sum.call(obj1); //3
10         sum.apply(obj1); //3
11         test(); //3

  注意事项:

    call && apply第一个参数接受的是this对象,call第二个参数之后能够接受字符串形式的参数,apply接受的是一个类数组/数组参数

    将null || undefined做为this的绑定对象传入call/apply/bind时,这些值在调用时会被忽略,实际应用的是默认绑定规则

  2.4 new绑定

    JavaScript语言中的new操做符和其余面向对象语言中的new操做符不大同样,由于在JavaScript中没有对象的概念。全部的函数均可以使用new来调用,new的调用又称为构造函数调用。在构造函数调用过程当中,会自动执行下面的操做。

  一、建立(或者说构造)一个全新的对象

  二、这个对象会被执行[[Prototype]]链接

  三、这个新对象会绑定到函数调用的this

  四、若是函数没有返回其余对象,那么new表达式中的函数调用会自动返回这个新对象

  构造函数也是js中经常使用的一种设计模式,如如下代码:

 1 function Test(a, b) {
 2             this.a = a;
 3             this.b = b;
 4             this.add = function() {
 5                 debugger;
 6                 console.log(this.a + this.b);
 7             }
 8         }
 9         var cc = new Test(1, 2);
10         cc.add();
11         console.log(cc);

  在new调用过程当中,返回了一个新对象,而且该对象的this指向Test;

  若有问题,烦请及时指出,谢谢!