面試,幾乎每次都會問到一個js中關於call、apply、bind的問題,好比…面試
怎麼利用call、apply來求一個數組中最大或者最小值數組
如何利用call、apply來作繼承數據結構
apply、call、bind的區別和主要應用場景app
首先問個問題,這三個函數的存在乎義是什麼?答案是改變函數執行時的上下文,再具體一點就是改變函數運行時的this指向。函數
function Person(name) { this.name = name } Person.prototype = { constructor: Person, showName: function() { console.log(this.name) } } var person = new Person('shifeng') person.showName() // 輸入'shifeng'
var animal = { name: 'cat }
上面代碼中有一個對象字面量,他沒有所謂的showName方法,可是我仍是想用?怎麼辦?(坑爹了,這好像在讓巧媳婦去作無米之炊),不過不要緊,call、apply、bind能夠幫咱們幹這件事。this
// 如下三種都會輸出'cat' // 1. call person.showName.call(animal) // 2. apply person.showName.apply(animal) // 3. bind person.showName.bind(animal)
咱們拿別人的showName方法,並動態改變其上下文幫本身輸出了信息,說到底就是實現了複用prototype
上面看起來三個函數的做用差很少,乾的事幾乎是同樣的,那爲何要存在3個傢伙呢,留一個不就能夠。因此其實他們乾的事從本質上講都是同樣的動態的改變this上下文,可是多少仍是有一些差異的...code
call和apply改變了函數的this上下文後便執行該函數, 而bind則是返回改變了上下文的一個函數對象
他們倆之間的差異在於參數的區別,call和aplly的第一個參數都是要改變上下文的對象,而call從第二個參數開始以參數列表的形式展示,apply則是把除了改變上下文對象的參數放在一個數組裏面做爲它的第二個參數。繼承
fn.call(contextObj, arg1, arg2, arg3...) fn.apply(contextObj, [arg1, arg2, aeg3...])
求數組中的最大值和最小值(並非最佳選擇)
var arr = [34,5,3,6,54,6,-67,5,7,6,-8,687] // max、min 是 Math 中的靜態方法,所以必然是沒有使用上下文的必要的。 // 所以 call、apply 綁定只須要用 null 或者 undefined 佔位就能夠了。 Math.max.call(null, 34,5,3,6,54,6,-67,5,7,6,-8,687) // 同Math.max(34,5,3,6,54,6,-67,5,7,6,-8,687) Math.max.apply(null, arr) Math.min.call(undefined, 34,5,3,6,54,6,-67,5,7,6,-8,687) Math.min.apply(undefined, arr) // 最佳方法, ES6數組解構 Math.max(...arr) Math.min(...arr)
將僞數組轉化爲數組
js中的僞數組(例如經過document.getElementsByTagName或者document.querySelectorAll獲取的元素)具備length屬性,而且能夠經過0、一、2…下標來訪問其中的元素,可是沒有Array中的push、pop等方法。咱們能夠利用call、apply來將其轉化爲真正的數組這樣即可以方便地使用數組方法了。
var arrayLike = { 0: 'shifeng', 1: 'xingyun', 2: 'ruxue', length: 3 }
上面就是一個普通的對象字面量,怎麼把它變成一個數組呢?最簡單的方法就是
var arr = Array.prototype.slice.call(arrayLike)
上面arr即是一個包含arrayLike元素的真正的數組啦(注意數據結構必須是以數字爲下標並且必定要有length屬性)
數組追加
在js中要往數組中添加元素, 能夠直接用push方法
var arr1 = [1,2,3] var arr2 = [4,5,6] // Array.prototype.push.apply(arr1, arr2) [].push.apply(arr1, arr2) // 使用一個輔助的空數組(爲了訪問非靜態方法),就是把arr2利用apply的數組參數特性push到arr1中. // 這樣經過解構能夠寫成 arr1.push(...arr2), 若是須要返回一個全新的對象,還可使用 [...arr1, ...arr2]。 // 還能夠寫成var newArr = arr1.concat(arr2) // arr1 [1, 2, 3, 4, 5, 6] // arr2 [4,5,6]
判斷變量類型
對於對象型的數據類型, 咱們能夠藉助call來得知他的具體類型, 例如數組
function isArray(obj){ return Object.prototype.toString.call(obj) == '[object Array]'; } isArray([]) // true isArray('qianlong') // false