this指向问题

this指向

this是执行上下文中的一个属性,它指向最后一次调用这个方法的对象,也就是说只有在函数被调用时,this才会被绑定。在实际开发中,this指向可以通过四种模式来判断:

  1. 函数调用模式,函数作为一个普通函数调用时,this指向全局对象window;
function testDefault() {
    console.log(this);
}
// this指向window对象
testDefault() // window
  1. 构造器模式调用,函数作为构造函数,使用new调用时,this指向这个构造函数的实例;
function Person(niName, age) {
    this.niName = niName
    this.age = age
    // this指向构造函数的实例person
    console.log(this); // Person { niName: 'limi2020', age: 25 }
}
let person = new Person('limi2020', 25)
  1. 方法模式调用,函数作为一个对象的方法调用时,this指向这个对象;
let obj = {
    niName: 'limi2020',
    say() {
        console.log(this);
    }
}
// this指向obj对象
obj.say() // { niName: 'limi2020', say: [Function: say] }
  1. call、apply、bind模式调用,这三个方法可以显式地指定函数的this。
let object = { niName: 'limi2020'}
function testCall() {
    return this
}
// this被显示指向object,以call()方法为例
console.log(testCall.call(object)); //{ niName: 'limi2020' }

四种模式的优先级:构造器调用模式 > call、apply、bind调用模式 > 方法调用模式 > 函数调用模式

call()、apply()、bind()

这三个方法都可以显式地指定函数的this,三个方法的第一个参数指定了函数体内this对象的指向。

1.call()、apply()、bind()区别
  1. call()和apply()会立即执行,bind()不会,而是返回一个函数;
  2. call()和bind()可以接收多个参数,apply()只能接收两个参数,且第二参数是一个数组或类数组;
  3. bind()参数可以分多次传入。
2.call()的实现
  1. 判断调用的对象是否为函数,因为即使定义在函数原型上,也有可能出现call等方式调用的情况;
  2. 判断传入的上下文对象是否存在,不存在,则将其设置为window对象;
  3. 将这个函数作为上下文对象的一个属性;
  4. 使用这个上下文对象调用这个函数,并将结果返回;
  5. 删除这个属性
  6. 返回结果
Function.prototype.myCall = function(context) {
    // 判断调用对象是否为函数,因为即使定义在函数原型上,也有可能出现使用call调用的情况
    if(typeof this !== 'function') {
        console.error('type error!')
    }
    // 判断传入的上下文对象是否存在,不存在,则设置为window
    context = context || window
    // 定义一个属性,避免与对象的属性发生命名冲突
    let fn = Symbol()
    // 将这个函数作为上下文对象的一个属性
    context.fn = this
    // 使用上下文对象来调用这个方法,并将结果保存
    let result = context.fn(...arguments)
    // 删除属性
    delete context.fn
    // 返回结果
    return result
}

// 测试myCall和call是否相同
function test() {
    return this
}
let obj = { niName: 'limi2020'}
console.log(test.myCall(obj)); // { niName: 'limi2020' }
console.log(test.call(obj)); // { niName: 'limi2020' }
3.apply()的实现

apply()和call()的实现相同,只是在参数处理上有些差别,apply()只有两个参数,且第二个参数为数组或类数组。

Function.prototype.myApply = function(context) {
    // 判断调用的对象是否为函数,因为即使定义在函数原型上,也有可能出现call等方式调用的情况
    if(typeof this !== 'function') {
        console.error('type errror!');
    }
    // 判断传入的上下文对象是否存在,不存在,则将其设置为window对象
    context = context || window
    // 定义一个属性,避免与对象的属性发生命名冲突
    let fn = Symbol()
    // 将这个函数作为上下文对象的一个属性
    context.fn = this
    let result = null
    // 使用上下文对象调用这个方法,并将结果返回
    if(arguments[1]) {
        // 参数处理:apply()只有两个参数,第二个参数为数组或类数组
        result = context.fn(...arguments[1])
    } else {
        result = context.fn()
    }
    // 删除属性
    delete context.fn
    // 返回结果
    return result
}
// 测试myApply和apply是否哦相同
function test() {
    return this
}
let obj = { niName: 'limi2020' }
console.log(test.myApply(obj)); // { niName: 'limi2020' }
console.log(test.apply(obj)); // { niName: 'limi2020' }
4.bind()的实现
  1. 判断调用对象是否为函数,因为即使定义在函数原型上,也有可能出现call等方式调用的情况;
  2. 保存当前函数的引用
  3. 创建一个函数, 并返回
  4. 函数内部使用apply来绑定函数调用,判断函数是否为构造函数,是就传入当前函数的this,其余都传入指定的上下文对象。
Function.prototype.myBind = function (context) {
    // 判断调用对象是否为函数,因为即使定义在函数原型上,也有可能出现call等方式调用的情况
    if (typeof this !== 'function') {
        console.error('type error!');
    }
    // 保存当前函数的引用
    let fn = this
    // 获取其余参数
    let args = [...arguments].slice(1)
    // 创建一个函数返回
    return function Fn() {
        // 函数内部调用apply来绑定函数调用,需要判断作为构造函数的情况,如果是构造函数就传入当前函数的this, 其余情况传入指定的上下文对象
        return fn.apply(this instanceof Fn ? this : context, args.concat(...arguments))
    }
}
// 测试myBind和bind是否相同
function test() {
    return this
}
let obj = { niName: 'limi' }
console.log(test.myBind(obj)()); // { niName: 'limi' }
console.log(test.bind(obj)()); // { niName: 'limi' }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值