博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
This in Javascript
阅读量:6214 次
发布时间:2019-06-21

本文共 7182 字,大约阅读时间需要 23 分钟。

hot3.png

1 什么是this?

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

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

(1) global context (全局)

(2) object construction (对象构造函数)

(3) object method (对象方法)

(4) simple function (简单函数)

(5) arrow function (箭头函数)

(6) event listener (事件监听)

 

2 详细说明6中使用情况

2.1  global context

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

console.log(this) // Window

 

2.2 object construction

当使用new创建新实例时,this指向创建的实例instance

function Human (age) {  this.age = age}let greg = new Human(23)let thomas = new Human(25)console.log(greg) // this.age = 23console.log(thomas) // this.age = 25

132858_DzGY_2510955.png

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() // Windowo.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 arglogThisAndArguments.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 arglogThisAndArguments.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 CALLlogThisAndArguments('First arg', 'Second arg');// -> Window {frames: Window, postMessage: ƒ, …}// -> First arg// -> Second arg// USING CALLlogThisAndArguments.call(obj, 'First arg', 'Second arg');// -> { val: 'Hello!' }// -> First arg// -> Second arg// USING APPLYlogThisAndArguments.apply(obj, ['First arg', 'Second arg']);// -> { val: 'Hello!' }// -> First arg// -> Second arg// USING BINDvar fnBound = logThisAndArguments.bind(obj, 'First arg', 'Second arg');fnBound();// -> { val: 'Hello!' }// -> First arg// -> Second arg

 

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

转载于:https://my.oschina.net/u/2510955/blog/1505780

你可能感兴趣的文章
原创《分享(Angular 和 Vue)按需加载的项目实践优化方案》
查看>>
窗口一闪而过
查看>>
迭代器 -> 固定的思路. for循环
查看>>
PowerShell定时记录操作系统行为
查看>>
selenium+python unittest实践过程之问题杂集
查看>>
ASP.NET中url传递中文的解决方案
查看>>
PHP中Trait详解及其应用
查看>>
windows 安装nodejs 和 npm
查看>>
HDU 2553 N皇后问题
查看>>
ubuntu apache2 虚拟主机服务
查看>>
组合数据类型练习,英文词频统计实例上
查看>>
ffmpeg获取视频封面图片
查看>>
sql 循环执行游标
查看>>
app-in purchase中测试出错:itunes连接出错的解决办法
查看>>
冒泡排序
查看>>
从iPod音乐库中存取音乐的源代码
查看>>
数学分析原理 定理 6.10
查看>>
豪杰的终章
查看>>
_itoa _itow _itot atoi atof atol
查看>>
内核下枚举进程(一)进程活动链
查看>>