Javascript this指向

this是干什么的?

在函数体内部,指代函数当前的运行环境(因为函数可以在不同的运行环境执行,所以this作用,就是在函数体内部获得当前的运行环境)

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

fn() // window 1
obj.fn() // {a: 2, fn: ƒ} 2

上述代码展示了函数fn在window和obj两种不同环境执行,产生结果也不一样。这就是this的作用效果。

  1. this对象

函数内部存在的特殊对象,在标准函数中,this引用的是把函数当成方法调用的上下文对象,这时候通常称其为this值(在网页的全局上下文中调用函数时,this指向window)

相关上下文概念可查看我的另一篇博客—Javascript 执行上下文

由上述代码可以看出函数内部的this值是不确定的,可改变的。所以想要确定this值是什么,不是看函数定义的时候,而是看它执行的时候

  1. this应用场景

1全局上下文中:

function fn1() {
    console.log(this)
}
fn1() //window

也叫默认绑定,就是函数调用时无任何调用前缀的情景。需要注意的是,严格模式下,指向undefined

"use strict";
function fn1() {
  console.log(this)
}
fn1() //undefined

2 箭头函数:

var a = 1;
var fn = () => {
  console.log(this)
  console.log(this.a);
}
var obj = {
  fn: fn,
  a: 2,
};
fn() // window 1
obj.fn() // window 1

箭头函数是ES6新增函数,与标准函数相比,它没有自己的this对象,因此它会向外层中寻找,上述代码,fn函数的外层就是全局,因此this指向window

3 作为对象方法被调用:

    const user = {
      name: "khkkm",
      sayHi() {
        console.log(this)
      }
    }
    user.sayHi() //{ name: 'khkkm', sayHi: [Function: sayHi] }

也叫隐式绑定,函数前面存在调用它的对象。this指向user对象

4 new关键字

class People {
  constructor(name) {
    this.name = name
    this.age = 10
  }
  sayHi() {
    console.log(this)
  }
}
const khkkm = new People("khkkm")
khkkm.sayHi() //People { name: 'khkkm', age: 10 }

通过new关键字来创建的实例,this指向当前实例本身

5 call、apply、bind

三者共同点:都是函数对象的方法且都可以改变this指向。

apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次。

var name = '王五'
var person = {
  name: '张三',
  say: function (age, sex) {
    console.log(this.name, age, sex)
  }
}
var person2 = {
  name: '李四'
}
person.say(20, '男') //张三 20 男
person.say.apply(person2, [17, '女']) //李四 17 女
person.say.apply(null, [21, '男']) //王五 21 男

call方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。

var name = '王五'
var person = {
  name: '张三',
  say: function (age, sex) {
    console.log(this.name, age, sex)
  }
}
var person2 = {
  name: '李四'
}
person.say(20, '男') //张三 20 男
person.say.call(person2, 17, '女') //李四 17 女
person.say.call(null, 21, '男') //王五 21 男

bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。

var name = '王五'
var person = {
  name: '张三',
  say: function (age, sex) {
    console.log(this.name, age, sex)
  }
}
var person2 = {
  name: '李四'
}
person.say(20, '男') //张三 20 男
person.say.bind(person2, 17)('女') //李四 17 女
person.say.bind(null, 21)('男') //王五 21 男

bind应注意:连续使用bind,this指向第一次使用bind的那个对象
function fn() {
  console.log(this)
}
fn.bind({name:'张三'}).bind({name:'李四'})() // {name: '张三'}

也叫显式绑定,通过call、apply以及bind方法改变this的行为

总结:以上讲了五种this场景,分别是默认绑定、箭头函数绑定、隐式绑定、new绑定、显示绑定。还有在分析this指向时要始终记住一句话:this指向不是看函数定义的时候,而是看它执行的时候

特殊:定时器中的this->window,拿setTimeout()举例

const zhangsan = {
  name: "张三",
  sayHi() {
    console.log(this)
  },
  wait() {
    setTimeout(function () {
      console.log(this)
    })
  }
}
zhangsan.sayHi() //对象zhangsan
zhangsan.wait()  //window
原因:超时调用的代码都是在全局作用域中执行的(相当于默认绑定),因此函数中的this的值在非严格模式下指向window对象
如果想让它指回对象zhangsan,可以这么做
const zhangsan = {
  name: "张三",
  sayHi() {
    console.log(this)
  },
  wait() {
    setTimeout(() => {
      console.log(this)
    })
  }
}
zhangsan.wait()  //对象zhangsan
原因:箭头函数无自己的this,会向外层找,它的this指向看wait函数的,因为外层是wait,然后wait是zhangsan对象调用的,所以指向zhangsan对象
  1. 优先级

上述5中this指向情况,其实是有优先级的:

箭头函数绑定>new绑定>显示绑定>隐式绑定>默认绑定。其中显示绑定bind > apply和call

最后练习一下吧!

function fn(a) {
  this.b = a;
  console.log(this);
}
fn(7); 
console.log(b); 
分析:很明显是默认绑定,this指向window,将参数a赋值给了window下的全局变量b
答案:window 7
function fn(num) {
  this.number = num
}
fn.number = 0
fn(1)
console.log(number)
console.log(fn.number)
分析:还是默认绑定,fn中this指向window,执行函数体,给window添加了全局变量number赋值为1,行4给fn添加了number变量赋值为0
答案:1 0
var obj = {
  name: "小明",
  objInner: {
    name: "小红",
    sayName: function () {
      console.log(this);
      console.log(this.name);
    }
  }
}
obj.objInner.sayName()
分析:是最内层的objInner调用的函数,属于隐式绑定,this指向调用它的对象
答案:{name: '小红', sayName: ƒ} 小红
var obj = {
  a: 1,
  c: 2,
  say: function (a) {
    console.log("this1: " + this);
    var sayA = function (a) {
      console.log("this2: " + this);
      this.a = a;
    };
    function sayC() {
      console.log("this3: " + this);
    }
    sayA(a);
    sayC()
  }
}
obj.say(3);
console.log(obj.a + "  " + obj.c);
console.log(window.a + "  " + window.c);
分析:行17属于隐式调用,this指向obj,行13 14都属于默认调用,this指向window
答案:this1: obj this2:window this3:window 1 2 3 undefined
obj = {
  func() {
    const arrowFunc = () => {
      console.log(this._name)
    }
    return arrowFunc
  },
  _name: "obj",
}
obj.func()()
func = obj.func
func()()
obj.func.bind({ _name: "newObj" })()()
obj.func.bind()()()
obj.func.bind({ _name: "bindObj" }).apply({ _name: "applyObj" })()
分析:行10在执行函数arrowFunc,箭头函数无this对象,向外层中找,this指向obj
行12也在执行函数arrowFunc,但是属于默认调用,指向全局window
行13 14都是改变this指向,前者改为对象{ _name: "newObj" },后者改为window
行15优先级bind > apply,this指向{ _name: "bindObj" }
答案:obj undefined newObj undefined bindObj
function fn() {
  console.log(this)
}
fn.call({})
fn2 = () => {
  console.log(this)
}
fn2.call({})
分析:行4输出{},call改变了this指向
行8输出window,优先级箭头函数绑定 > 显示绑定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值