zoukankan      html  css  js  c++  java
  • 深入浅出js中的this

    Q:this是什么?

    A:this是Javascript语言的一个关键字,它代表函数运行时,自动生成的一个内部对象,在每个 function 中自动根据作用域(scope) 确定, 指向的是此次调用者。

    Q:this的使用场景?

    A:  1.普通函数调用。

      2.作为对象的方法来调用。

      3.作为构造函数调用。

      4.函数被call,apply,bind调用的时候。

    栗子:

    普通函数调用

    function test1(){
      console.log(this);
    }
    test1();   // window

    在非严格模式下,由于this必须是一个对象,所以就默认为指向全局对象window。在严格模式下,上面的代码则会出现不同的结果,如下:

    function test1(){
      "use strict";
      console.log(this);
    }
    test1(); // undefined

    再看看下面这段代码:

    function test2(){
        this.x=2;
    }
    test2();
    console.log(window.x);   // 2

    由于普通函数的调用,this的值指向window。所以执行this.x的时候相当于执行了window.x,故window.x的值为2。

    作为对象的方法来调用

    var message = {
      content: "I'm a message!",
      showContent: function() {
        console.log(this.content);
      }
    };
    
    message.showContent();   // I'm a message!

      上面栗子是将函数保存为对象的属性, 这样就转化为一个方法, 可以通过对象调用这个方法。而当函数被当成对象的方法来调用时, 里面的 this 值就被设置为调用方法的对象。其实,不管被调用函数在声明时是属于方法,还是函数,当最终作为对象的方法来调用时,this都是指向方法的调用者,即母体对象。看看下面的栗子你就明白了。

    var obj = {
        x: 1,
        showX: function() {
            console.log(this.x)
        }
    }
    obj.showX();  // 1
    
    var obj1={x:11};
    obj1.showX=obj.showX;
    obj1.showX();  // 11
    
    
    show=function(){
        console.log('show'+this.x);    
    }
    
    obj1.showX=show;
    obj1.showX();   // show11

      上面栗子中,在obj对象中,匿名函数在声明时就作为obj对象的一个属性,this直接指向obj对象。函数show在声明时是一个全局函数,函数里面的this指向window,但当最后作为对象obj1的一个属性时,其this便指向了对象obj1。但是,请注意,并不是所有函数作为对象的方法来调用时,this都会指向调用者,如下:

    var obj = {
        x : 100,
        y : function(){
            setTimeout(
                function(){ console.log(this.x); }    
             , 1000);
        }
    };
    
    obj.y();  // undefined

    上面栗子中的this指向的是window对象,并不是我们期待的obj,所以会弹出undefined。所以在书写这类代码时,尽量避免这种写法,当然,你也可以在调用的时候将当时的this所指向的对象存在一个变量里,等到定时器运行时再用所存的变量去调用x,如下:

    var obj = {
        x : 100,
        y : function(){
            var that = this;
            setTimeout(
                function(){ console.log(that.x); }
             , 1000);
        }
    };
    
    obj.y();   // 100

    作为构造函数调用

    当一个函数作为构造器使用时(通过new关键字),它的this值绑定到新创建的那个对象。

    function Message(content){
       this.content = content;
       this.showContent = function(){
           console.log(this.content);
       };
    }
    
    var message = new Message("I'm a message!");
    message.showContent();   // I'm a message!

     再看看下面这个栗子。

    function obj(){
        this.a=666;
        return 'abc';
    }
    
    var obj=new obj();
    console.log(obj);  // obj{a: 666} 

    有木有看出什么?当一个带有return返回值的函数作为构造器去new一个新的对象时,return的值是被忽略的。是不是很神奇!

    函数被call,apply,bind调用的时候

    所有的函数都有apply()和call()这两个方法。我们可以通过这两个方法来改变函数的上下文, 在任何时候都有效, 用来显式地设置this的值。

    apply()方法接收两个参数: 第一个是要设置为this的那个对象,第二个参数是可选的,如果要传入参数,则封装为数组作为apply()的第二个参数即可。

    call()方法和apply()基本上是一样的,除了后面的参数不是数组,而是分散开一个一个地附加在后面。

    function warrior(speed, strength){
       console.log(
          "Warrior: " + this.kind +
          ", weapon: " + this.weapon +
          ", speed: " + speed +
          ", strength: " + strength
      );
    }
    
    var warrior1 = {
       kind: "ninja",
       weapon: "shuriken"
    };
    
    var warrior2 = {
       kind: "samurai",
       weapon: "katana"
    };
    
    warrior.call(warrior1, 9, 5);   // Warrior: ninja, weapon: shuriken, speed: 9, strength: 5
    warrior.apply(warrior2, [6, 10]);   // Warrior: samurai, weapon: katana, speed: 6, strength: 10

      在上面,我们通过对构造器warrior()传入不同的参数创建不同类型的对象, this将指向我们通过call() 和/或 apply()传入的对象。

      在第一个函数调用中,我们使用call() 方法来将this设置为warrior1对象, 并传入需要的其他参数, 参数间用逗号分隔。在第二个函数调用中, 其实都差不多, 只是传入的是warrior2对象, 并将必要参数封装为一个数组。

      除了call()和apply()以外,ECMAScript 5还增加了bind()方法,在调用一个函数或方法时也可以通过bind方法来绑定this对象。让我们看下面的栗子:

    function warrior(kind){
       console.log(
          "Warrior: " + kind +
          ". Favorite weapon: " + this.weapon +
          ". Main mission: " + this.mission
       );
    }
    
    var attributes = {
       weapon: "shuriken",
       mission: "espionage"
    };
    
    var ninja = warrior.bind(attributes, "ninja");
    
    ninja();   // Warrior: ninja. Favorite weapon: shuriken. Main mission: espionage

    在这个栗子中, bind()方法的使用方式还是类似的, 但warrior.bind()创建了一个新的函数(方法体和作用域跟warrior()一样),并没有改动原来的warrior()函数。新函数的功能和老的一样, 只是绑定到了attributes对象。

    注:bind方法和call,apply的区别在于,bind() 之后函数并没有执行,可以传给其他函数,在某个适当的时机再调用。

  • 相关阅读:
    【剑指offer】10 矩形覆盖
    【剑指offer】09 变态跳台阶
    【剑指offer】08 跳台阶
    【剑指offer】07 斐波那契数列
    【剑指offer】06 旋转数组的最小数字
    【剑指offer】05 用两个栈实现队列
    【剑指offer】04 重建二叉树
    【剑指offer】03 从尾到头打印链表
    【剑指offer】02 替换空格
    【剑指offer】01 二维数组中的查找
  • 原文地址:https://www.cnblogs.com/slly/p/5940723.html
Copyright © 2011-2022 走看看