JavaScript的this

在JavaScript中this是一个永恒的话题,正在能够完全掌握其实不易,本文主要讲解下一下几种情况下的判定:

  • 全局情况下
  • 对象调用下
  • call、apply、bind方法
  • dom事件绑定
  • setTimeout、setInterval
  • ajax等异步操作

全局window/global

一个在模块或者全局的情况下调用一个方法时【不在使用 'use strict'的情况,否则不会默认将this指向全局】

    var x = 9;
  function test(){
    this.x = 6;
  }
  test();
  alert(x); //输出6

可以知道,在这种情况下的this一般是window或者node中的global;

对象调用

这里可能是方法作为构造函数来使用,那么这个方法中的this会指向这个构造方法的实例,

 function test(){
    this.x = 1;
  }
  var o = new test();
  alert(o.x); // 1

可以看到,在一些情况下如果出现了类似的调用写法的话,this一般指向的就是调用者本身,形如【object.fn()】,这种情况下的fn里面的this一般指向前面调用fn方法的object对象;

call、apply、bind方法

apply()和call()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。这里就不讲述两者的不同了【基本就是两个函数的参数使用不一致】;

var x = 0;
  function test(){
    alert(this.x);
  }
  var o={};
  o.x = 1;
  o.m = test;
  o.m.apply(); //0
    o.m.apply(o); //1

ECMAScript 5 引入了 Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。也和call、apply用相同作用;
以上参考了:http://www.ruanyifeng.com/blo...
https://developer.mozilla.org...

dom事件绑定

在使用js操作dom的时候,很常见的会绑定事件;比如下面的将元素p绑定click事件,然后事件的回调函数中就可以使用this;this指向这个时间的目标元素,也就是绑定事件的具体元素;

function bluify(e){
  console.log(this === e.currentTarget); // 总是 true
  // 当 currentTarget 和 target 是同一个对象是为 true
  console.log(this === e.target);        
  this.style.backgroundColor = '#A5D9F3';
}
// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('p');
// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for(var i=0 ; i<elements.length ; i++){
  elements[i].addEventListener('click', bluify, false);
}

而在jQuery的使用中我们基本就是使用$(this)来表示事件的目标元素;因为this就是e.target;而把一个dom元素变为jQuery元素就是使用$把他抱起来就好了;

setTimeout、setInterval的this

在js中,window实现了WindowOrWorkerGlobalScope这个mixin的所有方法,其中最被经常使用的就是setTimeout、setInterval,因为这是一个异步的过程:

var x = 33;
var test = { 
    x: 22,
    test:function(){
        console.log(this.x)
    },
    time:function(){
        setTimeout(this.test,1000);
    }
}
test.time(); //输出33,不是22

在使用异步的setTimeout、setInterval时候,由于需要等待特定时间之后在进行执行,所以这个就和setTimeout、setInterval的执行有关,在https://zhuanlan.zhihu.com/p/...https://jakearchibald.com/201... 中可以知道,其实就是event loop的概念,所以其实在使用setTimeout、setInterval的时候会出现等待的时长并不是精准的参数中写入的时间,原因如下:

  • setTimeout、setInterval会把参数中的函数单独放在macrotask中,这里面是需要执行的event队列;
  • 当计时到达时,从macrotask提前需要定时执行的函数,等待当前正在执行的事件队列执行完成【这里还有当前macrotask后面的mairotask】这里的当前正在执行的就是引起计时不准确的原因;
  • 当执行完当前的macrotask+microtask以后,就开始执行定时函数,这个时候由于是新的全局下执行这个函数【这个时候就和本文第一种说的全局下调用function一样了,this指向window】,所以this会被window覆盖掉,故而不是在编写setTimeout时的执行上下文,函数的[[scrope]]时候的this,
  • 具体的macrotask+microtask可以参考https://zhuanlan.zhihu.com/p/...https://jakearchibald.com/201...

故而解决方法如下:

  • 把this重命名保存:self=this;这样self还是函数[[scrope]]中的this;而不会被window替换
  • 使用bind把this绑定
  • 使用立即执行函数来函数再包一层
    this.intervalID = setInterval(
        (function(self) {     //Self-executing func which takes 'this' as self
            return function() {    //Return a function in the context of 'self'
                   self.retrieve_rate();   //Thing you wanted to run as non-window 'this'
               }
         })(this),
     this.INTERVAL     //normal interval, 'this' scope not impacted here.
 ); 

ajax中的this

在ajax中我们都会非常谨慎的使用this;这个异步操作在this上也是和setTimeout差不多,肯定是会被覆盖的,但是并不是被window覆盖,相反而是被XMLHttpRequest对象覆盖:

{
    "url": "demo_test.txt",
    "type": "GET",
    "isLocal": false,
    "global": true,
    "processData": true,
    "async": true,
    "contentType": "application/x-www-form-urlencoded; charset=UTF-8",
    "accepts": {
        "*": "*/*",
        "text": "text/plain",
        "html": "text/html",
        "xml": "application/xml, text/xml",
        "json": "application/json, text/javascript",
        "script": "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
    },
    "contents": {
        "xml": {},
        "html": {},
        "json": {},
        "script": {}
    },
    "responseFields": {
        "xml": "responseXML",
        "text": "responseText",
        "json": "responseJSON"
    },
    "converters": {
        "text html": true
    },
    "flatOptions": {
        "url": true,
        "context": true
    },
    "jsonp": "callback",
    "dataTypes": [
        "text"
    ],
    "crossDomain": false,
    "hasContent": false
}

上面是一个ajax中的this的json格式,测试地址:http://www.runoob.com/try/try... 只需要在ajax的回调中打印this【console.log(JSON.stringify(this))】就可以在开发者工具的console中看到this这面目,也就是上面的格式的数据。


caoweiju
1.5k 声望53 粉丝

class Myself {