call、apply、bind的区别

98 阅读3分钟

相同点:

  • 都改变this指向,不会修改原先函数的this指向;
  • 第一个参数都是this要指向的对象;
  • 都可以利用后续参数传参;

不同点:

  • call和bind的参数是依次传参,--对应的;apply只有两个参数,第二个参数为数组;
  • call和apply是改变后页面加载之后就立即执行,是同步代码; bind是异步代码,改变后不会立即执行,而是返回一个新的函数;
const arr = [1,2,3,4,5,6];
function fn(...b){
    let a = b.reduce((sum,item)=> sum += item , 0);
    console.log(this,b,a);
}
fn(...arr);
const obj = {
    name: 'zhangsan'
}
fn.call(obj,1,2)

call方法

let obj = {
    a: 1,
    get: function(){
        return 2
    }
}
let g = obj.get;
g.call({},1,2,3);
g.apply({},[1,2,3]);

调用父构造函数实现父构造函数内的属性和方法继承

// 父类
function Super(sex){
    this.name = ['super'];
    this.sex = sex;
}
Super.prototype.getSuper = function(){
    return this.name;
}
//子类
function Sub(sex){
    Super.call(this,sex);//继承,在sub中使用call去调用Super
}
let sub1 = new Sub('女');// 创建sub的实例sub1
sub1.name.push('sub1');//修改sub1的name属性值
let sub2 = new Sub();
sub2.name.push('sub2');
console.log(sub1.sex); // 女
//子类实例继承了父类的name属性且可通过操作修改实例自身属性
console.log(sub2.name); // ['super','sub2']
//两个子类实例都继承了父类属性但修改子类实例自身属性并不影响父类和其它子类实例的相应属性
let super1 = new Super();
console.log(super1.getSuper()); // ['super']
//父类实例拥有父类构造函数内的属性和原型上的方法,修改子类实例属性时,父类属性不受影响;

call方法调用函数不指定第一个参数(argument)this的值将会被绑定为全局对象。

var sData = 'marshall';
function display(){
    console.log(this.sData)
}
display.call(); // 'marshall' 但是在严格模式下 ,this的值为undefined

apply方法

apply使用参数数组而不是一组参数列表。
apply调用一个具有给定this值的函数,以一个数组的形式提供参数;

var arr = ['one','two'];
var element = [0,1,2];
arr.push.apply(arr,element);
console.log(arr)//['one','two',0,1,2]

使用apply和内置函数
对于一些需要循环遍历数组各项的需求,用apply

var num = [2,4,6,1,8];
var max = Math.max.apply(null,num); // 8
var min = Math.min.apply(null,num); // 1

bind

bind会创建一个新的绑定函数,这个绑定函数包装了原函数的对象,调用绑定函数通常会执行包装函数。
绑定函数内部属性:

  • 包装的函数对象
  • 在调用包装函数时始终作为this传递的值
  • 在对包装函数做任何调用时都有 优先用列表元素填充参数列表
// 原函数中的this不会被改变
this.x = 9;
var module = {
    x: 81,
    getX: function(){
        return this.x
    }
}
console.log(moudle.getX()) // 81
var retrieveX = module.getX();
console.log(retrieveX()); // 9 
// 因为函数是在全局作用域调用的

var boundGetX = retrieveX.bind(moudle);//创建一个新的函数,把this绑定到module对象
console.log(boundGetX()); // 81

bind无法改变构造函数的指向

var name = 'window';
var newThis = {
    name: 'newThis'
}
function showName(info1,info2){
    console.log(this.name,info1,info2);
}
showName('a','b');//window a b
var newShowName = showName.bind(newThis,'hellow', 1, 2)
newShowName('a','world');//newThis hello 1
console.log(new newShowName().constructor); // 输出showName函数体
// ƒ showName(info1,info2){
//     console.log(this.name,info1,info2);
// }
// new newShowName()实例化一个新的方法 this不在指向newThis

call和apply应用场景

函数之间互相调用

function add(a,b){
    console.log(a+b);
}
function sub(a,b){
    console.log(a-b);
}
add.call(sub,5,6);
add.apply(sub,[5,6]);

构造函数之间的调用

function Person(){
    this.age = 50;
    this.showAge = function(){
        console.log(this.age);
    }
}
function Son(){
    this.age = 20;
}
var father = new Person();
var small = new Son();
father.showAge.apply(small); // 20
father.showAge.call(small); // 20
small.showAge(); // 报错 showAge is not a function

多重继承

1. 找最大值最小值
var arr = [1,2,3,...n];
Math.min.apply(this,arr); // 1

2.数组合并
var arr1 = new Array('1','2','3');
var arr2 = new Array('2','3','4');
Array.prototype.push.apply(arr1,arr2);// 6 返回的是length
arr1: ['1', '2', '3', '2', '3', '4']
arr2: ['2', '3', '4']
//继续执行 Array.prototype.push.apply(arr1,arr2); //9
arr1: ['1', '2', '3', '2', '3', '4', '2', '3', '4']
arr2: ['2', '3', '4']

类数组公用数组方法

function fn(){
    return [].slice.call(arguments,0)  //   类似对数组arr.slice(0)
} 
fn(1,2,3) // [1,2,3]