this 初探

经济基础决定上层建筑。


我来了

  • 学习,记录,备忘。
  • 感谢参考过的所有资料的作者。
  • 嗯,能看源码的就不要看文档,能看英文文档的就不要看中文文档,能自己上手验证的就不要仅仅参考。请保持质疑。深有所感。

this

定义

A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.
In most cases, the value of this is determined by how a function is called. It can't be set by assignment during execution, and it may be different each time the function is called. ES5 introduced the bind method to set the value of a function's this regardless of how it's called, and ES2015 introduced arrow functions which don't provide their own this binding (it retains the this value of the enclosing lexical context).
  1. js 的严格模式和非严格模式有所不同。至于有什么不同,可能写个 demo 比听别人解释更直接。
  2. 大多数情况,this 的值取决于 function 是怎么被调用的。在执行期间,是不能通过赋值来设置 this 的值的,并且每次 function 的调用,this 的值也是可能不同的。

可能较为难理解的就是 this 所对应的 context 了。
以下暂不考虑严格模式。

Global Context

在全局执行 context 下,无论是否在严格模式下,this 指向全局对象。

// 浏览器中,全局对象指 window
console.log(this === window) // true
  
// 全局调用 alert
// 相当于:window.alert(this)
alert(this) // [Object Window]

Function Context

Inside a function, the value of this depends on how the function is called.

在函数内,this 取决于这个函数是如何被调用的。取决于调用这个函数的对象。

  1. Simple call

    function sayName() {
      console.log(this.name)
    }
    
    var name = '~ window ~'
    
    var apple = {
      name: 'apple'
    }
    
    var banana = {
      name: 'banana'
    }
    
    // 全局调用,相当于:window.getThis(),this 指向 window
    sayName() // ~ window ~
    
    // call 改变 this 指向,this 指向 apple
    sayName.call(apple) // apple
    
    // apply 改变 this 指向,this 指向 banana
    sayName.apply(banana) // banana
    
    // 注意:
    // 在使用 call/apply 来改变 this 的指向的时候,
    // 如果第一个参数数据类型不是 object 时,如:7 或者 'test',内部会尝试将该值用其相关的构造函数转为对象,
    // 即:7 => new Number(7), 'test' => new String('test')
    
    function getThis() {
      console.log(this)
    }
    
    var str_1 = 'test'
    getThis.apply(str_1) // 原谅愚笨,这个结果不知如何描述,可测。
  2. The bind method

    ECMAScript 5 introduced Function.prototype.bind. Calling f.bind(someObject) creates a new function with the same body and scope as f, but where this occurs in the original function, in the new function it is permanently bound to the first argument of bind, regardless of how the function is being used.

    调用 f.bind(someObject) 会创建一个新 function ,这个新 function 拥有函数 f 相同的主体和作用域。但是 this 的存在只出现在原始函数(f)中,在新 function 中 ,this 就被永久的绑定在 bind 的第一个参数(someObject)上,无论这个新 function 是怎么被调用。

    function sayName() {
      console.log(this.name);
    }
      
    var apple = {
      name: 'apple'
    }
    
    var banana = {
      name: 'banana'
    }
    
    // bind 返回一个新函数,且新函数 this 已经绑定 apple
    var appleName = sayName.bind(apple)
    appleName() // apple
    
    // 由于 appleName 已经永久绑定 apple,即使再次绑定 banana,this 依然指向 apple
    var bananaName_1 = appleName.bind(banana)
    bananaName_1() // apple
    
    // 但是重新对原始函数进行 bind 绑定,会再次返回一个新函数,新函数 this 永久绑定 banana
    var bananaName_2 = sayName.bind(banana)
    bananaName_2() // banana
    // interesting...
  3. Arrow functions

    In arrow functions, this retains the value of the enclosing lexical context's this. In global code, it will be set to the global object.
    In arrow functions, this is set to what it was when it was created (in the following example, the global object). The same applies to arrow functions created inside other functions: their this remains that of the enclosing lexical context.

    箭头函数中的 this 保留 the enclosing lexical context 中 this 的值。全局环境中箭头函数 this 指向全局变量。
    箭头函数中的 this 指向定义这个箭头函数时所在对象(下面的例子中,this 指向全局对象)。在其他函数内部的箭头函数,这些箭头函数的 this 保留 the enclosing lexical context 。

    var getThis = (() => this)
    console.log(getThis() === window)
    // Create obj with a method bar that returns a function that
    // returns its this. The returned function is created as 
    // an arrow function, so its this is permanently bound to the
    // this of its enclosing function. The value of bar can be set
    // in the call, which in turn sets the value of the 
    // returned function.
    var obj = {bar: function() {
      var x = (() => this);
      return x;
    }
    };
    
    // Call bar as a method of obj, setting its this to obj
    // Assign a reference to the returned function to fn
    var fn = obj.bar();
    
    // Call fn without setting this, would normally default
    // to the global object or undefined in strict mode
    console.log(fn() === obj); // true
    
    // But caution if you reference the method of obj without calling it
    var fn2 = obj.bar;
    // Then calling the arrow function this is equals to window because it follows the this from bar.
    console.log(fn2()() == window); // true
    // 此例子觉得 MDN 上讲解的很到位了
    Note: if this arg is passed to call, bind, or apply on invocation of an arrow function it will be ignored. You can still prepend arguments to the call, but the first argument (thisArg) should be set to null.

    注意:箭头函数的 call/apply/bind 的第一个绑定参数会被忽略。依然可以用这些方法传递后面的参数,但是第一个参数应设置为 null 。

    var sayName = (() => this.name)
    
    var apple = {
      name: 'apple'
    } 
    
    var banana = {
      name: 'banana'
    }
    
    // this 依然指向全局对象 window
    console.log(sayName.call(apple))  // ''
    console.log(sayName.bind(banana)()) // ''
  4. As an object method

    When a function is called as a method of an object, its this is set to the object the method is called on.

    当一个函数作为一个对象的方法被调用时,这个函数的 this 指向调用该方法的对象。

    var apple = {
      name: 'apple',
      category: 'fruit',
      sayName: function (){
          console.log(this.name)
      }
    }
    
    function getCategory() {
      console.log(this.category) 
    }
    
    apple.getCategory = getCategory
    
    apple.getCategory() // fruit
    apple.sayName() // apple
    Similarly, the this binding is only affected by the most immediate member reference.

    this 的指向只与最直接调用函数的对象有关.

    var apple = {
      name: 'apple',
      category: 'fruit',
      sayName: function (){
          console.log(this.name)
      }
    }
    
    function madeIn() {
      console.log(this.name)
    }
    
    apple.produce = {
       name: 'China',
       madeIn: madeIn
    }
    
    // this 指向最直接调用自己的对象 apple.produce
    apple.produce.madeIn()
    The same notion holds true for methods defined somewhere on the object's prototype chain. If the method is on an object's prototype chain, this refers to the object the method was called on, as if the method were on the object.

    对象原型链上的方法也是如此(即: this 指向调用该方法的对象)。如果这个方法在对象的原型链上,当这个对象调用方法时,this 指向这个对象。正如对象的方法一样。

    var base_methods = {
      sayName: function() {
        console.log(this.name)
      },
      getCategory: function() {
        console.log(this.category)
      }
    }
    
    var apple = Object.create(base_methods)
    apple.name = 'apple'
    apple.category = 'fruit'
    
    apple.sayName() // apple
    apple.getCategory() // fruit
    // interesting...
    Again, the same notion holds true when a function is invoked from a getter or a setter. A function used as getter or setter has its this bound to the object from which the property is being set or gotten.

    getter/setter 中的函数调用也是如此(即: this 指向调用该方法的对象)。用作 getter/setter 的函数,this 指向用这个函数定义 get/set 属性的那个对象 。

    var apple = {
      name: 'apple'
    }
    
    function sayName() {
      console.log(this.name)
    }
    
    Object.defineProperty(apple, 'sayName', {
      get: sayName, enumerable: true, configurable: true
    })
    
    apple.sayName // apple
    // 目前对 getter/setter 的执行暂未深入了解
  5. As a constructor

    When a function is used as a constructor (with the new keyword), its this is bound to the new object being constructed.
    While the default for a constructor is to return the object referenced by this, it can instead return some other object (if the return value isn't an object, then the this object is returned).

    当一个 function 作为一个 constructor 的时候,在用 new 关键字实例化一个新对象之后,funtion 的 this 指向这个新对象。
    需要额外注意的是,constructor 在没有 return 的时候,默认返回一个 this 引用的对象,也可以指定 return 一个其他对象(如果 return 的不是一个对象,那么返回的是 this 引用的对象)
    constructor 如何执行不做赘述。

    function Person() {
      this.nature = 'Person'
    }
    
    function Teacher() {
      this.nature = 'Teacher'
      return {
        name: 'Jane'
      }
    }
    
    function Student() {
      this.nature = 'Student'
      return 'Summer'
    }
    
    var a = new Person()
    console.log(a.nature) // Person
    
    var b = new Teacher()
    console.log(b.nature) // undefined
    console.log(b.name) // Jane
    
    var c = new Student()
    console.log(c.nature) // Student
  6. As a DOM event handler
    当一个 function 用于在 DOM 的事件监听中, this 指向这个 DOM 元素。

    function getThis() {
      console.log(this)
    }
    
    var demo = document.getElementById('demo') // html 需要自己写
    
    demo.addEventListener('click', getThis, false) // this 指向 id='demo' 的 DOM 元素
    demo.addEventListener('mouseover', function() {
      console.log(this) // this 指向 id='demo' 的 DOM 元素
    }, false)
  7. In an inline event handler

    // 在 HTML 的 DOM 的内联中监听事件
    // 以下的 this 指向正在监听的 DOM 本身,即:这个 button 元素
    <button onclick="console.log(this)">
      Show this
    </button>
    
    // 然而下面的写法,在内联事件内部函数体中的 this 指全局对象 window
    <button onclick="console.log((function() { return this })())">
      Show inner this
    </button>

参考


好记性不如烂笔头。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值