Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

javascript中this指向的问题 #6

Open
hrpc opened this issue Apr 9, 2018 · 0 comments
Open

javascript中this指向的问题 #6

hrpc opened this issue Apr 9, 2018 · 0 comments

Comments

@hrpc
Copy link
Owner

hrpc commented Apr 9, 2018

宗旨:函数的调用方式决定了this的指向

JS中普通函数的调用方式有三种,绑定HTML特性,直接调用,方法调用,构造函数调用,除此之外还有一些别的调用方式,如bind,call,apply绑定对象调用,以及箭头函数调用。以下就来分析各个调用方式下this的指向问题。

  • 绑定HTML特性/绑定事件处理程序
<script>
  function showMessage(event) {
    console.log(event)
  }
</script>
<input type="button" value="click me" onclick="showMessage(this)">

此时this指向的就是事件目标元素

const button = docuemnt.getElementById('button')
button.onclick = function() {
  this // this指向button元素,因为事件处理程序是在button元素的作用域中运行
}

button.addEventListener('click', function() {
  this // this指向button元素,因为事件处理程序是在button元素的作用域中运行
}, false)
  • 直接调用
    就是通过函数名(...)这种方式调用,这时候函数内部的this指向全局对象,在浏览器环境中指向window,在node环境中指向global
    注意:在严格模式下指向undefined

举一个栗子:

const _global = type window === "undefined"  ? global : window;
function fn() {
  console.log(this === _global)
}
fn() // true

还需要注意的一点是,直接调用并不是指在全局作用域下的调用,在任何作用域下,直接通过函数名(...)来调用函数的方式,都称为直接调用,比如以下代码

(function() {
  function fn() {
    console.log(this === window)
  }
  // 非全局作用域下调用
  fn() // true
})()

bind()对直接调用的影响

bind()的作用主要是将当前函数与目标对象绑定,并返回一个新的函数,这个新的函数无论怎么调用,this都指向绑定的目标对象。比如以下代码:

const obj = {}
function fn() {
  console.log(this === window)
}
const fnObj = fn.bind(obj)
fn() // true
fnObj() // false
  • 方法调用

方法调用是指通过对象来调用其对象方法,它是对象.方法函数(...)这样的调用形式,这种情况下,this指向调用方法的对象。同时,也要注意bind对方法调用的影响。

const obj = {
  // 第一种方式,定义对象的时候,定义其方法
  test: function() {
    console.log(this === obj) 
  }
}
obj.test() // true

// 第二种方式,定义好对象之后为其附加一个方法,函数表达式
obj.test2 = function() {
  console.log(this === obj) 
}
obj.test2() // true

// 第三种方式,定义好对象之后为其附加一个方法
function fn() {
  console.log(this === obj)
}
obj.test3 = fn

obj.test3() // true

同时也要注意bind的影响

const obj = {}
obj.test = (function() {
  console.log(this === obj)
}).bind({})
obj.test() // false

再次注意,函数的this指向和定义无关,受调用方式的影响。

方法中this指向全局的情况
方法中的this指向全局对象,如果不是因为bind绑定this,那么一定是因为不是用的方法的调用方式。比如。

const obj = {
  test: function() {
    console.log(this === obj)
  }
}
const outer = obj.test
outer() // false

解决这个问题可以使用bind

const outer = obj.test.bind(obj)
outer() // true

new调用

在ES6之前,每个函数都可以作为构造函数调用,通过new调用来生成新的对象。

使用new调用一个构造函数,会创建一个新的对象,而其中的this就指向这个新的对象。

const data = 'Hi'
function Words(data) {
  this.data = data
}
const words1 = new Words('Hello World!')
words.data // Hello World
data // Hi

const words2 = new Words('Hello World')
words1 === words2 // false

实例1

function Person(age) {
  this.age = age
  setTimeout(function() {
    console.log(this.age)
  }, 1000)
}
const p = new Person(14) // undefined

此时,setTimeout的回调函数就是上述情况中的直接调用。所以this在非严格模式下指向window而非Person实例对象。

要是我就是想setTimeout指向person实例对象呢。三种方法
方法1:
提前将this保存起来

function Person(age) {
  this.age = age
  let _this = this
  setTimeout(function() {
    console.log(_this.age)
  }, 1000)
}

方法2:
bind()绑定this

function Person(age) {
  this.age = age
  setTimeout((function() {
    console.log(this.age)
  }).bind(this), 1000)
}

方法3:
箭头函数

function Person(age) {
  this.age = age
  setTimeout(() => {
    console.log(this.age)
  })
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant