js|this/call/apply/bind

95 阅读4分钟

一、this指向

1、函数调用模式:

函数中的this指向window

/**
 * 函数中this指向
 */
function test2() {
    console.log(this); // window 
}
test2();

2、方法调用模式:

方法中的this指向方法所在的对象(函数作为对象的方法使用)

/**
 * 方法中函数的指向
 */
var obj = {
    sayHi: function () {
        console.log(this); //obj{}这个对象
    }
}
obj.sayHi();
obj['sayHi']();

3、构造函数调用模式:

构造函数内的this指向了新创建的实例对象

/**
 * 构造方法中的函数指向
 */
var p = new Person();
function Person() {
    console.log(this); //Person{}
}

4、上下文调用模式:

call、apply、bind改变this的指向

/**
 *  call改变this指向
 */
var vm = {
    name: 'tom',
    age: 11,
    fn: function (a, b) {
        console.log(this.age);//undefined
        console.log(a + b); //NaN
    }
}
var fn2 = vm.fn;
fn2.call();// 没有参数,默认this指向window
/**
 *  call改变this指向
 */
var vm = {
    name: 'tom',
    age: 11,
    fn: function (a, b) {
        console.log(this.age); //11 
        console.log(a + b); //3 
    }
}
var fn2 = vm.fn; // 只是赋值
fn2.call(vm, 1, 2); //this指定是vm
/**
 *  apply改变this指向
 */
var vm = {
    name: 'tom',
    age: 11,
    fn: function (a, b) {
        console.log(this.age); //11 
        console.log(a + b); //3 
    }
}
var fn2 = vm.fn;
fn2.apply(vm, [1, 2]); //this指定是vm

bind可以改变函数指向,它通过构造一个新的函数

新函数和旧函数一样,但是在内存中是2份函数

var fn = function () {
    console.log(this); //[ 1, 3, 4 ]
}
var fn2 = fn.bind([1, 3, 4]);
console.log(fn === fn2); //false
fn2(); 

setTimeout中函数的指向是window,定时器中的function是window来调用的

/**
 * setTimeout中this指向
 */
function test() {
    var a = 100;
    setTimeout(function () {
        console.log(this.a); // 200
    }, 0)
}
var a = 200;
test();

二、call/apply/bind区别

相同点:

都是用来改变this的指向的

不同点:

call第一个参数是要给this的绑定值,后面是一个参数列表

apply第一个参数是要给this的绑定值,后面是一个参数数组

bind第一个参数是要给this的绑定值,后面一个是参数列表,返回的是一个函数,不是立即执行的,需要调用;而call,apply是立即执行的

如果call方法没有参数,或者参数为null或undefined或this,则等同于指向全局对象

【扩展】

常见的伪数组: arguments、jQuery对象、document.querySelectorAll的返回值

image.png

三、方法

call、apply、bind是Function.prototype原型上的方法

任何函数在任何地方都可以使用call、apply、bind,因为任何函数的原型链上都有Function.prototype原型

四、使用call和apply

1、不传参

function fn() {
console.log(123);
}
fn.call(); // 123
fn.apply(); // 123

2、传参


function sum(x, y) {
console.log(this);
console.log(x + y);
}
var arr = [2, 3];
sum.call(null, 2, 3); // 5
sum.apply(null, [2, 3]); // 5 第一个参数传入null,this指向js的全局对象,浏览器中是window

3、实际应用

var arr = [18, 29, 89, 73, 5, 10, 8, 99, 105, 52];
let max1 = Math.max.call(null, 18, 29, 89, 73, 5, 10, 8, 99, 105, 52);
console.log(max1); //105
let max2 = Math.max.apply(null, arr)
console.log(max2);// 105

4、修改this指向

/**
 * 改变this指向
 */
var dog = {
    name: '小青',
    age: 3
}
function animal(a, b) {
    console.log(a + b);
    console.log(this);
    console.log(this.name);
    console.log(this.age);
}
animal(1, 2);  // js全局对象 浏览器window undefined undefined
animal.call(dog, 1, 2);// this指向dog对象{ name: '小青', age: 3 }

5、类数组转数组

/**
 * 创建伪数组:常见的伪数组 arguments、jQuery对象、document.querySelectorAll的返回值
 */
var obj = {
    0: "卡卡西",
    1: "佐助",
    2: "鸣人",
    length: 3
};
[].push.call(obj, '小孩'); // 利用数组的方法,往伪数组后面添加一项
console.log(obj); // { '0': '卡卡西', '1': '佐助', '2': '鸣人', '3': '小孩', length: 4 }
var arr = [].slice.call(obj); // 数组的slice方法可以把类数组转换成真数组
console.log(arr); //[ '卡卡西', '佐助', '鸣人', '小孩' ]

五、bind

1、改变this指向

/**
 * bind改变this指向
 */
var fn = function () {
    console.log(this);
}
var fn2 = fn.bind([10, 20, 30]); // this固定指定值[10,20,30]
console.log(fn === fn2); // false
fn2();

2、修改定时器指向

/**
 * bind修改定时器指向
 */
var obj = {
    name: 'tom',
    age: 13,
    hi: function () {
        setTimeout(function () {
            console.log(this); //this指向obj
            console.log(this.age);
        }.bind(this), 1000)// this不在定时器内部,指向的是对象obj
    }
}
obj.hi(); // 13
/**
 * var that = this 解决定时器指向
 */
var obj = {
    name: 'tom',
    age: 13,
    hi: function () {
        var that = this;
        setTimeout(function () {
            console.log(this); //this指向window
            console.log(that.age);// that 指向obj
        }, 1000)
    }
}
obj.hi(); // 13

3、箭头函数没有this

/**
 * 箭头函数
 */
var obj = {
    name: 'yom',
    age: 12,
    hi: function () {
        setTimeout(() => { //箭头函数没有自己的this,使用的是外部函数的this 由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值,
            console.log(this); // this指向obj
            console.log(this.age);
        }, 0)
    }
}
obj.hi();// 12

参考👀

函数的四种调用模式=>分析this指向