javascript call() apply() bind() 的区别 原型链和原型

call()

  • 表示立即执行函数;
  • 如果有一个参数,且该参数为对象类型,则表示将函数中 this 的指向改变为这里带入的参数;
  • 函数中如果没有this,call就没有任何意义了,和普通的执行函数概念一样;
  • call() 在执行函数时,函数的参数从第二位开始依次写入;
  • 如果使用call() 或者apply() ,第一个参数为空或者是null,就意味着将函数中this 重定向到 window;

语法:方法名.call(对象)

function fn(_a,_b){
   this.a=_a;
   this.b=_b;
}
fn();//等同于
fn.call();//立即执行函数,this指向window

let obj={};
fn.call(obj);//立即执行函数,且this指向obj

fn(3,4);//等同于
fn.call(obj,3,4);

apply()

  • apply() 和 call() 一样都是改变this的指向为第一个参数对象;
  • apply() 的参数只有2个,第一个参数为函数中 this 的指向,第二个参数是一个数组,这个数组就是函数的参数;
  • 如果使用call() 或者apply() ,第一个参数为空或者是null,就意味着将函数中this 重定向到 window;
function fn(_a,_b){
   this.a=_a;
   this.b=_b;
}
fn();//等同于
fn.call();//立即执行函数,this指向window

let obj={};
fn.apply(obj);//立即执行函数,且this指向obj

fn(3,4);//等同于
fn.apply(obj,[3,4]);

案例:

var obj={
    abc:function(_a,_b){
       this.a=_a;
       this.b=_b;
    }
}

obj.abc(3,5);//obj对象的a属性变为3,b属性变为5
var o1={};
obj.abc.call(o1,3,5);//将abc函数中的this指向o1
console.log(obj);//{a: 3, b: 5, abc: fn()}
console.log(o1);//{a: 3, b: 5}

如果使用call() 或者apply() ,第一个参数是null,就意味着将函数中this重定向到window;

var obj={
    abc:function(_a,_b){
       this.a=_a;
       this.b=_b;
    }
}
obj.abc.call(null,3,4);
//等同于
obj.abc.call(window,3,4);
console.log(obj);//{abc:fn()}
console.log(a,b);//3,4

案例:返回数组中最大的数值

因为Math.max 没有this,null,传入以后并没有任何作用,目的是传参时传入的是数组。

var Math={
    max:function(){
        if(arguments.length===0) return;
        var max=arguments[0];
        if(arguments.length===1) return max;
        for(var i=1;i<arguments.length;i++){
            max=max>arguments[i] ? max : arguments[i];
        }
        return max;
    }
}
var arr=[1,3,5,6];
var max=Math.max.apply(null,arr);
console.log(max);//6

案例:将一个元素集合转为数组

<div></div>
<div></div>
<div></div>
<div></div>
<script>
	var divs=document.getElementsByTagName("div");
    console.log(divs);//HTMLCollection(5) [div, div, div, div, div]
    var arr=Array.prototype.slice.call(divs);
    //var arr=[].slice.call(divs);//跟上面的写法意思一样
    console.log(arr);//[div, div, div, div, div]
</script>

Array.prototype.slice.call() 实现原理:

class Array1{
  constructor(){

    }
    // 属性和方法  动态属性,实例属性,该类的原型属性,实例化的原型链属性
    // 动态属性 其实相对static 静态来说的
    // 实例属性,该属性是通过new 构造函数来实例化对象以后调用的
    // 该类的原型属性 站在类的立场上,类.prototype.属性   ES5中的方式
    // 实例化的原型链属性 站在实例对象的的立场上,ES5中,对象中原型链上的方法和属性
    slice(start,end){
        // ES6中实例方法中的this就是该类实例化的对象
        start=Number(start);
        end=Number(end);
        if(isNaN(start)) start=0;
        if(isNaN(end)) end=this.length;
        if(start<0) start=this.length+start;
        if(end<0) end=this.length+end;
        var arr=[];
        for(var i=start;i<end;i++){
            arr.push(this[i]);
        }
        return arr;
    }
}

Array1.prototype.slice();//this指的是Array1
var arr=new Array1();
arr.slice();//this指的是实例化出来的arr,它的__proto__是Array1

arr.slice.call(divs);
Array1.prototype.slice.call(divs);

原型链 原型

  • 对象的原型链和构造函数(类)的原型是同一个引用对象;
  • 原型链是针对对象的叫法,只有对象才有__proto__;
  • 原型是构造函数(类)的叫法,只有构造函数(类)才有prototype;
  • 实例化构造函数(原型)后,可以通过实例调用该构造函数(原型)的方法,会自动拥有自身原型的属性和方法;
class Box{
    static a=3;
    b=4;
    constructor(){

    }
    play(){
        console.log(this);//打印结果如下图
    }
    static once(){

    }
}

Box.once();//静态方法
var b=new Box();
b.play();
Box.prototype.play();

可以看出 b.__proto__ === Box.prototype
原型链和原型的区别

//如果在对象的对象属性中没有找到,则会去原型链上找到里他最近的这个属性,
b.play()===b.__proto__.play();
Box.prototype.play()===b.play();
//得出如下结论
b.__proto__===Box.prototype

bind()

当需要在回调函数中重新执行回调函数中的this,就需要是用bind来指向对象

function fn1(fn){
    fn(3);
}
function fn2(_a){
    this.a=_a;
}

// 当需要在回调函数中重新执行回调函数中的this,就需要是用bind来指向对象
var obj={};
// 把fn2函数中的this指向obj,并且返回这个被指向this后新的函数
fn1(fn2.bind(obj));
console.log(obj);//{a:3}

// 这里创建了一个新的函数,return了一个新的函数
var fns=fn2.bind(obj);
console.log(fns===fn2);//false

bind()的实现原理:

function fn1(fn){
    fn(3);
}
function fn2(_a){
    this.a=_a;
}
function bind(fn,obj){
   var fn1=function(){
       fn.apply(obj,Array.from(arguments));
   }
   return fn1;
}
var obj={};
var fns=bind(fn2,obj);
fn1(fns);
console.log(obj);//{a:3}

案例:匿名回调函数中改变this的指向

var obj={
 b:2,
    a:function(){
        setTimeout(function(){
            console.log(this.b);//this指向window
        },100)
        setTimeout((function(){
            console.log(this.b);//this指向obj
        }).bind(this),100)
        this.bindHandler=this.clickHandler.bind(this)
        //监听事件
        document.addEventListener("click",this.bindHandler);
    },
    clickHandler:function(e){
        console.log(this.b);//this指向Obj
        //解除事件监听
        document.removeEventListener("click",this.bindHandler);
    }
}
obj.a();

call() apply() bind() 的区别

  • 它们在功能上是没有区别的,都是改变 this 的指向,区别主要是在于方法的实现形式和参数传递上的不同。 call 和 apply 方法都是在调用之后立即执行的。而bind 调用之后是返回原函数,需要再调用一次才能执行。
  • 写法不同:
  • 函数.call(对象,arg1,arg2…)
  • 函数.apply(对象,[arg1,arg2…])
  • var fn = 函数.bind(对象,arg1,arg2…)
发布了74 篇原创文章 · 获赞 39 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Charissa2017/article/details/104368990