文档章节

This in Javascript

p
 pengqinmm
发布于 2017/08/10 13:00
字数 1461
阅读 20
收藏 1

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 = 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/

© 著作权归作者所有

p
粉丝 1
博文 62
码字总数 34998
作品 0
西安
程序员
私信 提问
分享51本关于JavaScript方面的学习书籍(免费下载)

分享51本关于JavaScript方面的学习书籍(免费下载) 1、JavaScript面向对象15分钟教程 2、原型、作用域、闭包的完整解释 3、Javascript面向对象特性实现(封装、继承、接口) 4、JavaScript面向...

邓剑彬
2012/12/02
1K
12
JavaScript 开发者必备的7个资源

JavaScript 最近几年已经由一个二流选手变成使用最广的函数式编程语言。越来越多的资源投入到这个语言的开发中。 我最近做了很多JS开发,有前台的,也有在后台使用 Node.js 的。我想在这里分...

虫虫
2012/03/17
8.2K
5
学习Javascript的8张思维导图

分别归类为:  javascript变量  javascript运算符  javascript数组  javascript流程语句  javascript字符串函数  javascript函数基础  javascript基础DOM操作  javascript正则表达式...

thinkyoung
2014/09/23
0
0
React Native iOS混合开发实战教程

在做RN开发的时候通常离不了JS 和Native之间的通信,比如:初始化RN时Native向JS传递数据,JS调用Native的相册选择图片,JS调用Native的模块进行一些复杂的计算,Native将一些数据(GPS信息,...

JiaPengHui
2018/09/15
0
0
easyui中jquery.min.js引入要放在其他js之前

今天刚刚学习easyui 在引入easyui的js时,如果把自己的js放在easyui的js之前的话,easyui就不能成功引入例如: <script type="text/javascript" src="js/login.js"></script> //自己的js <s......

路人丁语
2015/08/13
0
2

没有更多内容

加载失败,请刷新页面

加载更多

Visual Paradigm 教程[UML]:如何绘制封装图?(上)

下载Visual Paradigm最新试用版 查看本教程视频文件 在项目开始时,您只有有限数量的图表,一切都简单而美观。然而,当时间过得匆匆时,已经创建了越来越多的图表,并且它们开始变得无法管理...

xiaochuachua
39分钟前
2
0
Pure-ftpd服务安装及虚拟用户设置

安装 sudo apt-get install pure-ftpd 虚拟用户设置 首先,可以在系统中添加相应的用户和组,如用户ftpuser 和组ftpgroup ,专门用来管理ftp服务。也可以是能登录系统的用户,但最好是不能登...

Gm_ning
40分钟前
1
0
一位面试了阿里,滴滴,网易,蚂蚁金服,最终有幸去了网易的Java程序员【面试题分享】

前言 15年毕业到现在也近三年了,最近面试了阿里集团(菜鸟网络,蚂蚁金服),网易,滴滴,点我达,最终收到点我达,网易offer,蚂蚁金服二面挂掉,菜鸟网络一个月了还在流程中... 最终有幸去...

Java领航员
42分钟前
3
0
“大团队”和“敏捷开发”,谁说不可兼得?

阿里妹导读:当小团队的产出跟不上业务需要,团队就面临规模化的问题。从1个团队到3个团队,仍可以通过简单的团队沟通保持高效协作。当产品复杂到需要5个以上团队同时开发时,我们需要一定的...

阿里云云栖社区
46分钟前
1
0
基于虹软人脸识别Demo android人脸识别

参加一个比赛,指定用虹软的人脸识别功能,奈何虹软人脸识别要自己建人脸库,不然就只能离线用,总不能装个样子,简单看了下虹软Demo,下面决定用这种简单方法实现在线人脸识别: Android端(...

是哇兴哥棒棒哒
51分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部