this是属性和方法“当前”(运行时)所在的对象。this是函数调用时发生的绑定,它的值只取决于调用位置(箭头函数除外)。

函数调用的时候会产生一个执行上下文,this是对这个执行上下文的记录。

❌误区需要注意:

this不是指向函数本身;this和函数作用域无关;this和声明位置无关系,只和调用位置有关系。

名次解释:

调用栈: 到达当前执行位置调用的所有的函数。

既然this是调用时绑定的对象,我们只需要搞清楚this的绑定规则:

1.默认绑定

当函数调用时,如果函数直接调用,没有任何修饰(函数前没有任何对象),则默认绑定this到window。

如果使用严格模式,默认绑定到undefined。

所以,立即执行函数在任何位置,函数内部的this,永远指向window(严格模式下undefined)。

    <script>
// 在函数作用域中声明函数
function a() {
!function b() { // 函数前面有任何符号都能将函数声明变为表达式
console.log(this);// window
}();
!function c() { // 函数前面有任何符号都能将函数声明变为表达式
'use strict';
console.log(this); //undefined
}();
}
var obj = { a }
obj.a();
</script>

2. 隐式绑定(隐式丢失)

当函数调用时,前面有上下文对象时,隐式绑定规则将this绑定到该对象上。

如果多层嵌套,绑定到最近的上下文对象(obj1.obj2)上。

    <script>
var obj2 = {a: 30, foo: foo};
var obj1 = {a: 20, obj2}; // 注意obj2要先声明,否则报错
function foo() {
console.log(this.a); //
// this === obj2
}
obj1.obj2.foo();
</script>

js监听函数的回调函数,将this绑定到调用的DOM对象上。

    <div id="root">Click ME</div>
<script>
const rootElement = document.querySelector('#root');
rootElement.addEventListener('click', function() { // 此处严禁使用箭头函数
console.log(this); // rootElement
})
</script>

前端开发中this最容易出错的地方,就是隐式绑定丢失导致的this指向window(严格模式undefined)的问题。

隐式绑定丢失常见的几种情况:

1.赋值给变量

    <script>
var objA = {
a: 100,
foo: function() {
console.log(this.a);
}
};
objA.foo(); // 100 this === objA
var func = objA.foo; // 相当于func = function(){}
// 函数调用时,相当于函数直接调用,无任何修饰,使用默认绑定
func(); // undefined this === window
</script>

2.作为自定义函数的回调函数传参

将上下文对象的函数作为参数传入回调函数,相当于赋值给参数变量。

    <script>
var a = 'outer';
function foo() {
console.log(this.a);
}
var obj = { a: 'inner', foo }
function doFoo(fn) { // 调用的时候相当于fn=obj.foo
fn(); // 调用位置,相当于直接调用foo()
}
doFoo(obj.foo); // 'outer' this === window
</script>

这个问题可以解释,为什么React类组件中事件回调函数触发必须绑定this:

首先: React类组件中,构造函数、实例方法、静态方法都是在严格模式下运行的。

import React from 'react';
import ReactDOM from 'react-dom'; class App extends React.Component{
constructor(props) {
super(props);
}
onClick() {
console.log(this); // undefine
}
// onClick后面跟的是回调函数,相当于callback = this.onClick;
// <button onClick={callback}>Click Me</button>
// 调用的时候是直接调用callback(),所以this应该是默认绑定
// 由因为在实例方法中,是严格模式,所以是undefined
// 要想在onClick方法中使用this,需要进行this显式绑定
render() {
return (
<button onClick={this.onClick}>Click Me</button>
)
}
} ReactDOM.render(<App />, document.getElementById('root'));

3. 作为javascript一些内置函数的回调函数传参

常见的有setTimeout,数组的遍历函数如forEach, map, reduce, reduceRight, some, every,filter,flatMap等

    <script>
function foo() {
setTimeout(function() {
console.log(this); // window
}, 1000);
console.log(this);// objA
}
var objA = { foo };
objA.foo();
/*
上面是调用定时器setTimeout方法,并传入两个参数,第一个是回调函数
function setTimeout(callback, delay) {
// 等待delayms;
callback();
}
*/
</script>
    <script>
var arr = [1,2,3,4,5,6];
arr.forEach(function(item) {
console.log(this); // window
});// 相当于直接执行forEach方法
/*
上面相当于调用Array原型链上的forEach方法,传入回调函数的参数
Array.prototype.forEach = function(callback) {
callback();
}
*/
</script>

上面的三种情况都是隐式绑定丢失,导致this使用默认绑定的情况。还有一种回调函数this被修改的情况。

PS: this绑定隐式修改

这种情况一般是,有些js库中将事件处理器将回调函数绑定到DOM元素上。

如:jquery库的事件回调函数

<body>
<div id="root">Click Me 1</div>
<script>
$("#root").click(function() { // click方法的回调函数
console.log(this); // $("#root")
})
</script>
</body>

3. 显式绑定

js提供了三种显示绑定的方法apply,call,bind。另外

三种方法调用有区别,具体的可以参考三种方法的详细介绍

通过这三种方法,可以将函数this绑定到一个不相关的对象, 还可以解决隐式绑定中隐式丢失的问题。

1. 指定对象

    <script>
function foo(sth) {// sth === 3
return this.a + sth; // this.a === 2
}
var obj = {a:2};
var bar = function() {
// apply和call绑定后立即执行,所以要用外面的函数套一层
return foo.apply(obj, arguments);
// 如果是call
// return foo.call(obj, ...arguments)
};
var b = bar(3);
console.log(b); //
//还可以通过bind
// var bar = foo.bind(obj, 3);
</script>

2. React中事件处理绑定this-不传参

React类组件中,不绑定,默认this严格模式下是undefined,需要将函数内部this绑定绑定到当前组件上。

1. 使用箭头函数固定this的指向

箭头函数内部没有this(箭头函数不能使用new命令),所以箭头函数的this绑定代码块外部作用域(函数作用域或者全局作用域)的this。即定义时所在对象。

这点和普通的this是执行时所在对象不同。而且箭头函数绑定作用域后不能修改(不能通过bind等方法修改)。

// 箭头函数固定this的方法适用于setTimeout,类实例方法
class App extends React.Component{
constructor(props) {
super(props);
}
add = () => {
console.log(this); // 当前组件
     setTimeout(() => {
       console.log(this); // 当前组件
     },1000)
}
render() {
return (
<div onClick={this.add}>Click Me</div>
)
}
}

有一点需要注意: 箭头函数绑定的是代码块外部的函数或者全局作用域。对象没有作用域。

    const shape = {
q() {
console.log(this);
},
p: () => {
console.log(this);
}
}
console.log(shape.p()); // window/严格模式下undefined
console.log(shape.q()); // shape

2. 使用bind方法在构造函数中进行绑定

class App extends React.Component{
constructor(props) {
super(props);
this.add = this.add.bind(this);
}
add(e) {
console.log(this); // 当前组件
setTimeout(function(){
console.log(this); // 当前组件
}.bind(this), 1000)
}
render() {
return (
<div onClick={this.add}>Click Me</div>
)
}
}

3. React中事件处理绑定this-传参

只有传参的时候才使用这些绑定方式;

因为它每次渲染生成新的回调函数,如果作为props传参,会进行额外的重新渲染。

1. 在事件属性的回调函数中使用箭头函数

这种方式,必须显式的传递事件对象e

class App extends React.Component{
constructor(props) {
super(props);
}
add(e) {
console.log(this); // 当前组件
}
render() {
return (
<div onClick={(e) => this.add(e)}>Click Me</div>
)
}
}

2. 在事件属性的回调函数中使用bind方法

这种方式会隐式的传递事件对象e, 在其他参数之后

class App extends React.Component{
constructor(props) {
super(props);
}
add(id, e) { //第二个参数
console.log(this); // 当前组件
}
render() {
return (
<div onClick={this.add.bind(this, id}>Click Me</div>
)
}
}

3. 使用data-*属性传递参数

该方法不会在render后每次生成新函数;

  add = (e) => {
console.log(e.target.dataset.number)
}
render() {
return (
<button data-number={5} onClick={this.add}>App </button>
)
}

4. this赋值给变量传参

针对setTimeout等回调函数隐式绑定丢失的情况,除了上面的箭头箭头函数和bind方法,还有

class App extends React.Component{
constructor(props) {
super(props);
this.add = this.add.bind(this);
}
add(e) {
console.log(this); // 当前组件
const that = this; //赋值给变量,传递
setTimeout(function(){
console.log(that); // 当前组件
}, 1000)
}
render() {
return (
<div onClick={this.add}>Click Me</div>
)
}
}

5. 使用一些原生函数的自身参数

数组的很多处理方法,以回调函数作为参数,这些回调函数中this会默认是window/undefined(严格模式);

他们提供最后一个参数用于绑定内部的this。不适用于setTimeout.

class App extends React.Component{
constructor(props) {
super(props);
this.add = this.add.bind(this);
}
add(e) {
console.log(this); // 当前组件
const that = this;
([1]).forEach(function(item) {
console.log(this); // 当前组件
},that);// 第二个参数是被绑定的this对象
}
render() {
return (
<div onClick={this.add}>Click Me</div>
)
}
}

4. new 绑定

使用new命令实例化一个构造函数的时候,逻辑如下:

1)创建一个空对象

2)将对象的原型对象指向构造函数的prototype属性

3)将这个空对象绑定到this

4) 没有其他返回对象的情况下,返回this

5.优先级

new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

PS: 特殊的this取值

在class中,this除了指代类的实例对象外,在静态方法中的this有别的指向:

静态方法中,this指向当前类。

最新文章

  1. MyEclipse新建web project和navicat110_mysql_en工具
  2. iOS 7新功能例子
  3. 把电脑装成ubuntu系统了
  4. HDU 2647 Reward (拓扑排序)
  5. 关于JDK中的集合总结(一)
  6. Random Integer Generator
  7. Spring、Spring依赖注入与编码剖析Spring依赖注入的原理
  8. 提高SQL执行效率
  9. Salesforce的Auto Number
  10. 两个port贴合七夕主题,百度输入法的“情感营销”策略
  11. 自己定义定时器(Timer)
  12. 【编程技巧】java不使用第三个变量处理两个变量的交换
  13. [Swift]LeetCode949. 给定数字能组成的最大时间 | Largest Time for Given Digits
  14. 菜鸡学C语言之真心话大冒险
  15. php结合layui前端实现 多图上传
  16. 调试WebApi的一些方法
  17. vue搭配axios踩坑
  18. VC CListCtrl 第一列列宽自适应
  19. css特效-一道闪光在图片上划过
  20. leadecode 2 Add Two Numbers

热门文章

  1. wordpress 图片上传时发生了错误,请稍后再试 的解决办法
  2. 【数据结构】洛谷2019 OI春令营 - 普及组 作业
  3. 复杂链表的复制——牛客offer
  4. BZOJ2555 SubString(后缀自动机+LCT)
  5. mysql存储emoji表情报错的处理方法【更改编码为utf8mb4】
  6. (十二) web服务与javaweb结合(3)
  7. sys.dm_exec_query_stats的total_worker_time的单位是微秒还是毫秒
  8. MySQL INNER JOIN子句介绍
  9. Linux 数据库MySql 安装配置教程!
  10. POJ2945(Find the Clones)--字典树,map