This in Javascript

1 什么是this?

this是一个keyword, 它的值老是变换,依赖于调用它场景javascript

有6种状况,this会指向特定的值java

(1) global context (全局)数组

(2) object construction (对象构造函数)浏览器

(3) object method (对象方法)app

(4) simple function (简单函数)函数

(5) arrow function (箭头函数)post

(6) event listener (事件监听)this

 

2 详细说明6中使用状况

2.1  global context

当在任何函数以外调用this时,即global context, this指向浏览器的默认全局对象Windowspa

console.log(this) // Window

 

2.2 object construction

当使用new建立新实例时,this指向建立的实例instancecode

function Human (age) {
  this.age = age
}

let greg = new Human(23)
let thomas = new Human(25)

console.log(greg) // this.age = 23
console.log(thomas) // this.age = 25

new一个函数对象,返回被调用的函数名和建立的对象

function ConstructorExample() {
    console.log(this);
    this.value = 10;
    console.log(this);
}

new ConstructorExample();

// -> ConstructorExample {}
// -> ConstructorExample { value: 10 }

 

2.3 object method

Methods here are defined with ES6 object literal shorthand, 好比下面

let o = {
  aMethod () {}
}

在method里面的this,都指向该对象自己

let o ={
  sayThis () {
    console.log(this) // o
  }
}

o.sayThis() // o

 

2.4 simple function

最多见的函数形式,相似下面的

function hello () {
  // say hello!
}

this指向Window, 即便 simple function 在object method中,也是指向Window

function simpleFunction () {
  console.log(this)
}

const o = {
  sayThis () {
    simpleFunction()
  }
}

simpleFunction() // Window
o.sayThis() // Window

为何object method中的 simpleFunction() this没有指向o, 这也是让人迷惑的地方啊,或许下面的代码能解释一下,只能说object method里面嵌套的函数的this都从新指向了Window

const o = {
  doSomethingLater () {
    setTimeout(function() {
      this.speakLeet() // Error, this-->Window
    }, 1000)
  },
  speakLeet() {
    console.log(`1337 15 4W350M3`)
  }
}

要解决这个问题,须要在嵌套函数外将this赋值给self

const o = {
  doSomethingLater () {
    const self = this
    setTimeout(function () {
      self.speakLeet()
    }, 1000)
  },

  speakLeet () {
    console.log(`1337 15 4W350M3`)
  }
}

 

2.5 arrow function

在箭头函数中,this老是指向箭头函数所在做用域的对象

好比箭头函数在object method中,那么箭头函数中的this就指向object

const o = {
  doSomethingLater () {
    setTimeout(() => this.speakLeet(), 1000)
  },

  speakLeet () {
    console.log(`1337 15 4W350M3`)
  }
}

 

2.6 Event Listener

在事件监听函数中,this指向触发事件的元素

let button = document.querySelector('button')

button.addEventListener('click', function() {
  console.log(this) // button
})

 

若是要在监听函数中调用其余函数,须要先将this赋值给其余变量,如self 

function LeetSpeaker (elem) {
  return {
    listenClick () {
      const self = this
      elem.addEventListener('click', function () {
        self.speakLeet()
      })
    },
    speakLeet() { console.log(`1337 15 4W350M3`) }
  }
}

 

固然,使用箭头函数也能够直接使用this, 访问元素可使用e.currentTarget

function LeetSpeaker (elem) {
  return {
    listenClick () {
      elem.addEventListener('click', (e) => {
        console.log(e.currentTarget) // elem
        this.speakLeet()
      })
    },
    speakLeet () {
      console.log(`1337 15 4W350M3`)
    }
  }
}

new LeetSpeaker(document.querySelector('button')).listenClick()

 

若是想要移除监听事件,则须要在绑定事件时,第二个参数--回调函数要使用命名函数,而非匿名函数

function someFunction () {
  console.log('do something')

  // Removes the event listener.
  document.removeEventListener('click', someFunction)
}

document.addEventListener('click', someFunction)

 

须要注意的是,有些时候,上述的规则会共同做用,这是会有优先级,好比下面的例子,new和对象方法共同做用的状况,那么,new 规则主导

var obj1 = {
    value: 'hi',
    print: function() {
        console.log(this);
    },
};

new obj1.print(); // -> print {}

 

小结:

某些规则共同做用,优先级以下,从前日后,优先级越低:

(1) new 操做符

(2) bind()

(3) call(), apply()

(4) object method

(5) global object, except in strict mode

 

3 改变this的指向

什么状况下须要改变this的指向?好比想要得到类数组对象如{1:'a', 2: 'b'}的数组值,就可使用Array.slice方法,但OOP中,方法都是和对象绑定的,因此须要手动修改this的指向。

Javascript中共提供了三种方法修改this的指向, call,apply,bind

3.1 call

咱们使用func.call(param), 传递参数给this, 第一个参数绑定给this,后面的做为函数的实参

例子1:不带参数的函数

function logThis() {
    console.log(this);
}

var obj = { val: 'Hello!' };

logThis(); // -> Window {frames: Window, postMessage: ƒ, …}
logThis.call(obj); // -> { val: 'Hello!' };

 

例子2:带参数的函数

function logThisAndArguments(arg1, arg2) {
    console.log(this);
    console.log(arg1);
    console.log(arg2);
}

var obj = { val: 'Hello!' };

logThisAndArguments('First arg', 'Second arg');
// -> Window {frames: Window, postMessage: ƒ, …}
// -> First arg
// -> Second arg

logThisAndArguments.call(obj, 'First arg', 'Second arg');
// -> { val: 'Hello!' }
// -> First arg
// -> Second arg

 

call + arguments 移花接木

aruguments在Javascript中,是传递给函数的参数,一个类数组的对象

function add() {
    console.log(arguments);
}

add(4); // -> { '0': 4 }
add(4, 5); // -> { '0': 4, '1': 5 }
add(4, 5, 6); // -> { '0': 4, '1': 5, '2': 6 }

当有需求要遍历这些参数,使用Array的map, forEach等,咱们可使用call来说arguments转变为数组

Array.slice:经过this引用,返回调用数组的一份拷贝,若是Array.slice传入arguments, 就会返回一份新的数组,从arguments建立而来的

function add() {
    var args = [].slice.call(arguments);
    console.log(args);
}

add(4, 5, 6); // -> [ 4, 5, 6 ]

 

3.2 apply

apply 运行的机制和call相似,不一样的是arguments是以数组的形式传递的

function logThisAndArguments(arg1, arg2) {
    console.log(this);
    console.log(arg1);
    console.log(arg2);
}

var obj = { val: 'Hello!' };

logThisAndArguments('First arg', 'Second arg');
// -> Window {frames: Window, postMessage: ƒ, …}
// -> First arg
// -> Second arg

logThisAndArguments.apply(obj, ['First arg', 'Second arg']);
// -> { val: 'Hello!' }
// -> First arg
// -> Second arg

 

3.3 bind

bind和call, apply运行不太同样,func.bind调用后函数并不当即执行,而是当函数调用时才会触发, 传参的方式和call同样,一个一个传

function logThisAndArguments(arg1, arg2) {
    console.log(this);
    console.log(arg1);
    console.log(arg2);
}

var obj = { val: 'Hello!' };

var fnBound = logThisAndArguments.bind(obj, 'First arg', 'Second arg');

console.log(fnBound);
// -> [Function: bound logThisAndArguments]

fnBound();
// -> { val: 'Hello!' }
// -> First arg
// -> Second arg

 

function sayThis () {
  console.log(this)
}
const boundFunc = sayThis.bind({hippy: 'hipster'})
boundFunc()  // {hippy: "hipster"}

 

遇到箭头函数,可能状况就不同了

const sayThis = _ => console.log(this)
const boundFunc = sayThis.bind({hippy: 'hipster'})
boundFunc() // Window

 

传给bind的其余参数则做为实参

const sayParams = (...args) => console.log(...args)
const boundFunc = sayParams.bind(null, 1, 2, 3, 4, 5)
boundFunc() // 1 2 3 4 5

 

事件监听函数只调用一回的例子

function LeetSpeaker (elem) {
  return {
    listenClick () {
      this.listener = this.speakLeet.bind(this)
      elem.addEventListener('click', this.listener)
    },

    speakLeet(e) {
      const elem = e.currentTarget
      this.addLeetSpeak()
      elem.removeEventListener('click', this.listener)
    },

    addLeetSpeak () {
      const p = document.createElement('p')
      p.innerHTML = '1337 15 4W350M3'
      document.body.append(p)
    }
  }
}

const button = document.body.querySelector('button')
const leetSpeaker = LeetSpeaker(button)
leetSpeaker.listenClick()

 

 

综合示例:

function logThisAndArguments(arg1, arg2) {
    console.log(this);
    console.log(arg1);
    console.log(arg2);
}

var obj = { val: 'Hello!' };

// NORMAL FUNCTION CALL
logThisAndArguments('First arg', 'Second arg');
// -> Window {frames: Window, postMessage: ƒ, …}
// -> First arg
// -> Second arg

// USING CALL
logThisAndArguments.call(obj, 'First arg', 'Second arg');
// -> { val: 'Hello!' }
// -> First arg
// -> Second arg

// USING APPLY
logThisAndArguments.apply(obj, ['First arg', 'Second arg']);
// -> { val: 'Hello!' }
// -> First arg
// -> Second arg

// USING BIND
var fnBound = logThisAndArguments.bind(obj, 'First arg', 'Second arg');
fnBound();
// -> { val: 'Hello!' }
// -> First arg
// -> Second arg

 

参考资料:https://zellwk.com/blog/this/

相关文章
相关标签/搜索