详解this的指向

本文目录:

  • 1.在方法中,this 指的是所有者对象
  • 2.单独使用的时候,this 指的是全局对象
  • 3.在函数中,this 指的是全局对象,严格模式下,this 是 undefined
  • 4.在事件中,this 指的是接收事件的元素
  • 5.this指向错乱问题及硬绑定方法
  • 6.call()、apply()、bind()的联系和区别

JavaScript this 关键词指的是它所属的对象。
它拥有不同的值,具体取决于它的使用位置:

1.在方法中,this 指的是所有者对象

var person = {
  firstName: "Bill",
  lastName : "Gates",:
  id: 678,
  fullName : function() {
    return this.firstName + " " + this.lastName;
  }
};

本例中,fullName方法的拥有者是person对象,所有方法中的this 指的就是 person 对象
在JS对象中出现的this指向的都是对象本身

2.单独使用的时候,this 指的是全局对象

var x = this;
// [object Window]

在浏览器窗口中,全局对象是 [object Window]

3.在函数中,this 指的是全局对象,严格模式下,this 是 undefined

在 JavaScript 函数中,函数的拥有者默认绑定 this。
因此,在函数中,this 指的是全局对象 [object Window]。

function myFunction() {
  return this;
  // [object Window]
}

注:在函数中使用时,在严格模式下,this 是未定义的(undefined)。

4.在事件中,this 指的是接收事件的元素

在 HTML 事件处理程序中,this 指的是接收此事件的 HTML 元素


其他形式的事件绑定



function ClickOn(obj){
    obj.style.width="200px";
 // obj当前点击的标签
}

  document.getElementById('i1').onclick = function(){
             this.style.width="200px";
     // this 代指当前点击的标签
  }

注意:

  • 箭头函数里面没有自己的this
  • 构造函数的this默认为实例对象
  • this的绑定和函数的声明位置没有关系,只取决于函数的调用方式,当一个函数被调用时,会创建一个活动记录(有时候也被称为执行上下文)。这个记录会包含函数在哪里调用、传入的参数等信息,this就是这个活动记录的一个属性,会在函数执行的过程中用到。简单一句话就是:谁去调用它,this就指向谁。

下面代码的清楚结果是什么?

function foo() {
    console.log(this.a)
}
var obj = {
    a: 2,
    foo: foo
}
obj.foo()

关注点:谁调用的?谁调用,指向谁
输出结果为:2

function foo() {
    console.log(this.a)
}
var obj2 = {
    a: 42,
    foo: foo
}
var obj1 = {
    a: 2
    obj2: obj2
}
obj1.obj2.foo()

输出结果为:42
调用foo方法的是obj1.obj2
需要注意的是代码调用和代码使用并不是一回事,以下面的代码为例

function foo() {
    console.log(this.a)
}
var obj = {
    a: 2,
    foo: foo
}
var bar = obj.foo
var a = '我是全局属性a'
bar()

输出结果为:我是全局属性a
原因是?
var bar = obj.foo的foo后面没有加括号,所以不是调用,而是使用代码

function foo() {
    console.log(this.a)
}
function deFoo(fn) {
    fn()
}
var obj = {
    a: 2,
    foo: foo
}
var a = '我是全局变量a'
deFoo(obj.foo)

输出结果为:
我是全局变量a

function foo(){
    console.log(this.a)
}
var obj = {
    a:2,
    foo:foo
}
var a= '我是全局变量a'
setTimeout(obj.foo,100)

输出结果为:我是全局变量a
思考一下:结果为什么是‘我是全局变量a’呢?

首先要了解一件事情,this的指向并不是不可改变的,最典型的情景就是像 call() 和 apply() 这样的方法可以将 this 引用到任何对象
call() 和 apply() 方法是预定义的 JavaScript 方法。
它们都可以用于将另一个对象作为参数调用对象方法。

var person1 = {
  fullName: function() {
    return this.firstName + " " + this.lastName;
  }
}
var person2 = {
  firstName:"Bill",
  lastName: "Gates",
}
person1.fullName.call(person2);  // 会返回 "Bill Gates"

5.this指向错乱问题及硬绑定方法

5.1.定时器

使用js中的定时器(setInterval,setTimeout),很容易会遇到this指向的问题。

var name = 'my name is window';
var obj = {
    name: 'my name is obj',
     fn: function () {
        var timer = null;
        clearInterval(timer);
        timer = setInterval(function () {
             console.log(this.name);  //my name is window
        }, 1000)
     }
 }

在这里,从this.name可以看出this的指向是window。
如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。这是因为JS的定时器方法是定义在window下的。

5.2.事件绑定

在事件绑定的函数中有容易this指向发生改变的现象。

为了不让代码中的this指向错乱,我们可以采用硬绑定的形式

function foo(something) {
    console.log(this.a, something)
    return this.a + something
}
var obj = {
    a: 2
}
var bar = function () {
    //apply显式的修改掉了this的指向
    return foo.apply(obj, arguments)
}
var b = bar(3)
console.log(b)
// 输出结果为: 5(2+3)
setTimeout(bar(4), 100)
// 输出结果为: 6(2+4)
//以上这种形式被称为硬绑定

硬绑定的方法除了apply,常用的还有call和bind
最终总结:
1.看看是不是new去调用一个函数,如果是就指向new创建出来的对象
2.看是不是通过call、apply、bind去绑定的,如foo.call(obj),如果是就指向了括号中的对象
3.函数是否在某个上下文对象中调用?如果是的话,函数就指向那个上下文对象
如,obj.foo(),谁去调用这个函数就指向谁
4.箭头函数中没有自己的this,在它里面使用的this是外部环境的

案例联系:

function identify() {
    return this.name.toUpperCase()
}
function speak() {
    var greeting = 'hello,我是' + identify.call(this)
    console.log(greeting)
}
var p1 = {
    name: 'xiaoHong'
}
var p2 = {
    name: 'xiaoLi'
}
  • identify.call(p1)
    输出结果:xiaoHong
  • identify.call(p2)
    输出结果:xiaoLi
  • speak.call(p1)
    思路分析
    首先,speak既然call p1了,那么speak里面的this都指向p1了
    identify.call(this)就变成了identify.call(p1),
    identify也call给p1了,那么identity里的this也都变成了p1
    最终输出结果:hello,我是xiaoHong
  • speak.call(p2)
    输出结果:xiaoLi

6.call()、apply()、bind()的联系和区别

希望使用某个上下文调用该函数,请使用 .bind() ,这在事件中很有用。 如果要立即调用函数,请使用.call() 或 .apply(),并修改上下文。
举例说明
假设你的数学老师要求你创建一个库并提交。你写了一个抽象的库,它可以求出圆的面积和周长:

var mathLib = {
    pi: 3.14,
    area: function(r) {
        return this.pi * r * r;
    },
    circumference: function(r) {
        return 2 * this.pi * r;
    }
};

提交后,老师调用了它:

mathLib.area(2);
12.56

老师发现他给你要求是 pi 精确到小数点后 5 位数而你只精确到 2 位, 现在由于最后期限已过你没有机会提交库。 这里 JS的 call 函数可以帮你, 只需要调用你的代码如下:
mathLib.area.call({pi: 3.14159}, 2)
它会动态地获取新的 pi 值,结果如下:
12.56636

接下来看示例1

var name = "小王",age = 17
var obj={
  name:"小张",
  objAge:this.age,
  myFun:function(){
  console.log(this.name + "年龄" + this.age)
  }
}
obj.objAge;  //17
obj.myFun()  //小张年龄undefined

示例2

var fav = "盲僧"
function shows(){
  console.log(this.fav)
}
shows()  //盲僧

比较一下这两者this 的差别,第一个打印里面的this 指向obj,第二个全局声明的shows()函数 this 指向的是window ;
如:

var name = "小王",age = 17
var obj={
  name:"小张",
  objAge:this.age,
  myFun:function(){
  console.log(this.name + "年龄" + this.age)
  }
}
var db = {
  name:"德玛",
  age:99
}
obj.myFun.call(db);    //德玛年龄99
obj.myFun.apply(db);    //德玛年龄99
obj.myFun.bind(db)();   //德玛年龄99

以上出了bind 方法后面多了个 () 外 ,结果返回都一致!
由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行
2,对比call 、bind 、 apply 传参情况下

var name = "小王",age = 17
var obj={
  name:"小张",
  objAge:this.age,
  myFun: function (city,city2) { 
  console.log(this.name + "年龄" + this.age + "来自" + city + "去往" + city2)
  }
}
var db = {
  name:"德玛",
  age:99
}
obj.myFun.call(db,'成都','上海');     //德玛 年龄 99  来自 成都去往上海
obj.myFun.apply(db,['成都','上海']);        //德玛 年龄 99  来自 成都去往上海  
obj.myFun.bind(db,'成都','上海')();         //德玛 年龄 99  来自 成都去往上海
obj.myFun.bind(db,['成都','上海'])();   //德玛 年龄 99  来自 成都,上海去往undefined

微妙的差距!
从上面四个结果不难看出

  • call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
  • call的参数是直接放进去的,第二第三第n个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'成都', ... ,'string' );
  • apply的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['成都', ..., 'string' ]);
  • bind除了返回是函数以外,它 的参数和call 一样。
    当然,三者的参数不限定是string类型,允许是各种类型,包括函数 、 object 等等!

你可能感兴趣的:(详解this的指向)