this的情况(函数和对象中的this指向谁)

this是什么

首先 this:函数的执行主体,也就是谁去执行这个函数。和作用域是两个不同的概念。
作用域scope是函数执行的上下文。举个例子:我去米其林三星吃饭。我去吃饭,吃饭的人是我(函数的执行主体),去哪里吃饭?米其林三星(函数作用域),这就是函数作用域和函数执行主体的区别,这两个概念一定不能混淆。
函数执行主体this,有以下几种情况:
1.事件绑定
2.普通函数执行
3.构造函数执行
4.箭头函数执行
5.基于call/apply/bind强行改变this
现在逐一讨论这几种情况。

1.事件绑定

不管是DOM0还是DOM2级事件绑定,给元素E的某个事件行为绑定方法,当事件触发方法执行,那么方法中的this就是当前元素E本身。
特殊情况:IE6-8基于attachEvent实现DOM2级事件绑定,方法中的this不是当前元素本身,大部分时候都是window。如果基于call/apply/bind强行改变this了,应该以强制改变的为主。

2.普通函数执行

函数执行,看函数前面有没有“点”,如果有点,点前面是谁,this就是谁,如果没有点,那么this就是window。(js严格模式下,是undefined)。
举个栗子

obj.fn()  //this是obj
fn()  //this是window
func.prototype.fn()  //this是func.prototype

自执行函数执行,里面this是window/undefined(严格模式)
回调函数中的this,如果没有强制改变,一般都是window/undefined(严格模式)

3.构造函数执行

构造函数中的this指向实例对象

function Fn() {
    // 创建一个实例对象
    // ---- 也会像普通函数执行一样,让其执行 「THIS指向实例对象」
    // 返回值没有或者是基本值,则返回的是实例对象...
}
let f1 = new Fn; 

4.箭头函数执行

箭头函数没有自己的this,使用的this是来自所处上下文的。
在没有箭头函数之前,是这样处理this问题的:let self = this;

let obj = {
    n: 0,
    fn() {
        let self = this;
        setTimeout(function() {
            console.log(this); //window
            console.log(self); //obj
            self.n++;
        }, 500)
    }
}
obj.fn()

使用箭头函数,不用考虑this的问题

let obj = {
    n: 0,
    fn() {
        setTimeout(() => {
            console.log(this); //obj
            this.n++;
            console.log(this.n)
        }, 500)
    }
}
obj.fn()

5.基于call/apply/bind强行改变this

在这里插入图片描述
首先看一个例子,怎么让obj和fn有关联呢?

① call/apply

fn.call(context)
机制:fn实例基于原型链去Function的prototype上找call方法,把call方法执行。
call方法首先把fn执行,然后把fn中的this改成第一个参数context,也就是obj。一言以蔽之:把fn执行,把fn中的this改成obj,并且把除了obj之外的参数依次传递给fn

在这里插入图片描述
fn.call(undefined); 在非严格模式下,context不传递或者传递null/undefined,则this都改为window;严格模式下,不传是undefined,否则传递谁,this就改为谁;
fn.call(10, 20); //this->Number{10} x->20 y->undefined
下面是一个mdn的例子。
在这里插入图片描述 call和apply比较

  1. fn.apply(obj, [10, 20]); //this->obj x->10 y->20 apply和call的最大区别,就是在给fn传递参数的时候,apply需要把所有需要传递的参数信息放在一个数组中,而call是一个个的传递进来即可,但是不论那种办法,最后的结果都是,都是把这些参数一个个的传递给fn

  2. call的性能要比apply好一些「一丢丢」:尤其是传递三个及以上参数的时候。

  3. apply有些有意思的应用

// 获取数据中的最大值
 let arr = [10, 13, 24, 15, 26, 34, 11];
//方法1
arr.sort((a, b) => b - a);
console.log(`数组中的最大值是:${arr[0]}`) 
//方法2
console.log(Math.max(arr)); //NaN Math是对象的方法,数组没法直接用
console.log(Math.max(10, 13, 24, 15, 26, 34, 11)); //34
console.log(Math.max(...arr)); //34
//方法3
 let max = Math.max.apply(Math, arr); //利用apply传递给方法的参数是基于数组完成的
console.log(`数组中的最大值是:${max}`);
//其他求数组最大值的方法,比如假设法,假设max是最大值
 let max = 0;
arr.forEach(item => {
    item > max ? max = item : null;
});
console.log(`数组中的最大值是:${max}`);
//or
let max = eval(`Math.max(${arr})`);
//`Math.max(${arr})`是把arr变成字符串,也就是"10, 13, 24, 15, 26, 34, 11",然后用eval把`Math.max(${arr})`变成真正的表达式执行。

console.log(`数组中的最大值是:${max}`);

总结:call/apply/bind都是立即执行函数,并且改变函数中的THIS,再并且给函数传递参数信息

② bind

bind并不会把函数立即执行,它是预先处理函数中的THIS和参数的。
先弄清楚事件绑定的一个细节。

document.body.onclick = fn; //->把fn方法本身作为值绑定给BODY的点击事件,当触发BODY的点击操作,浏览器会帮助我们把fn函数执行「并且传递一个事件对象」,方法中的THIS->BODY  =>this:body x:MouseEvent  y:undefined
document.body.onclick = fn(); //->立即把fn执行,把其执行的返回结果当作值,赋值给事件绑定,事件触发执行的是返回结果

bind用法和原理

// 需求:点击BODY的时候,把FN执行,并且让THIS->OBJ,而且传递10/20给X/Y
document.body.onclick = fn.call(obj, 10, 20); //这样处理不行:因为call/apply都是把函数立即执行的,还没有等到点击的时候,函数都执行完了
document.body.onclick = fn.bind(obj, 10, 20);
//fn.bind原理就是返回一个匿名函数,把匿名函数赋给 document.body.onclick,当点击的时候,执行这个匿名函数,在这个匿名函数里面使用call改变this指向。
document.body.onclick = function (ev) {
    // 创建一个匿名函数,绑定给点击事件,触发BODY点击行为的时候,执行的是匿名函数
    //   + this->body
    //   + ev->传递的MouseEvent事件对象
    // 在匿名函数中,我们自己执行fn即可,接下来想改啥改啥
    fn.call(obj, 10, 20, ev);
};//实现需求的另外一种方式(不使用bind)
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值