this

this的重要性

this是JavaScript中最为关键的语法点,一般在简单项目里,this可有可无,但大部分的开发任务中,this起着关键作用。

this的由来

以下面JS代码为例,调用该自身对象的其它属性的值。

var obj = { 
name: 'cheche',
say: function(){
      console.log('你好,'+obj.name)
       }
 }
obj.say()   // 'cheche'

假如我们要在控制台打印出你好,cheche,通过上面的JS代码虽然用变量获取可以实现,但通过写obj.name,这样严重依赖了变量名,如果变量名obj变了,那么里面用到变量名的地方都得改变,上面代码的写法显然不可取。
为了不依赖变量名,可以给obj.say()传入一个参数,

var obj = {
name: 'cheche',
say: function(person,问候语){
       console.log(问候语+','+person.name)
     }
}
obj.say(obj,'早')   // '早,cheche'

这样的写法算是比较友好的,没有依赖变量名。但是得传入两个参数,那能不能只传一个参数?
如果只传一个参数便是这种形式obj.say('早'),但obj.say(obj,'早')obj.say('早')这二者很难区分,所以JS之父布兰登·艾克把第一个参数隐藏起来,但隐藏起来之后,怎么知道person.name是什么,于是就引入了this作为它的第一个参数。那么上面的代码就变成了这样,

var obj = {
name: 'cheche',
say: function(问候语){
     console.log(问候语+','+this.name)
     }
}
obj.say('早')      // '早,cheche'

其中obj,say('早')形式上等价于obj.say.call(obj,'早'),前者是一种语法糖的形式,后面这种才是计算机真正认为的,浏览器认为obj === this,所以我们可以认为this就是函数call的第一个参数。

this的概念

this的数据类型

var obj = {
  foo: function(){
    console.log(this)
  }
}
var bar = obj.foo
obj.foo()   //obj
bar()      //undefined => Window

上面代码里,调用obj.foo()打印出来的是obj这个对象自身,因为obj.foo()形式上等于obj.foo.call(obj),而call的第一个参数就是this,所以我们知道了this就是一个对象,即使你指定的this是数字1,它也会变为一种对象的形式。

img

在调用bar()时,并没有指定this,形式上相当于bar.call(undefined),这时浏览器会默认this等于window对象。但在nodejs里,this === global,global是个全局对象。
不要以为bar = obj.foo,就认为bar() === obj.foo(),函数是独立存在的,不会进行补全。

this这个参数

当函数未调用时,this只是一个参数,无法确认this是什么。

var fn = function(p1){
console.log(this)
console.log(p1)
}

这是你无法确认this和p1是什么,因为他们只是参数。当你调用之后,便可以指定参数了。

this的确定

既然我们知道,函数传的第一参数就是this,那我们该如何确定它?

  1. 通过console.log(this)
    这是最简单的方法,但是缺乏说服力。
  2. 读DOM或jQuery的源代码
    水平不够,读不懂
  3. 查对应API的文档
    html:<button id="xxx">点我</button>
    js:xxx.addEventListener('click',function(){console.log(this)}) //点击button
    

通过查EventTarger.addEventListener文档,可以知道this值是触发事件元素的引用。所以,this === xxx,即等于button元素。

改变this的值

var obj = {
 fn : function(){
console.log(this)}
}
setTimeout(obj.fn,1000)  //window对象

当向setTimeout()传递一个函数时,该函数的this会指向一个错误的值,在非严格模式下,this是window对象,而在严格模式下,this是undefined。那我们该如何让this为obj这个对象呢?有三种方法。

  1. 用包装函数
    setTimeout(function(){obj.fn()},1000),这样强制指定this为obj

  2. bind

    setTimeout(obj.fn.bind(obj),1000)
    

    obj.fn.bind(obj)会返回一个新的函数,在形式上等于

    function(){obj.fn()}
    

    可以理解为在

    obj.fn
    

    外套一层,重新写个call覆盖原来的this。

  3. this的高级用法

var a = [1,2,3]
var b = a.join()
var c = [4,5,6]
var d = a.join.call(c)
console.log(b)  // '1,2,3'
console.log(d)  // '4,5,6'

array.join()是个数组的API,是将数组所有元素连接到字符串中,默认以逗号隔开。那么a.join(),里面的join是怎么获取到数组a的,它的原理是什么?我们可以做出以下猜想。

var a = [1,2,3]
var a.join = function(){
var result = ''
for(var i=0;i<this.length;i++){
  if(i !== this.lenght -1){  //最后一个不用加逗号
   result += this[i] + ','
  }else{
    result +=this[i] }
}                            

我们做出猜想,join函数里有用了this,而这个this等于数组a。如果把this的传入改为数组c,那么join函数会把数组c变成字符串d。也就是说数组c调用了数组a的方法。
如果c不是一个数组,而是一个伪数组,同样可以用a的方法。a的slice方法操作了this,把c当作this给a的slice操作就行了。slice()是一个截取数组的方法

var a = [1,2,3]
var c = {0:4,1:5,2:6,length:3}
var e = a.slice.call(c,0,2)
console.log(e) // [4,5]
  1. 打出想要的this
html:
<button id=xxx name="curry"
javascript:
var obj = {
name: 'allen',
say: function(){
    xxx.onclick = function(){console.log(this.name)}
    }
}
obj.say()  //xxx.name

此时this是xxx这个按钮,我们想打出’cheche’,怎么办?

  • 古老方法
    将函数外的this赋值给一个变量,然后把函数里的this改成那个变量就行了。

    var obj = {
    name: 'cheche',
    var _this = this
    say: function(){
       xxx.onclick = function(){console.log(_this.name)}
      }
    }
    obj.say()  // 'cheche'
    
  • bind

    var obj = {
    name: 'cheche',
    say: function(){
       xxx.onclick = function(){console.log(_this.name)}
      }.bind(this)
    }
    obj.say()  // 'cheche'
    

此时,bind传入的this是函数外的this,可以理解为bind()是最里面的this传入,当然以它传入为准。

  • 箭头函数
    var obj = {name: 'cheche',say: function(){xxx.onclick = ( ) =>  {console.log(this.name)}}obj.say()  // 'cheche'
    

箭头函数没有隐藏的this,它自身并不会传入this,所以箭头函数里的this不是被点击的元素,而是它外面的this。

箭头函数无法用call()指定this

this的特殊用法

在new关键字调用构造函数时,构造函数里的this表示的是一个实例对象。

下边this因为

function Arr(id){
 this.id = id
}
var s = new Arr(1)

此时这个this就是对象`s’。

下边代码this指向的是被点击button元素

var button = document.querySelector('button');
var name = 'jack';
var object = {
    name: 'lucy',
    sayHi: function() {
        var that = this;
        button.onclick = function() {
            console.log(that.name);
        }
    },

}
object.sayHi();   //btn

setTimeout和setInterval this处理

setTimeout中函数内的this是指向了window对象,这是由于setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致这些代码中包含的 this 关键字会指向 window (或全局)对象.

方法一:保存this变量

var num = 0;
function Obj (){
    var that = this;    //将this存为一个变量,此时的this指向obj
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(function(){
            console.log(that.num);    //利用闭包访问that,that是一个指向obj的指针
        }, 1000)
    }
}
var obj = new Obj; 
obj.getNum();//1  打印的是obj.num,值为1
obj.getNumLater()//1  打印的是obj.num,值为1

方法二bind

var num = 0;
function Obj (){
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(function(){
            console.log(this.num);
        }.bind(this), 1000)    //利用bind()将this绑定到这个函数上
    }
}
var obj = new Obj; 
obj.getNum();//1  打印的为obj.num,值为1
obj.getNumLater()//1  打印的为obj.num,值为1

方法三箭头函数

function Obj (){
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(() => {
            console.log(this.num);
        }, 1000)    //箭头函数中的this总是指向外层调用者,也就是Obj
    }
}
var obj = new Obj; 
obj.getNum();//1  打印的是obj.num,值为1
obj.getNumLater()//1  打印的是obj.num,值为1
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容