Javascript中的this指向

原文链接

一、global this

即在全局环境中的this指针

  • 浏览器环境 window
    • 需要注意的是,在该环境下,在标签最外层直接声明的变量,会直接挂载window对象中。
  • node环境 global
    • node环境下运行的js脚本,全局声明变量并不会挂载在任何一个对象,而在全局环境下的this为一个空对象{},但在声明的函数内部的this指向的是global对象
      console.log(this)
      function testThis() {
        console.log(this)
      }
      testThis()
      
      
    图片.png

二、function this

  • 浏览器环境指向全局
    foo = "bar";
    function testThis () {
      this.foo = "foo";
    }
    console.log(window.foo);  // logs bar 
    testThis();
    console.log(window.foo);  // logs foo
    
  • 在node环境下,函数内部this指向global
    var foo = 'bar'
    function testThis () {
      this.foo = "foo";
    }
    console.log(global.foo);  // logs undefined 
    testThis();
    console.log(global.foo);  // logs foo
    console.log(this.foo) // logs undefined 
    console.log(this) // logs {} 
    
  • 使用new关键字,则函数内部this指向新创建的对象
    foo = 'bar'
    function testThis() {
      this.foo = "foo";
    }
    let target = new testThis()
    console.log(this.foo); // logs "bar"
    console.log(target.foo); // logs 'foo'
    /* 这其实是因为new操作符内部使用了apply方法 */
    

三、prototype this

每一个函数在创建期间自动拥有一个prototype属性,而该函数创建的实例会自动带有__proto__属性,该属性指向该构造函数的prototype,原型模式实现继承的关键是__proto__

而当实例对象(this)用.访问属性值时,如果在实例对象本身找不到,则会通过__proto__去查找它的原型对象身上是否含有要寻找的属性。

function A(){}
A.prototype = {
  name: 'aab'
}
let a = new A()
a.getName = function(){
  // 此处的this相当于 a,即为父元素 
  return this.name
}
console.log(a.getName())  // logs aab
  • 闭包内嵌函数没有this
    事实上,闭包中的内嵌函数的thisglobal this

    A.prototype.name = 'aab'
    A.prototype.curreyFun = function() {
      var info = "attempting to log this.foo:";
      function embeddedFun() { 
        // 尽管通过闭包内嵌函数的作用域上有了info,但是却没有this
        console.log(info,this.name)
      }
      embeddedFun()
    }
    new A().curreyFun() // logs "attempting to log this.foo: this.foo: undefined"" 
    
  • 以下是解决这种窘境的一些方法

    • 缓存this,提升作用域
      A.prototype.name = 'aab'
      A.prototype.curreyFun = function() {
        var  info = "attempting to log this.foo:";
        var self = this 
        function embeddedFun() { 
          // 尽管通过闭包内嵌函数的作用域上有了info,但是却没有this
          console.log(info,self.name)
        }
        embeddedFun()
      }
      new A().curreyFun() // logs "attempting to log this.foo:aab"
      
  • 当A函数作为实参传入B函数里面执行,A函数有B的作用域吗?答 : 形参函数不是在B函数定义的,当然没有B函数的作用域链
    • 没有B函数的所有属性
      function test1(fn,value) {
        let value2 = 2 
        fn()
      }
      
      function fn() { 
        // throw error value and value2 undefined
        console.log(value,value2)
      }
      test1(fn,1)
      
    • A函数的this指向全局
      function fn() { 
        // logs undefined 此处的this其实指向global
        console.log(this.name)
      }  
      let a = {
        name : 'a ',
        sayName : function(fn) { 
          fn()
        }
      }
      a.sayName(fn)
      
  • 闭包函数拥有父函数的arguments参数吗?

拥有

function test(value) { 
  // logs 2121231
  return function() { 
    console.log(value)
   }()
}
test(2121231)  

function test(value) { 
  return function() { 
    console.log(value)
  }
}

let a = test(213213)
// logs 213213
a()

本章小结

对于函数的this,大多数与他的词法作用域有关,即你写代码的地方,但是笔试的时候,常常要结合函数执行时的this情景。大多是这种情况。

  • 全局环境函数的定义,为global或window,视浏览器环境而定
  • 在原型链上定义,并使用实例调用,则指向实例本身,直接调用该函数的引用则为undefined
  • 嵌套函数的this,无论如何都指向全局
  • bind、apply、call这种情况要注意

四、Object this

  • 对象的属性,它的this始终指向定义该对象的环境this

    function a() {
      this.name = 'global'
      // 此处this为全局
      var test = {
        name: this.name,
         a: { 
           name:this.name 
         }
       }
       console.log(test.name,test.a.name)
     }
    a() // logs global global
    
  • 在一个对象中的一个函数里,你可以通过this来引用这个对象的其他属性。(相当于prototype this)

    var obj = {
      foo: "bar",
      logFoo: function () {
        console.log(this.foo);
      }
    };
    obj.logFoo(); // logs "bar"
    
  • 嵌套对象的函数定义,始终指向该对象

    var test = {
      name: '321321',
      a: {
        b: {
          name: 'bbb',
          getName: function () {
            console.log(this.name)
          }
        }
      }
    }
    test.a.b.getName()  // logs bbb
    
  • 需要你注意的地方
    箭头函数的this是静态的,它始终指向它所在对象所在函数定义时的this

    const cat = {
      lives: 9,
      jumps: () => {
        this.lives--;
     }
    }
    // 这个this指向的是window 而不是 cat 详情请看阮一峰的es6
    

五、DOM event this

  • DOM事件处理程序里面,this始终指向这个处理程序被所绑定到的HTML DOM节点
    function Listener() {
      document.getElementById("foo").addEventListener("click",this.handleClick);
    }
    Listener.prototype.handleClick = function (event) {
      console.log(this); //logs "
    " } var listener = new Listener(); document.getElementById("foo").click(); // IE的attachEvent的this不是dom节点本身 document.attachEvent('onclick',function(){ console.log(this) // logs window IE NM$L })

六、HTML this

  • 在HTML节点的属性里面,你可以放置JavaScript代码,this指向了这个元素

七、override this(重载this?)

  • 你可以通过eval来访问this(eval相当于插入一段js,它的作用域是动态的,不推荐使用)
    A.prototype.getName= function () {
        eval("console.log(this.name)"); //logs "bar"
    }
    // eval() 是全局对象的一个函数属性。 函数会将传入的字符串当做 JavaScript 代码进行执行。
    
    • 在通过Function来创建一个函数的时候,同样能够访问this ( 这没啥意思啊 )
    function Thing () {
    }
    Thing.prototype.foo = "bar";
    Thing.prototype.logFoo = new Function("console.log(this.foo);");
    var thing = new Thing();
    thing.logFoo(); //logs "bar"
    

八、with this

with是js中的关键字,它与另一个关键字catch一样,都是可以延长作用域链,但是红宝书里关于它的例子是非常少的。

  • 你可以通过with来将this添加到当前的执行环境,并且读写this的属性的时候不需要通过this
    A.prototype.name= "aab";
    A.prototype.getName= function () {
      with (this) {
        console.log(name);
        name = "ccc";
      }
    }
    var a= new A();
    // logs "aab"
    a.getName();
    console.log(a.foo); // logs "ccc"
    
    with风评不好,并且在开发中很少使用到,故这部分仅作了解

九、jQuery this

  • 在许多情况下JQuery的this都指向HTML元素节点。也就是链式操作

十、thisArg this

你可能知道许多类库的方法可以通过一个叫做thisArg 的函数参数来传递实例,这个函数参数会作为this的上下文。
例如:原生的JavaScript在ECMAScript 5的时候也允许函数传递一个thisArg参数了,比如forEach(callback,thisArg)

let a = { name: 'aab' } 
['a','b','c'].forEach((item,index)=> {
  console.log(item,this.name)
},a)
// logs a,aab b,aab, c,aab

它要解决的问题是:当函数作为参数传递时,由于指向环境的变更,导致该函数要使用bind绑定this对象再传入。使得代码更加简洁。
本质:方法里面调用了一次cal或者apply

十一、箭头函数this

  • 箭头函数体内的this,绑定在当前函数定义时的this对象
    function foo() {
      // 函数运行时,此箭头才算真正定义
      setTimeout(() => {
        console.log('id:', this.id);
      }, 100);
    }
    var id = 21;
    foo.call({ id: 42 });
    // logs 42 因为此时的this指向{ id:42 }
    
    这段代码相当于
    funciton foo() { 
      let self = this 
      setTimeout( ()=> {
        console.log('id:',self.id)
      }) 
    }
    
  • 箭头函数可以让this指向固定化
    由于箭头函数的this是静态的,所以构造函数的属性定义为箭头函数时,该函数的this不能通过bind、call、apply动态绑定this执行
    function A() { 
      this.name = 'aac'
      this.getName = ()=> { 
        console.log(this.name)
      }
      this.getName2 = function() { 
        console.log(this.name)
      }
    }
    // 用new执行函数,此时函数的this指向新创建的对象
    let a = new A()
    let b = { 
      name:'ccc'
    }
    
    // logs aac
    a.getName().call(b)
    // logs ccc
    a.getName2.call(b)
    
    
  • 定义在对象内的箭头函数的this一样指向当前对象定义时的this对象
    function A() { 
      this.name = 'aac'
      return {
        props: { 
          a: {
            name:'2232323',
            b:()=> { 
              console.log(this.name) // logs aac
            },
            c:function() {
              console.log(this.name)  // logs 2232323
            }
          } 
        }
      }
    }
    
  • 箭头函数不适合用来定义对象的属性
    例子1、
      function A() { 
      }
      A.prototype.getName = ()=> {
        console.log(this.name)
      }
      var a = new A()
      a.name = '2233'
      a.getName() // logs undefined
    
    例子2、
    var a = { 
      name : '23232',
      getName:()=> { 
        console.log(this.name)  
      }
    }
    a.getName() // logs undefined
    

你可能感兴趣的:(Javascript中的this指向)