JS this指向问题

更新日期: 2019-09-23阅读: 1.5k标签: this

this问题,是很多前端同学初学JS的拦路虎,甚至一些两三年工作经验的同学对this的理解还是模棱两可或是有一些误解,一个常见的误区就是:this指向函数本身。本文尝试总结了一下this的使用,并在最后总结了this指向的确定规则。

其实this很简单,你早该熟悉它了!


一、普通函数:谁调用指向谁

这是确定this指向最普遍的规则,普通函数是指由构造函数Function创建或者使用关键字function声明定义的函数。通常情况下,普通函数的this并不天生指向其定义所在的对象,而是指向它的调用者。
而如果没有指定调用者,严格模式下this会是undefined,非严格模式下则是指向全局对象(Node环境中是global,浏览器环境中则是window)。
且看示例,obj2和obj共享了func函数:

let obj = {
  a: 'obj',
  func () {
    console.log(this.a)
  }
}

let obj2 = {
  a: 'obj2',
  func: obj.func
}

obj.func() // this指向调用者obj,输出:obj1
obj2.func() // this指向调用者obj2,输出:obj2

let func = obj.func
window.a = 'window'
func() // 未指定调用者,this指向全局对象,输出:window

示例中,this指向了给定的调用者或者全局对象window。如果是严格模式,最后的代码会报错,因为this指向了undefined:

"use strict"
// 省略函数定义
let func = obj.func
window.a = 'window'
func() // 报错:Uncaught TypeError: Cannot read property 'a' of undefined


二、箭头函数中的this

箭头函数的this指向,和箭头函数定义所在上下文的this相同。对于普通函数,this在函数调用时才确定;而对于箭头函数,this在箭头函数定义时就已经确定了,并且不能再被修改。

且看示例:

let obj = {
  A () {
    return () => {
      return this
    }
  },
  B () {
    return function () {
      return this
    }
  }
}

let func = obj.A()
console.log(func() === obj) // true

func = obj.B()
console.log(func() === obj) // false
console.log(func() === window) // true

例子中,obj的A/B函数都是返回一个内部定义的函数,A、B内部定义的函数函数返回值都是this,只是在A中,内部函数是箭头函数,而B中则是普通函数。将A、B函数创建的内部函数分别赋值给变量func进行调用,未指定调用者,可以发现箭头函数的返回值this能够指向obj,而普通函数的this则因为未指定调用者而指向了全局对象。

总而言之一句话,箭头函数this在定义时就已确定,指向此时外层函数的this,如果没有外层函数则指向全局对象。严格模式下箭头函数没有外层函数的情况this的指向会不会不同呢?并不会,依然指向全局对象而不是undefined,这点需要稍微注意:

"use strict"
let func = () => {
  return this
}
let func2 = function () {
  return this
}
console.log(func() === window) // true
console.log(func2() === window) // false
console.log(func2() === undefined) // true


三、构造函数中的this

JS里的普通函数可以使用new操作符来创建一个对象,此时该函数就是一个构造函数,箭头函数不能作为构造函数。执行new操作符,其实JS内部完成了以下事情:

创建一个空的简单JavaScript对象(即{});将构造函数的prototype绑定为新对象的原型对象 ;将步骤1新创建的对象作为this的上下文并执行函数 ;如果该函数没有返回对象,则返回this。
"use strict"
function A () {
  this.a = 1
  this.func = () => {
    return this
  }
}

let obj = new A()
console.log(obj.a) // 1
console.log(obj.func() === obj) // true


四、使用apply和call动态改变函数this的指向

使用apply可以在调用函数时改变this的指向,当然,箭头的this已经不可改变,所以不能通过该方法修改this指向。apply是函数原型上的方法,所以通过function创建的函数都有这个属性,函数的apply属性也是一个函数,它接收两个参数,第一个参数为指定的this对象,第二个参数为函数的参数列表:

func.apply(thisArg, [argsArray])
function dir (x, y) {
  this.x = x
  this.y = y
}

let obj = {
  a: 1,
  b: 2
}
dir.apply(obj, [3, 4])

console.log(JSON.stringify(obj)) // {"a":1,"b":2,"x":3,"y":4}

可以看到,通过apply函数指定了函数dir调用的this对象为obj,对this对象的修改就是对obj的修改。call函数和apply的作用都是更改this的指向,只是使用上稍微不同,apply方法中,第二个参数为一个数组,原函数所有的参数都放入该数组中,而call函数接收多个参数,第二个及之后的所有参数都被作为参数来调用原函数,对于上例,改为call方法调用如下:

dir.call(obj, 3, 4)
console.log(JSON.stringify(obj)) // {"a":1,"b":2,"x":3,"y":4}

call和apply的第一个参数用于指定this对象。注意非严格模式下,如果该参数为null或undefined,则会指向全局对象,严格模式下则this依然指向给定的null或undefined:

let obj = {
  test () {
    return this === window
	}
}
console.log(obj.test()) // false
console.log(obj.test.call(undefined)) // 非严格模式输出:true,严格模式输出:false

对箭头函数使用call或者apply将不能指定this,因为箭头函数的this在定义时已经确定:

let dir = (x, y) => {
  this.x = x
  this.y = y
}

let obj = {
  a: 1,
  b: 2
}
dir.apply(obj, [3, 4])

console.log(JSON.stringify(obj)) // {"a":1,"b":2,"x":3,"y":4}
console.log(window.x, window.y) // 3, 4

dir该为箭头函数后,定义时this指向window,通过apply调用时,对this的操作还是在window上生效。


五、使用bind创建新函数并绑定this

bind() 方法会创建一个新函数,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。bind方法有以下特点:

新生成一个函数,包装了原函数,这意味着原函数的功能不被影响,但是调用新函数会调用原函数对箭头函数无效新生成的函数绑定了固定的this,再次bind生成的函数不会重新绑定this,很类似箭头函数,不过二者存在很大不同。非严格模式指定this为undefined,结果会指向全局对象bind函数的第二个以及之后的参数,会作为原函数的参数并占用参数位置,绑定函数接收的参数会拼接在后面一并传给原函数。
let test = function (b, c) {
  console.log(this.a, b, c)
}
let obj = { a: 'obj1-a' }
let obj2 = { a: 'obj2-a' }

let bindFunc = test.bind(obj, 'b')
bindFunc('c') // obj1-a, b, c

bindFunc.call(obj2, 'c') // 指定this无效,输出:obj1-a, b, c

test.call(obj2, 'b', 'c') // 原函数不被影响,输出:obj2-a, b, c

let bindFunc2 = bindFunc.bind(obj2, 'c-rebind') 
bindFunc2() // 不能再次重新绑定,输出:obj1-a, b, c-rebind

let bindFunc3 = test.bind(undefined, 'b')
window.a = 'window-a'
bindFunc3('c') // 非严格模式输出window-a, b, c

以上是bind函数的常规用法,bind函数生成的函数与箭头函数不同,它仍然可以用于new操作符,不过这种情况下,bind的this对象会被忽略,this还是指向新生成的对象:

function A (b) {
  this.a = 'a'
  this.b = 'b'
}

let obj = {
  a: 'obj-a'
}

let B = A.bind(obj)

let newObj = new B()
console.log(newObj.a, obj.a) // a, obj-a


六、总结

以上就是this指向的大部分内容,在这里对this指向的规则做一个简单总结:

绝大多数情况下,非箭头函数内部的this由调用者确定,即谁调用指向谁箭头函数的this取决于函数定义所在的上下文中this,即函数定义外部this是什么,箭头函数内部的this就是什么,相当于固化了当前执行环境中的this,注意不是函数定义所在的对象!构造函数中的this指向新创建的对象apply/call方式调用的函数,其this可以在调用时指定bind函数可以将根据一个函数生成一个新的函数,并且该函数的this绑定到指定的对象上

其实最基本的规则就是第1条,其它的规则则是补充一些特殊情况下this的指向和使用方法。

来自:http://wintc.top/article/28

链接: https://www.fly63.com/article/detial/7017

解读Javascript中的this机制,别再为了 this 发愁了

JavaScript中有很多令人困惑的地方,或者叫做机制。但是,就是这些东西让JavaScript显得那么美好而与众不同。比方说函数也是对象、闭包、原型链继承等等,而这其中就包括颇让人费解的this机制。

js中this的详细解释,JavaScript中this绑定规则【你不知道的JavaScript】

this的绑定过程之前的调用栈 和 调用位置,this绑定规则:1、默认模式,2、隐式绑定,3、显式绑定,4、new绑定

JavaScript中this的运行机制及爬坑指南

在 JavaScript 中,this 这个特殊的变量是相对比较复杂的,因为 this 不仅仅用在面向对象环境中,在其他任何地方也是可用的。 本篇博文中会解释 this 是如何工作的以及使用中可能导致问题的地方,最后奉上最佳实践。

JS中this的指向问题(全)

this关键字在js中的指向问题不管是工作还是面试中都会经常遇到,所以在此对它进行一下总结:全局作用域中、闭包中指window、函数调用模式:谁调用就指谁、构造函数中,this指实例对象、apply/call改变this的指向、bind改变this指向等

彻底淘汰并消除JavaScript中的this

如果这很难明白,为什么我们不停止使用它呢?认真的思考一下?如果你读过 将90%的垃圾扔进垃圾桶后,我如何重新发现对JavaScript的爱, 当我说扔掉它时,你不会感到惊讶,this被丢弃了

JavaScript this常见指向问题

this的用法:直接在函数中使用 谁调用这个函数this就指向谁 ,对象中使用, 一般情况下指向该对象 ,在构造函数中使用

JavaScript函数中this的四种绑定策略

this的四种绑定策略:默认绑定、隐式绑定、显示绑定、new绑定。首先要明确的是一般情况下,this不是函数被定义时绑定,而是函数被调用时被绑定 ,那么函数中的this有四种绑定方式:

改变this的指向

this是Javascript语言的一个关键字。它代表函数运行时,自动生成的一个内部对象.this永远指向函数的调用者。随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。

js手写实现apply,call,bind

apply和call的区别就是传的参数形式不一样。call是一个一个的传,apply可以将参数以数组的形式传进去。而bind是传入第二个和后面的参数,且绑定this,返回一个转化后的函数。

this指向问题的经典场景

以函数形式调用,this指向window;以方法形式调用,this指向调用方法的那个对象;构造函数调用,this指向实例的对象;使用window对象的方法使,指向window;多重场景改变this指向

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!