函数2---call、apply、bind

  函数call() apply() bind() 三个方法目的相同: 改变函数中this的指向;三者在使用上存在差异,这导致三者有各种的应用场景:call()、apply()更为接近;

  相同点: call、apply、bind 第一个参数:this指向的对象(上下文),之后的参数为要传入的变量;

  不同点:

var obj = {name:"Hello , function !"}

function fun(){
    console.log(this.name)  
}

fun.call(obj)
fun.apply(obj)
fun.bind(obj)()

//1.call、apply 使用接近,只是参数传递形式不同
//2.call、apply 会立即执行函数;bind返回一个绑定obj的函数,不会立即执行(这点很重要!决定了bind的使用场景)

一,call()

  1.用法:

     call( [obj , [arg1,[arg2,...]]] )

  2.第一个参数obj的取值:

    2.1).不传、null、undefined,函数的this指向window对象(在不严格模式下,this指向window;严格模式下,对应的this指向undefined、null、undefined)

function A(){console.log(this)} // 不严格模式下
A.call() // window
A.call(null) // window
A.call(undefined) // window

function B(){"use strict"; console.log(this)} // 严格模式下
B.call() // undefined
B.call(null) // null
B.call(undefined) // undefined

    2.2).传入数值、字符串、布尔值等基础类型,this指向对应的包装类型Number、String、Boolean

    2.3).传入函数名,this指向这个函数的引用

A.call( B ) // function B(){"use strict"; console.log(this) }

    2.4).传入对象,this指向这个对象(最常用)

  3.使用场景

    3.1)一个容易犯错的例子

function add(x,y){
    console.log( x+y )  
}
function sub(x,y){
    console.log( x-y )
}
add.call( sub, 8, 6 )  // 14
// 执行的是add函数,只不过add函数中this指向了sub函数

    3.2)改变this的指向(常用)

var name = "hi!"
var obj = {name:"hello!"}
function A(){console.log(this.name)}
A() // "hi!"
A.call(obj) // "hello!"

    3.3) 继承(常用)

function Person(id,name){
    this.id = id;
    this.name = name;
}
function Student(id,name,grade,score){
    Person.call(this,id,name) // 使用call()完成继承
    this.grade = grade;
    this.score = score;
}

var stu = new Student('123','Jame','grade 2','98') 

二,apply()

  apply 和 call 使用基本相同,仅仅是参数传递不同;call() 参数一个一个传;apply() 传递的是一个数组;

  当传递参数个数已知时,使用call();如果参数不定,使用apply();

  用法:

    apply(obj,[arg1,arg2,...])

  例题:

    1.自定义log()函数,模拟console.log()函数;

      分析:

        1.1)console.log()函数,可以传递多个不同类型的参数;通过自定义函数简单模拟console.log()可以这样做:

         function log(arg){ console.log(arg) }

         但是这样有个问题,只能传递一个参数,多个参数怎么办?

        2.1) 由于多个参数就涉及到arguments对象,可以将log()改装成:

         function log(){ console.log(arguments) }

         这样还不行,console.log()会把arguments直接打印出来;

             此时的arguments仅仅是console.log()函数中arguments对象的arguments[0];

         如果想按照console.log()方式打印出来,必须将log()的arguments传值给console.log()函数的arguments对象;

       正确方式如下:

// 只能使用apply
function log(){
    console.log.apply(console,arguments)
}

// 不能使用call函数,否则console.log()函数仍然会把 log()函数的arguments对象当成自身arguments[0]

三,bind()

  函数绑定bind是ES5中新增的,IE6,7,8不支持;

  bind绑定对象后不会立即执行,而是返回一个函数,以便特定情况下使用;这个技巧大量的用在回调函数、事件处理程序中;

  1.事件处理程序中的this指向

    DOM元素绑定事件后,事件处理程序中this默认情况下指向DOM元素;(IE8 默认指向window)

    如果要使用别的对象中的方法,就必须使用bind()进行绑定

var handler = {
    name: "Hello world",
    handClick: function(event){
        console.log( this.name )
    }
}
document.getElementById('demo').addEventListener('click',handler.handClick); 
// 输出undefined,DOM绑定后的函数handClick中的this指向DOM元素;而this中不包含name

    如果要想输出handler对象中的name,需要使用bind绑定该对象

document.addEventListener('click',handler.handClick.bind(handler))

  2.回调函数中的this指向,常见的定时器

    在定时器中,回调函数中的this默认指向window;

    经常需要在定时器的回调函数中使用别的对象中的属性、方法,解决的办法有三种:1.新建一个中间变量  2.bind绑定  3.ES6中的箭头函数 ,最常用的是bind、ES6箭头函数

var obj = {
    msg:"Hello world",
    getInfo: function(){
        setTimeout(function(){console.log(this.msg)},0) // this->window ,输出undefined
     
        // 1.使用中间变量that
        var that = this;
        setTimeout(function(){console.log(that.msg),0}) // that->obj,输出"Hello world"

        // 2.使用bind
        setTimeout(function(){console.log(that.msg).bind(obj),0}) // this->obj,输出"Hello world"

       // 2.使用ES6箭头函数,箭头函数中的this指向定义这个函数时所在的对象
       setTimeout(() => {console.log(this.msg)},0}) // this->obj,输出"Hello world"
    }

}    

  bind函数的应用场景基本就是回调函数、事件处理程序;

  如何实现bind()函数?(绑定一个上下文,并返回绑定了这个上下文的函数)

function bind(fn,context){
    return function(){
        return fn.apply(context,arguments)
    }
}

参考:(感谢以下文档)

[1] 理解call、apply、bind
[2] 《JavaScript高级程序设计(第3版)》

原文地址:https://www.cnblogs.com/RocketV2/p/6734910.html