javascript必備知識點之 call、apply、bind 的應用

上文分享了this的應用,這次分享call、apply、bind的應用

1.每個函數都有call、apply、bind方法,通俗點說就是function foo()這樣形式就能使用foo.call()/apply()/bind()方法。

2.它們都能改變函數執行上下文。區別在於:

1)call、apply返回值爲undefined,且函數立即執行,bind返回值爲函數,不會立即執行。

2)call(this,arg2,arg4,arg4…)接收的參數爲數據列(通俗點說就是一個一個用逗號隔開的值),apply(this,[a1,a2,a3])接收的第二個參數爲數組。

下面通過實例來看運用
定義一個全局變量obj、foo函數,此時foo函數中的this指向window,要訪問obj中的屬性,需要先訪問window,再從window中取值this.obj.name

var obj = {
   name:'hehe',
   age:23
}

function foo(){
   console.log(this.obj.name)    // 'hehe'
}
var obj = {
 name: 'hehe',
 age: '23',
 foo: function () {
   console.log(this.name)    // 'hehe'
 }
}
obj.foo()

上段代碼和下段有相似之處:obj都是全局變量,都是訪問全局的obj裏的name屬性。不同的是foo函數裏的this指向:上段代碼中this指向Window,下段指向obj。

call:
使用call可以改變執行上下文, foo中的this指向obj而不是Window了。

var obj = {
    name:'hehe',
    age:23
}

function foo(){
    console.log(this.name)    // 'hehe'
}

foo.call(obj)

同理原先指向obj的寫法,使用call後,可以使this指向其他變量。

var o = {
  name: 'aaaaaaa'
}
var obj = {
  name: 'hehe',
  age: '23',
  foo: function () {
    console.log(this.name)    // 'aaaaaaa'
  }
}
obj.foo.call(o)

對於call第一個參數之後的參數,即爲函數可以使用的參數,例如:

var o = {
  name: 'aaaaaaa'
}
var obj = {
  name: 'hehe',
  age: '23',
  foo: function (n, m) {
    console.log(this, n, m)    // { name: 'aaaaaaa' } { a: '4' } '6'
  }
}
obj.foo.call(o, { a: '4' }, '6')

apply改變this的使用和call一毛一樣,只是後面的參數爲數組:

var o = {
  name: 'aaaaaaa'
}
var obj = {
  name: 'hehe',
  age: '23',
  foo: function (n, m) {
    console.log(this, n, m)    // 同樣分別獲得值 { name: 'aaaaaaa' } { a: '4' } '6'
  }
}
obj.foo.apply(o, [{ a: '4' }, '6'])

call的應用,例如Object.prototype.toString.call()、Array.prototype.slice.call() //apply

Object和Object.prototype都有toString方法,Object.toString()返回函數"function Object() { [native code] }",Object.prototype.toString()返回類型"[object Object]",那麼call在這裏充當了什麼角色?call改變了toString執行的上下文,Object.prototype.toString()toString this指向Object,Object.prototype.toString.call([]) this就指向[],Object.prototype.toString.call(1) this指向1。那麼爲什麼是使用Object.prototype而不是Array.prototype或者Number.prototype?因爲所有對象繼承自Object,而Array、Number並不互相包含其他繼承對象。

所有的對象都繼承自Object,Object.prototype 指向 Object.prototype原型對象,Object.prototype.constructor指向Object。當我們使用arr.toString()時,不能進行復雜數據類型的判斷,因爲它調用的是Array.prototype.toString,雖然Array也繼承自Object,但js在Array.prototype上重寫了toString,而我們通過toString.call(arr)實際上是通過原型鏈調用了Object.prototype.toString。

Array.prototype.slice.call(arguments):slice 方法可以用來將一個類數組(Array-like)對象/集合轉換成一個新數組。call()方法的第二個參數表示傳遞給slice的參數即截取數組的起始位置。

function list() {
  return Array.prototype.slice.call(arguments,1); //apply(arguments,[1])
}
var list1 = list(1, 2, 3); // [2, 3]

再來看看bind,bind接收參數形式和call一毛一樣(this,arg2,arg3,arg4…)。bind返回的是個函數,但不會立即執行,所以我加了個()執行它。

var o = {
  name: 'aaaaaaa'
}
var obj = {
  name: 'hehe',
  age: '23',
  foo: function (n, m) {
    console.log(this, n, m)   // { name: 'aaaaaaa' } { a: '4' } '6'
  }
}
obj.foo.bind(o, { a: '4' }, '6')()   // 這裏執行了bind返回的函數 foo

bind的應用,例如在react中寫了一個方法,調用這個方法之前需要將這個方法的this指向綁定到constructor上下文。例如:

constructor(props){
    super(props)
    this.foo = this.foo.bind(this)
}

這樣在jsx中調用foo方法this指向組件實例。

不使用bind改變this指向,this的指向就會丟失。例如:

var obj = {
  name: 'hehe',
  age: '23',
  foo: function () {
    console.log(this)   // { name: 'hehe', age: '23', foo: [Function: foo] }
  }
}
obj.foo()
var Fo = obj.foo
Fo()    // window

this指向最終調用它的執行環境。在react中使用bind改變上下文執行環境後,由於react是虛擬DOM結構,所以在div標籤中使用onClick事件,this指向的不是DOM而是組件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章