函數2---call、apply、bind

  函數call() apply() bind() 三個方法目的相同: 改變函數中this的指向;三者在使用上存在差別,這致使三者有各類的應用場景:call()、apply()更爲接近;html

  相同點: call、apply、bind 第一個參數:this指向的對象(上下文),以後的參數爲要傳入的變量;數組

  不一樣點:app

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.用法:oop

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

  2.第一個參數obj的取值:spa

    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、Booleancode

    2.3).傳入函數名,this指向這個函數的引用htm

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版)》

相關文章
相關標籤/搜索