zoukankan      html  css  js  c++  java
  • 【转】深入浅出JavaScript之this

    JavaScript中的this比较灵活,根据在不同环境下,或者同一个函数在不同方式调用下,this都有可能是不同的。但是有一个总的原则,那就是this指的是,调用函数的那个对象。

    下面是我的学习笔记,把它罗列成8种情况。

    全局的this(浏览器) 

    全局作用域的this一般指向全局对象,在浏览器中这对象就是window,在node中这对象就是global。

    1
    2
    3
    4
    console.log(this.document === document); // true (document === window.document)
    console.log(this === window); // true
    this.a = 37;  //相当于创建了一个全局变量a
    console.log(window.a); // 37

    一般函数的this(浏览器) 

    一般的函数声明或者函数表达式,直接调用函数的话,this依然指向全局对象,在浏览器中这对象就是window,在node中这对象就是global。

    1
    2
    3
    4
    function f1(){ 
      return this
    }
    f1() === window; // true, global object

    再举一个例子,看完就非常透彻了

    1
    2
    3
    4
    5
    function test(){
     this.x = 1;
      alert(this.x);
    }
    test(); // 1

    为了证明this就是全局对象,对代码做一些改变:

    1
    2
    3
    4
    5
    var x = 1;
    function test(){
     alert(this.x);
    }
    test(); // 1

    运行结果还是1。再变一下:

    1
    2
    3
    4
    5
    6
    var x = 1;
    function test(){
     this.x = 0;
    }
    test();
    alert(x); //0

    但是在严格模式下,一般函数调用的时候this指向undefined,这也是node为什么要用严格模式的一个原因。

    1
    2
    3
    4
    5
    function f2(){ 
      "use strict"; // see strict mode 
      return this;
    }
    f2() === undefined; // true

    作为对象方法的函数的this 

    this作为对象方法来使用是比较常见的。

    下面这个例子,我们创建了一个对象字面量o,o里面有个属性f,它的值是一个函数对象,把函数作为对象属性的值这种方式我们常常叫作对象的方法。作为对象的方法调用的时候,这时候this指向对象o

    1
    2
    3
    4
    5
    6
    7
    8
    var o = { 
       prop: 37, 
       f: function() {   
         return this.prop;   
      }
    }; 
     
    console.log(o.f()); // logs 37

    我们不一定要定义成函数字面量这样子的对象,像下面这种情况,我们只定义了一个对象o,如果直接调用independent()函数的话,this会指向window,但是我们通过赋值的方式,临时创建一个属性f,并指向函数对象的时候,我们仍然拿到了37。

    1
    2
    3
    4
    5
    6
    7
    8
    var o = {prop: 37};
     
    function independent() { 
       return this.prop;
    }
     
    o.f = independent; 
    console.log(o.f()); // logs 37

     所以并不是看函数是怎么创建的,而是只要将函数作为对象的方法去调用,this就会指向这个对象。

    对象原型链上的this 

    下面这个例子中:我们先创建了一个对象o,里面有一个属性f,函数作为对象属性的值,我们通过Object.create(o)创建了一个对象p,p是一个空对象,它的原型会指向o,然后使用p.a = 1; p.b = 4创建对象p上的属性,那么我们调用原型上的方法时,this.a,this.b依然能取到对象p上的a和b。这里需要注意的是p的原型才是o,我们调用p.f(),调用的是原型链o上的属性f,原型链上的this可以拿到当前的对象p。

    1
    2
    3
    4
    5
    var o = {f:function(){ return this.a + this.b; }};
    var p = Object.create(o);
    p.a = 1;
    p.b = 4;
    console.log(p.f()); // 5 

    get/set方法与this 

    get/set方法中的this一般会指向get/set方法所在对象里面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function modulus(){  
       return Math.sqrt(this.re * this.re + this.im * this.im);
    }
    var o = {
      re: 1,
      im: -1,
      get phase(){     
         return Math.atan2(this.im, this.re);   
      }
    };
    Object.defineProperty(o, 'modulus', {       //临时动态给o对象创建modules属性
      get: modulus, enumerable:true, configurable:true});
     
    console.log(o.phase, o.modulus); // logs -0.78 1.4142

    构造函数中的this

    用new把MyClass作为构造函数调用的话,this会指向空的对象,并且这个对象的原型会指向MyClass.prototype(可以看这篇文章对原型链的总结),但是调用的时候做了this.a = 37的赋值,所以最后this会作为返回值(没写return语句,或者return的是基本类型的话,会将this作为返回值),第二个例子return语句返回了对象,那么就会将a = 38作为返回值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function MyClass(){   
       this.a = 37;
    }
    var o = new MyClass(); 
    console.log(o.a); // 37
     
    function C2(){   
       this.a = 37;  
       return {a : 38}; 
    }
     
    o = new C2(); 
    console.log(o.a); // 38

    call/apply方法与this 

    除了不同的调用方式外,函数对象有些方法能修改函数执行的this,比如call/apply。

    call和apply基本上没差别,只不过call传参的方式是扁平的,而apply是把一个数组传进去。如下面这个例子

    什么时候用call和apply呢?比如我们想调用Object.prototype.toString,但是我们想指定某个this的时候,那我们就可以就用Object.prototype.toString.call(this)这样子的方式来调用些无法直接调用的方法。如下面这个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function add(c, d){ 
       return this.a + this.b + c + d; 
    }
    var o = {a:1, b:3};
    add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16     //第一个参数接收的是你想作为this的对象
    add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
     
    function bar() { 
       console.log(Object.prototype.toString.call(this));
    }
    bar.call(7); // "[object Number]"

    bind方法与this 

    bind方法是es5开始提供的,所以ie9+才支持

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function f(){ 
       return this.a; 
    }
     
    var g = f.bind({a : "test"});   //想把某个对象作为this的时候,就把它传进去,得到一个新对象g
    console.log(g()); // test       //重复调用的时候,this已经指向bind参数。这对于我们绑定一次需要重复调用依然实现绑定的话,会比apply和call更加高效(看下面这个例子)
     
    var o = {a : 37, f : f, g : g}; 
    console.log(o.f(), o.g()); // 37, test   //o.f()通过对象的属性调用,this指向对象o;比较特殊的是即使我们把新绑定的方法作为对象的属性调用,o.g()依然会按之前的绑定去走,所以答案是test不是g

    总结

    做项目的时候才发现这些基础概念有多么的重要,如果不把它们逐个落实了,真的是一不小心就会掉进坑里。后续我还会对原型链,作用域,继承,链式调用,正则等知识进行总结,欢迎关注 

  • 相关阅读:
    connect: network is unreachable问题的解决
    Linux图形界面与字符界面切换
    Xshell远程连接Linux服务器出错
    demo-placeholder兼容ie8
    Python设计TFTP客户端
    python hashlib、hmac模块
    python time、datetime、random、os、sys模块
    python 字符串和字典
    ssh远程登录时提示access denied
    指针的指针与指针的引用
  • 原文地址:https://www.cnblogs.com/BruceWan/p/5964787.html
Copyright © 2011-2022 走看看