js之this的简单总结

1,339 阅读6分钟

1 什么是this

1.1 this 的指向

this 是 js 中的关键字,看起来很混乱,但其实很好理解,用网上通用的一句话来说就是: this 的指向在函数定义时确定不了,只有函数指向的时候才能确定 this 到底指向谁,也即是说this的卒中指向的是那个调用他的对象

总结起来就六个字 谁调用,指向谁

例子1

function foo() {
    var name = "我爱js";
    console.log("this", this); // this window
    console.log("name", this.name)// this undefined
}
foo()

为什么这里的 this 是window对象,因为我们是在全局作用域中调用的,实际上是省略了 window. 看如下代码的调用方式,跟上一个完全相同

例子2

function foo() {
    var name = "我爱js";
    console.log("this", this); // this window
    console.log("name", this.name)// this undefined
}
window.foo()

效果跟上面是完全相同的

例子3

var foo = {
    name: "JavaScript",
    child: function () {
        console.log("this", this) // this {name: "JavaScript", child: ƒ}
        console.log("name", this.name) //name JavaScript
    }
}

foo.child()

在这里调用者是 foo, 所以this指向的是 foo 然而事实是这样吗,再看看上面的例子

例子4

var foo = {
    name: "JavaScript",
    child: function () {
        console.log("this", this) // this {name: "JavaScript", child: ƒ}
        console.log("name", this.name) //name JavaScript
    }
}

window.foo.child()

这里最终调用者是window,但是this并没有指向window,这是怎么回事?难道是六字真言有问题?要弄懂这个问题,我们需要再看几个例子

例子5

var foo = {
    a: 1,
    b: {
        a: 2,
        fn: function() {
            console.log(this.a) // 2
        } 
    }
}

foo.b.fn()

而且这个例子中的this也同样没指向foo, 看到这里,可能会有疑惑,会推翻六字箴言,但是,如果再补充几句话,就会消除歧义

  1. 非严格模式下,如果一个函数中有this,并且这个this没有被上一级调用,那么这个this的指向就是window,具体参考例子1,例子2

  2. 如果一个函数中有this,这个函数被上一级对象调用,那么this就指向上一级对象,具体参考例子3

  3. 如果一个函数有this,假如调用这个函数的有多级对象,那么this指向的是调用它的上一级对象,具体参考例子5

我们再来验证一下情况3 例子 6

var foo = {
    a: 1,
    b: {
        //a: 2,
        fn: function() {
            console.log(this.a) // undefined
        } 
    }
}

foo.b.fn()

此时,我们看到,输入的值是undefined,也就是说,这个时候this指向的是b的作用域,而b中没有定义a,所以输出的是undefined 然而一切并不是依靠想象,总有情况例外,我们看下一个例子

例子7

var foo = {
    a: 1,
    b: {
        a: 2,
        fn: function() {
            console.log(this) // window
            console.log(this.a) // undefined
        } 
    }
}

var tm = foo.b.fn
tm()

从这个例子中我们看到,this指向的是window,这是怎么回事?其实很好理解,还记得六字箴言吗?谁调用,指向谁,这一句var tm = foo.b.fn只是进行了赋值,把 fn赋值给了全局变量 tm ,然而最终的调用时机是在 window作用域调用的函数 tm(), 所以 this的指向是window对象。至此, this 的指向已经完毕

1.2 this指向总结

就六个字 谁调用,指向谁,另加几个条件

  1. 非严格模式下,如果一个函数中有this,并且这个this没有被上一级调用,那么这个this的指向就是window

  2. 如果一个函数中有this,这个函数被上一级对象调用,那么this就指向上一级对象

  3. 如果一个函数有this,假如调用这个函数的有多级对象,那么this指向的是调用它的上一级对象

2 改变 this 的指向

通常情况下,我们想使用其它环境变相下的状态,这就需要借助this来实现,常用改变this指向的有以下几种方式可以实现

  1. 构造函数
  2. call
  3. apply
  4. bind
2.1 构造函数改变this的指向

先看这个例子 例子8

function Foo () {
    this.name = 'Justin'
}

var f = new Foo()
console.log(f.name) // Justin

在使用 new 关键字调用构造函数时,会依次执行

  1. 首先创建一个新对象 f,并把构造函数的 prototype赋值给这个新对象的 __proto__
  2. 将这个新对象 f 赋值给this,并执行构造函数
  3. 如果函数返回了其它对象,那么 new 表达式中的函数调用会自动返回这个新对象, 否则忽略,这句话的意思是,如果函数没有 return一个对象, this代表的就是 new 出来的实例,否则,this指向的是返回的新对象

假如函数中有 return, 来看看this的指向? 看下面这个例子 例子9

function Foo () {
    this.name = 'Justin'
    return {}
}

var f = new Foo()
console.log(f.name) // undefined

在这里, 输出的是undefined,也就是说,this根本没有指向 f, 那么this到底指向哪里了呢 其实,如果一个返回值是对象,那么this指向的就是返回的对象, 如果反回的不是对象,那么指向的还是指向函数的实例, null除外

例子10 利用下面几个例子验证

function Foo () {
    this.name = 'Justin'
    return function(){}
}

var f = new Foo()
console.log(f.name) // undefined

例子11

function Foo () {
    this.name = 'Justin'
    return 1
}

var f = new Foo()
console.log(f.name) // Justin

例子12


function Foo () {
    this.name = 'Justin'
    return undefined
}

var f = new Foo()
console.log(f.name) // Justin

例子13

function Foo () {
    this.name = 'Justin'
    return null
}

var f = new Foo()
console.log(f.name) // Justin

以上跟第三点总结相符合,至此,构造函数改变this指向解析完毕

2.2 通过 call, apply, bind 改变 this 的指向

例子13

var w = "流星火雨"
var obj = {
    name: "黄忠",
    w: this.w,
    state: function(name, ow){
        console.log(`${this.name} 的 w 技能是 ${this.w}`)
    }
}

var skills = {
    name: '马岱',
    w: "紫电箭雨"
}
obj.state() // 忠 的 w 技能是 undefined
obj.state.call(skills) //马岱 的 w 技能是 紫电箭雨
obj.state.apply(skills) //马岱 的 w 技能是 紫电箭雨
obj.state.bind(skills) //马岱 的 w 技能是 紫电箭雨

从上面看出来, call, apply, bind把this的指向改变为call, apply, bind所传入的第一个参数, 除此之外,他们是没有什么区别的

2.3 call, apply, bind的区别

先看一个例子

var w = "流星火雨"
var obj = {
    name: "黄忠",
    w: this.w,
    state: function(HeroType, HP){
        console.log(`${this.name} 的 w 技能是 ${this.w}, 他是一个 ${HeroType}, 他的血量是${HP}`)
    }
}

var skills = {
    name: '马岱',
    w: "紫电箭雨"
}
obj.state() // 忠 的 w 技能是 undefined
obj.state.call(skills, "法师", 100) //马岱 的 w 技能是 紫电箭雨
obj.state.apply(skills, ["法师", 100]) //马岱 的 w 技能是 紫电箭雨
obj.state.bind(skills, "法师", 100)() //马岱 的 w 技能是 紫电箭雨

不同的是, apply第二个参数接受的是一个数组, call 直接按参数排列就可以了,而 bind 跟call相似,只不过后面多了个 ()

最后,再加一个复杂的案例

function foo() {
    let args = Array.prototype.slice.apply(arguments)
    console.log("args", args) //[ '国家队', '黄忠', '典韦', '张莹莹', '黄盖', '许褚' ]
}

foo("国家队", "黄忠", "典韦", "张莹莹", "黄盖", "许褚")

更多请参考: www.iquanku.com/admin/28.ht…