JS基礎總結(4)——this指向及call/apply/bind

lzg9527發表於2020-01-31

前言

農曆2019即將過去,趁著年前幾天上班事情少,整理了一下javascript的基礎知識,在此給大家做下分享,喜歡的大佬們可以給個小贊。本文在github也做了收錄。

本人github: github.com/Michael-lzg

this 指向

使用 JavaScript 開發的時候,很多開發者多多少少會被 this 的指向搞蒙圈,但是實際上,關於 this 的指向,記住最核心的一句話:哪個物件呼叫函式,函式裡面的 this 指向哪個物件。

1、普通函式:誰呼叫指向誰

全域性變數指向全域性物件-window

var username = 'cn'
function fn() {
  alert(this.username) //cn
}
fu()
複製程式碼

有一點需要注意,let 宣告的全域性變數,不是指向 window 物件

let username = 'cn'
function fn() {
  alert(this.username) //undefined
}
fn()
複製程式碼

2、物件函式呼叫

就是那個函式呼叫,this 指向哪裡

window.b = 2222
let obj = {
  a: 111,
  fn: function() {
    alert(this.a) //111
    alert(this.b) //undefined
  }
}
obj.fn()
複製程式碼

3.建構函式中呼叫

JS裡的普通函式可以使用new操作符來建立一個物件,此時該函式就是一個建構函式,箭頭函式不能作為建構函式。執行new操作符,其實JS內部完成了以下事情:

  1. 建立一個空的簡單JavaScript物件(即{});
  2. 將建構函式的prototype繫結為新物件的原型物件 ;
  3. 將步驟1新建立的物件作為this的上下文並執行函式 ;
  4. 如果該函式沒有返回物件,則返回this。
function A () {
  this.a = 1
  this.func = () => {
    return this
  }
}

let obj = new A()
console.log(obj.a) // 1
console.log(obj.func() === obj) // true
複製程式碼

4.箭頭函式中呼叫

箭頭函式的this指向,和箭頭函式定義所在上下文的this相同。對於普通函式,this在函式呼叫時才確定;而對於箭頭函式,this在箭頭函式定義時就已經確定了,並且不能再被修改。

let obj = {
  A () {
    return () => {
      return this
    }
  },
  B () {
    return function () {
      return this
    }
  }
}

let func = obj.A()
console.log(func() === obj) // true

func = obj.B()
console.log(func() === obj) // false
console.log(func() === window) // true
複製程式碼

apply、call、bind

在 javascript 中,call 和 apply 都是為了改變某個函式執行時的上下文(context)而存在的,換句話說,就是為了改變函式體內部 this 的指向。 舉個例子

function fruits() {}

fruits.prototype = {
  color: 'red',
  say: function() {
    console.log('My color is ' + this.color)
  }
}

var apple = new fruits()
apple.say() //My color is red

// 但是如果我們有一個物件banana= {color : "yellow"} ,我們不想對它重新定義 say 方法,
//那麼我們可以通過 call 或 apply 用 apple 的 say 方法:
banana = {
  color: 'yellow'
}
apple.say.call(banana) //My color is yellow
apple.say.apply(banana) //My color is yellow
複製程式碼

apply、call 區別

對於 apply、call 二者而言,作用完全一樣,只是接受引數的方式不太一樣

func.call(this, arg1, arg2)
func.apply(this, [arg1, arg2])
複製程式碼

apply、call 例項

// 陣列追加
var array1 = [12 , "foo" , {name:"Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
// array1 值為  [12 , "foo" , {name:"Joe"} , -2458 , "Doe" , 555 , 100]

// 獲取陣列中的最大值和最小值
var  numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers),   //458
var maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

// 驗證是否是陣列
functionisArray(obj){
  return Object.prototype.toString.call(obj) === '[object Array]'
}
複製程式碼

bind()

bind()最簡單的用法是建立一個函式,使這個函式不論怎麼呼叫都有同樣的 this 值。 bind()方法會建立一個新函式,稱為繫結函式,當呼叫這個繫結函式時,繫結函式會以建立它時傳入 bind()方法的第一個引數作為 this,傳入 bind() 方法的第二個以及以後的引數加上繫結函式執行時本身的引數按照順序作為原函式的引數來呼叫原函式。

this.num = 9
var mymodule = {
  num: 81,
  getNum: function() {
    console.log(this.num)
  }
}

mymodule.getNum() // 81

var getNum = mymodule.getNum
getNum() // 9, 因為在這個例子中,"this"指向全域性物件

var boundGetNum = getNum.bind(mymodule)
boundGetNum() // 81
複製程式碼

當呼叫 bind 函式後,bind 函式的第一個引數就是原函式作用域中 this 指向的值

function func() {
  console.log(this)
}

let newFunc = func.bind({ a: 1 })
newFunc() // 列印:{a:1}

let newFunc2 = func.bind([1, 2, 3])
newFunc2() // 列印:[1,2,3]

let newFunc3 = func.bind(1)
newFunc3() // 列印:Number:{1}

let newFunc4 = func.bind(undefined / null)
newFunc4() // 列印:window
複製程式碼
  • 當傳入為 null 或者 undefined 時,在非嚴格模式下,this 指向為 window。
  • 當傳入為簡單值時,內部會將簡單的值包裝成對應型別的物件,數字就呼叫 Number 方法包裝;字串就呼叫 String 方法包裝;true/false 就呼叫 Boolean 方法包裝。要想取到原始值,可以呼叫 valueOf 方法。

傳遞的引數的順序問題

function func(a, b, c) {
  console.log(a, b, c) // 列印傳入的實參
}

let newFunc = func.bind({}, 1, 2)

newFunc(3) //1,2,3
// 可以看到,在 bind 中傳遞的引數要先傳入到原函式中。
複製程式碼

返回的新函式被當成建構函式

// 原函式
function func(name) {
  console.log(this) // 列印:通過{name:'wy'}
  this.name = name
}
func.prototype.hello = function() {
  console.log(this.name)
}
let obj = { a: 1 }
// 呼叫bind,返回新函式
let newFunc = func.bind(obj)

// 把新函式作為建構函式,建立例項

let o = new newFunc('seven')

console.log(o.hello()) // 列印:'seven'
console.log(obj) // 列印:{a:1}
複製程式碼

新函式被當成了建構函式,原函式 func 中的 this 不再指向傳入給 bind 的第一個引數,而是指向用 new 建立的例項。在通過例項 o 找原型上的方法 hello 時,能夠找到原函式 func 原型上的方法。

apply、call、bind 比較

  • apply 、call 、bind 三者都是用來改變函式的 this 物件的指向的;
  • apply 、call 、bind 三者第一個引數都是 this 要指向的物件,也就是想指定的上下文;
  • apply 、call 、bind 三者都可以利用後續引數傳參;
  • bind 是返回對應函式,便於稍後呼叫;apply 、call 則是立即呼叫 。
var obj = {
  x: 81
}

var foo = {
  getX: function() {
    return this.x
  }
}

console.log(foo.getX.bind(obj)()) //81
console.log(foo.getX.call(obj)) //81
console.log(foo.getX.apply(obj)) //81
複製程式碼

推薦文章

關注的我的公眾號不定期分享前端知識,與您一起進步!

JS基礎總結(4)——this指向及call/apply/bind

相關文章