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() 之后函数并没有执行,可以传给其他函数,在某个适当的时机再调用。

  • 相关阅读:
    SQL Server, Timeout expired.all pooled connections were in use and max pool size was reached
    javascript 事件调用顺序
    Best Practices for Speeding Up Your Web Site
    C语言程序设计 使用VC6绿色版
    破解SQL Prompt 3.9的几步操作
    Master page Path (MasterPage 路径)
    几个小型数据库的比较
    CSS+DIV 完美实现垂直居中的方法
    由Response.Redirect引发的"Thread was being aborted. "异常的处理方法
    Adsutil.vbs 在脚本攻击中的妙用
  • 原文地址:https://www.cnblogs.com/slly/p/5940723.html
Copyright © 2011-2022 走看看