面试中经常问到javascript的this,你知道多少?
来源:互联网 发布:怎么样数据共享 编辑:程序博客网 时间:2024/05/19 02:00
this是JavaScript中的一个关键字, 它的值取决于他所在的函数如何被调用. 下面是this可以获得新值的6中不同的方式:
1.this在全局范围内
2.this在对象的构造函数内
3.this在对象的方法内
4.this在一个简单的函数内
This在全局范围内
当this在任何函数外面被调用时, 也就是说在全局环境中被调用, 在浏览器中, 它默认指向Window对象.
接下来让我们看一个密切相关的环境 - this在对象的方法内.
This在对象的方法内
方法是与对象关联的函数的另一种通俗叫法, 如下所示:
(注: 这里的方法是用ES6语法来写的).
继续下一个环境.
This在一个简单的函数内
简单函数应该是你非常熟悉的函数了, 就像下面这个一样. 以相同形式编写的匿名函数也被认为是简单函数.
要弄清楚为什么, 请看下面的代码. 在这里, setTimeout函数(简单函数)中的this.speakLeet函数会延迟一会执行.
一个快速的解决办法是创建一个变量来保存对象中的this的引用. 通常, 这个变量名称叫做self或者that.
This在箭头函数中
this在箭头函数中总是跟它在箭头函数所在作用域的this一样(在它直接作用域). 所以, 如果你在对象中使用箭头函数, 箭头函数中的this总是指向这个对象本身, 而不是指向Window.
使用箭头函数, 可以用以下方式来改写speakLeet的例子:
This在事件侦听器内
在事件侦听器内, this被指向的是触发这个事件的元素:
要移除事件侦听器, 绑定事件时, 作为第二个参数传递的回调函数(上面定义的匿名函数), 需要以具名的形式定义:
通过bind来改变this的值
bind是一个方法, 它存在于每个函数(方法)中. 它允许你改变this上下文环境. 此方法接收任意数量的参数, 并返回绑定后的函数.
将原文中的实例代码修改下就能得到正确的结果了:
回到原文. 前面说过bind方法里面的第一个参数将成为绑定函数的this指向, 其他参数则作为原始函数的实参(arguments)传入.
现在, 让我们回到之前的代码: 移除事件侦听器, 并解析到底发生了什么:
this是JavaScript种的关键字. 它出现在许多的JavaScript框架中, 所以你必须知道它的作用.
在这篇文章中, 你了解了this在6种不同上下文中的指向. 你也学习了如何使用诸如bind之类的函数来改变this的指向. 此外, 你还附带了解了如何正确移除事件侦听器.
这就是你所需要知道的this的一切. 只要深刻领会了本文中的概念, 你就再也不会对this感到困惑了.
如果你有任何问题, 请在评论区留下评论.
1.this在全局范围内
2.this在对象的构造函数内
3.this在对象的方法内
4.this在一个简单的函数内
5.this在箭头函数内
6.this在一个事件侦听器内
你可能会想知道this在每个环境中是什么? 为什么有必要第一时间去改变this的值? 要回答你的问题, 让我们先看下this是如何在每个环境中改变的.This在全局范围内
当this在任何函数外面被调用时, 也就是说在全局环境中被调用, 在浏览器中, 它默认指向Window对象.
console.log(this) // Window通常, 在全局环境中, 我们很少使用this关键字, 因此对它也没那么在意. 让我们继续看下一个环境.
This在对象的构造函数内
当你使用new关键字创建一个对象的新的实例时, this关键字指向这个实例.function Human (age) { this.age = age;}let greg = new Human(22);let thomas = new Human(24);console.log(greg); // this.age = 22console.log(thomas); // this.age = 24通过上面的代码片段, 你会发现, greg是Human的一个实例. 现在, 无论何时调用greg, this都不会指向thomas. 所以, 让this指向对象的实例是完全有道理的.
接下来让我们看一个密切相关的环境 - this在对象的方法内.
This在对象的方法内
方法是与对象关联的函数的另一种通俗叫法, 如下所示:
(注: 这里的方法是用ES6语法来写的).
let o = { // A method aMethod(){}}在对象的任何方法内的this都是指向对象本身.
let o = { sayThis(){ console.log(this); }}o.sayThis() // o既然this指向对象本身, 你就可以用方法来获取对象的实例, 如下所示:
function Human(name) { return { name, getName() { return this.name; } }}const zell = new Human("Zell");const vincy = new Human("Vincy");console.log(zell.getName()); // Zell在这两个对象上下文中, 改变的this值使你获得正确的实例, 这就是面向对象的编程的基础. 当然, 这是另外一个话题了.
继续下一个环境.
This在一个简单的函数内
简单函数应该是你非常熟悉的函数了, 就像下面这个一样. 以相同形式编写的匿名函数也被认为是简单函数.
function hello() { // say hello!}在浏览器中, 简单函数里面的this总是被指向Window. 即使你在对象的方法中调用一个简单函数, 在这个简单函数里面的this也是指向Window.
function simpleFunction() { console.log(this);}const o = { sayThis() { simpleFunction(); }}simpleFunction(); // Windowo.sayThis(); // Window不幸的是, 对于初学者来说, 这些变化总是飘忽不定的. 他们总是认为简单方法中的this也是指向对象本身. 我也踩过这个坑.
要弄清楚为什么, 请看下面的代码. 在这里, setTimeout函数(简单函数)中的this.speakLeet函数会延迟一会执行.
const o = { doSomethingLater() { setTimeout(function() { this.speekLeet(); // 报错 }, 1000); }, speakLeet() { console.log(`1337 15 4W350M3`); }}o.doSomethingLater(); // 报错: Uncaught TypeError: this.speekLeet is not a function调用上面代码: o.doSomethingLater() 会报错. 报错的原因是, 在setTimeout函数中, this已经被重新指向Window了, 而Window并没有speakLeet这个方法.
一个快速的解决办法是创建一个变量来保存对象中的this的引用. 通常, 这个变量名称叫做self或者that.
const o = { doSomethingLater() { const self = this; setTimeout(function() { self.speakLeet(); }, 1000); }, speakLeet() { console.log(`1337 15 4W350M3`); }}o.doSomethingLater(); // `1337 15 4W350M3`另一种方法是使用ES6中的箭头函数, 让我们进入下一环境.
This在箭头函数中
this在箭头函数中总是跟它在箭头函数所在作用域的this一样(在它直接作用域). 所以, 如果你在对象中使用箭头函数, 箭头函数中的this总是指向这个对象本身, 而不是指向Window.
使用箭头函数, 可以用以下方式来改写speakLeet的例子:
const o = { doSomethingLater() { setTimeout(() => this.speakLeet(), 1000); }, speakLeet() { console.log(`1337 15 4W350M3`); }}o.doSomethingLater(); // `1337 15 4W350M3`第三种改变任何函数(方法)中的this值的方法是bind, call或者apply. 我们将在稍后介绍bind, 择日在介绍call和apply. 但是首先, 让我们来看看最后一种环境 - 事件侦听器.
This在事件侦听器内
在事件侦听器内, this被指向的是触发这个事件的元素:
let button = document.querySelector('button');button.addEventListener('click', function() { console.log(this); // button});当你在创建更加复杂的组件时, 你可能会发现你需要在方法内创建事件侦听器.
function LeetSpeaker(elem) { return { listenClick() { elem.addEventListener('click', function() { // Do something here }); } }}既然事件侦听器里面的this指向的是触发该事件的元素, 如果你需要在里面调用其它方法, 你需要在外层定义一个变量来保存对对象的引用:
function LeetSpeaker(elem) { return { listenClick() { const self = this; elem.addEventListener('click', function() { self.speakLeet(); }); }, speakLeet() { console.log(`1337 15 4W350M3`); } }}或者, 你也可以使用箭头函数. 如果你这么做, 那么事件侦听器内的this就不是指向触发该事件的元素了, 但是你仍然可以通过event.currentTarget来获得该元素.
function LeetSpeaker(elem) { return { listenClick() { elem.addEventListener('click', e => { console.log(e.currentTarget); // elem this.speakLeet(); }); }, speakLeet() { console.log(`1337 15 4W350M3`); } }}但是, 当移除事件侦听器的需求出现时, 这两种方法都不是很合适, 因为他们都是匿名函数.
要移除事件侦听器, 绑定事件时, 作为第二个参数传递的回调函数(上面定义的匿名函数), 需要以具名的形式定义:
function someFunction() { console.log(`do something`);// Removes the event listener. document.removeEventListener('click', someFunction);}document.addEventListener('click', someFunction);如果你需要事件侦听器里面的this指向事件侦听器所在的环境的对象, 你需要使用bind来手动创建this环境.
function LeetSpeaker(elem) { return { listenClick() { this.listener = this.speakLeet.bind(this); elem.addEventListener('click', this.listener); }, speakLeet(e) { const elem = e.currentTarget; console.log(`1337 15 4W350M3`); elem.removeEventListener(this.listener); } }}如果你还不是很了解bind, 上面的代码可能会让你感到困惑. 所以, 在我告诉你怎么回事之前, 先让我们稍微了解下bind是干什么的.
通过bind来改变this的值
bind是一个方法, 它存在于每个函数(方法)中. 它允许你改变this上下文环境. 此方法接收任意数量的参数, 并返回绑定后的函数.
const sayThis = _ => console.log(this);const boundFunc = sayThis.bind(/* arguments... */);你传入bind方法的第一个参数将成为绑定后的函数里面的this的值. 一旦你创建了一个绑定函数, 只要你愿意, 可以随时随地调用它:
const sayThis = _ => console.log(this);const boundFunc = sayThis.bind({hippy: 'hipster'});
boundFunc(); // {hippy: 'hipster'} // 译者注: 实际打印结果并不是这个, 而是Window译者注: 原文例子应该是有误的. 我们知道箭头函数里面是没有自己的this的, 而是引用外层的this, 所以也就不能通过call(), apply(), bind()这些方法来改变this的指向. 参考下面的ES6和ES5的两段代码:
// ES6 function foo() { setTimeout(() => { console.log('id: ', this.id); }, 100);}// ES5function foo() { var _this = this; setTimeout(function() { console.log('id: ', _this.id); }, 100);}上面一段代码是通过ES6编写的, 下面一段代码是通过babel转换后的ES5的代码. 转换后的ES5的代码清楚的说明, 箭头函数里面的this是引用的外部的this, 它并没有自己的this.
将原文中的实例代码修改下就能得到正确的结果了:
const sayThis = function() { console.log(this);}const boundFunc = sayThis.bind({hippy: 'hipster'});boundFunc(); // {hippy: 'hipster'}这样修改以后, 就能正确的修改this的指向了.
回到原文. 前面说过bind方法里面的第一个参数将成为绑定函数的this指向, 其他参数则作为原始函数的实参(arguments)传入.
const sayParams = (...args) => console.log(...args);const boundFunc = sayParams.bind(null, 1, 2, 3, 4, 5);boundFunc(); // 1 2 3 4 5这就是你所需要知道的bind方法.
现在, 让我们回到之前的代码: 移除事件侦听器, 并解析到底发生了什么:
function LeetSpeaker(elem) { return { listenClick() { // Binds this.speakLeet with a reference to the instance. // Sets bound function to this.listener, // so we can remove it later. this.listener = this.speakLeet.bind(this); elem.addEventListener('click', this.listener); }, speakLeet(e) { console.log(`1337 15 4W350M3`); // Gets the element so we can remove the event listener. const elem = e.currentTarget; // Removes the event listener. elem.removeEventListener('click', this.listener); } }}总结
this是JavaScript种的关键字. 它出现在许多的JavaScript框架中, 所以你必须知道它的作用.
在这篇文章中, 你了解了this在6种不同上下文中的指向. 你也学习了如何使用诸如bind之类的函数来改变this的指向. 此外, 你还附带了解了如何正确移除事件侦听器.
这就是你所需要知道的this的一切. 只要深刻领会了本文中的概念, 你就再也不会对this感到困惑了.
如果你有任何问题, 请在评论区留下评论.
阅读全文
0 0
- 面试中经常问到javascript的this,你知道多少?
- 面试中经常问到的问题
- 嵌入式软件面试中经常问到的问题~。。~
- Java面试中经常问到的算法题
- Java web中面试经常问到的问题
- 面试中经常问到的七种排序方法
- Java面试中经常问到的算法题
- struts2面试经常问到的几个问题
- j2ee面试经常问到的基础知识
- 面试经常问到的集合类
- 关于java 中面试中经常问到的static、final的常识
- js经常用到的美元符号$ 你知道多少
- 经常谈到Oracle的权限,你究竟知道多少
- Java面试中经常问到的算法题 - - JavaEye技术网站 (转载)
- 对比接口和抽象类(面试中经常问到的)
- iOS堆和栈的区别(面试经常问到)
- Java研发面试经常问到的问题。
- 面试经常问到的问题(自己记录)
- 我的词条 之 偏移地址
- JAVA Debug 调试 java application JUnit单元测试
- vm中的disk负载观察iostat的%util不科学
- 递归 (斐波那契数的简单两种应用)
- 566. Reshape the Matrix
- 面试中经常问到javascript的this,你知道多少?
- 欢迎使用CSDN-markdown编辑器
- Android遇到Error:Execution failed for task ':app:mergeDebugResources'. > Error: java.ut和java.lang.Clas
- jQuery生成二维码
- JAVA知识_12
- 属性动画
- android常用控件
- (一)JMS初识
- 10.23联考