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

前言

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

本人github: github.com/Michael-lzg前端

this 指向

使用 JavaScript 開發的時候,不少開發者多多少少會被 this 的指向搞蒙圈,可是實際上,關於 this 的指向,記住最核心的一句話:哪一個對象調用函數,函數裏面的 this 指向哪一個對象。vue

一、普通函數:誰調用指向誰

全局變量指向全局對象-windowjava

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

有一點須要注意,let 聲明的全局變量,不是指向 window 對象webpack

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

二、對象函數調用

就是那個函數調用,this 指向哪裏git

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

3.構造函數中調用

JS裏的普通函數可使用new操做符來建立一個對象,此時該函數就是一個構造函數,箭頭函數不能做爲構造函數。執行new操做符,其實JS內部完成了如下事情:github

  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在箭頭函數定義時就已經肯定了,而且不能再被修改。web

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 兩者而言,做用徹底同樣,只是接受參數的方式不太同樣vue-cli

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
複製代碼

推薦文章

關注的個人公衆號不按期分享前端知識,與您一塊兒進步!

相關文章
相關標籤/搜索