JavaScript中的this关键字的几种用法

JS 里的 this

  • 在 function 内部被创建
  • 指向调用时所在函数所绑定的对象(拗口)
  • this 不能被赋值,但可以被 call/apply 改变

1. this 和构造函数

    function C(){
    this.a = 37;
    }

    var o = new C();
    console.log(o.a); // logs 37


    function C2(){
    this.a = 37;
    return {a:38};
    }

    var b = new C2();
    console.log(b.a); // logs 38

2. this 和对象

对象内部方法的this指向调用这些方法的对象:

  • 函数的定义位置不影响其this指向,this指向只和调用函数的对象有关。
  • 多层嵌套的对象,内部方法的this指向离被调用函数最近的对象(window也是对象,其内部对象调用方法的this指向内部对象, 而非window)。
    //1:this指向调用函数的对象
    var o = {
        prop: 37,
        f: function() {
            return this.prop;
        }
    };
    console.log(o.f());  //37   this指向o
    var a = o.f;
    console.log(a()):  //undefined  this指向a ,a中没有定义prop

    var o = {prop: 37};
    function independent() {
        return this.prop;
    }
    o.f = independent;
    console.log(o.f()); // logs 37      this指向o

    //2:this指向离被调用函数最近的对象
     var o = {
            prop: 37,
            f: function() {
                return this.prop;
            }
        };
        function independent() {
            return this.prop;
        }
        o.b = {
            g: independent,
            prop: 42
            };
            console.log(o.b.g());   //42    this指向o.b

3. this 和函数

普通函数内部的this分两种情况,严格模式非严格模式

//非严格模式下,this 默认指向全局对象window
function f1(){
  return this;
}
f1() === window; // true

//而严格模式下, this为undefined
function f2(){
  "use strict"; // 这里是严格模式
  return this;
}
f2() === undefined; // true

4. 全局环境的this

前面提到 this 是 “指向调用时所在函数所绑定的对象”, 这句话拗口但绝对正确,没有一个多余的字。
全局环境中有不同的宿主对象,浏览器环境中是 window, node 环境中是 global。这里重点说下浏览器环境中的 this。
浏览器环境中非函数内 this 指向 window

    alert(window=== this) // true

因此你会看很很多开源 JS lib 这么写

    (function() {
        // ...
        
    })(this);

或这样写

    (function() {
        // ...
    
    }).call(this);

比如 underscore 和 requirejs,大意是把全局变量 window 传入匿名函数内缓存起来,避免直接访问。至于为啥要缓存,这跟 JS 作用域链有关系,读取越外层的标识符性能会越差。

浏览器中比较坑人,非函数内直接使用 var 声明的变量默认为全局变量,且默认挂在 window 上作为属性。

    var andy = '刘德华'
    alert(andy === window.andy) // true
    alert(andy === this.andy) // true
    alert(window.andy === this.andy) // true

因为这个特性,有些笔试题如

    var x = 10;
    function func() {
        alert(this.x)
    }
    var obj = {
        x: 20,
        fn: function() {
            alert(this.x)
        }
    }
    var fn = obj.fn
    func() // 10
    fn() // 10

没错,最终输出的都是全局的 10。永远记住这一点:
判断 this 指向谁,看执行时而非定义时只要函数(function)没有绑定在对象上调用,它的 this 就是 window

5. this和DOM事件

当函数被当做监听事件处理函数时, 其 this 指向触发该事件的元素 (针对于addEventListener事件)

扫描二维码关注公众号,回复: 3961980 查看本文章
    // 被调用时,将关联的元素变成蓝色
    function bluify(e){
      //在控制台打印出所点击元素
      console.log(this);
      //阻止时间冒泡
      e.stopPropagation();
      //阻止元素的默认事件
      e.preventDefault();      
      this.style.backgroundColor = '#A5D9F3';
    }

    // 获取文档中的所有元素的列表
    var elements = document.getElementsByTagName('*');

    // 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
    for(var i=0 ; i<elements.length ; i++){
      elements[i].addEventListener('click', bluify, false);

6. this和内联事件

内联事件中的this指向分两种情况:

  • 当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素
  • 当代码被包括在函数内部执行时,其this指向等同于 函数直接调用的情况,即在非严格模式指向全局对象window, 在严格模式指向undefined



    依次点击上边的三个按钮后在控制台的输出结果

    7. setTimeout & setInterval

    对于延时函数内部的回调函数的this指向全局对象window(当然我们可以通过bind方法改变其内部函数的this指向)
    看下边代码及截图
    //默认情况下代码
    function Person() {  
        this.age = 0;  
        setTimeout(function() {
            console.log(this);
        }, 3000);
    }

    var p = new Person();//3秒后返回 window 对象
    ==============================================
    //通过bind绑定
    function Person() {  
        this.age = 0;  
        setTimeout((function() {
            console.log(this);
        }).bind(this), 3000);
    }

    var p = new Person();//3秒后返回构造函数新生成的对象 Person{...}


8. this可以被 call/apply 改变

当函数通过Function对象的原型中继承的方法 call() 和 apply() 方法调用时, 其函数内部的this值可绑定到 call() & apply() 方法指定的第一个对象上, 如果第一个参数不是对象,JavaScript内部会尝试将其转换成对象然后指向它。

例子:

    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

    add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

    function tt() {
    console.log(this);
    }
    // 返回对象见下图(图1)
    tt.call(5);  // Number {[[PrimitiveValue]]: 5} 
    tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}

9. me/self/that/_this 暂存 this

如果采用 OOP 方式写 JS 代码,无可避免的会用到 this,方法内会访问类的内部属性(字段),也可能会调用类的另一个方法。当类的方法内又有一个 function 时,比如浏览器端开发经常遇见的给 DOM 元素添加事件,这时如果事件处理器(handler)中的想调用类的一个方法,此时 handler 内的 this 是 dom 元素而非类的当前对象。这个时候,需要把 this 暂存,开发者发挥着自己的聪明才智留下了几种经典的命名** me, self, that, _this**。如


如:

一般会在每个方法的第一句就把 this 暂存下来

10. ES5 中新增的 bind 和 this

bind方法在ES5引入, 在Function的原型链上, Function.prototype.bind。通过bind方法绑定后, 函数将被永远绑定在其第一个参数对象上, 而无论其在什么情况下被调用。

function f(){
  return this.a;
}

var g = f.bind({a:"azerty"});
console.log(g()); // azerty

var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty

11. ES6 箭头函数=> 和 this

箭头函数的一个重要特征就是颠覆了上面的一句话,再贴一次

判断 this 指向谁,看执行时而非定义时,只要函数(function)没有绑定在对象上调用,它的 this 就是 window

是的,前面一直用这句话来判断 this 的指向,在箭头函数里前面半句就失效了。箭头函数的特征就是,定义在哪,this 就指向那。即箭头函数定义在一个对象里,那箭头函数里的 this 就指向该对象。如下

var book = {
    author: 'John Resig',
    init:  function() {
        document.onclick = ev => {
            alert(this.author) ; // 这里的 this 不是 document 了
        }
    }
};
book.init()

对象 book 里有一个属性 author, 有一个 init 方法, 给 document 添加了一个点击事件,如果是传统的函数,我们知道 this 指向应该是 document,但箭头函数会指向当前对象 book。

箭头函数让 JS 回归自然和简单,函数定义在哪它 this 就指向哪,定义在对象里它指向该对象,定义在类的原型上,就指向该类的实例,望文知意这样更容易理解。
作为方法的箭头函数this指向全局window对象,而普通函数则指向调用它的对象

原文参考:
https://www.cnblogs.com/snandy/p/4773184.html
http://www.cnblogs.com/dongcanliang/p/7054176.html

猜你喜欢

转载自www.cnblogs.com/amcy/p/9919884.html