1瓣颅、call()
面試當中幾乎每次都會問到一個js中關(guān)于call、apply譬正、bind的問題宫补,比如…
- 怎么利用call、apply來求一個數(shù)組中最大或者最小值
- 如何利用call曾我、apply來做繼承
- apply粉怕、call、bind的區(qū)別和主要應用場景
首先抒巢,要明白這三個函數(shù)的存在意義是什么斋荞?答案是改變函數(shù)執(zhí)行時的上下文,再具體一點就是改變函數(shù)運行時的this指向虐秦。有了這個認識平酿,接下來我們來看一下,怎么使用這三個函數(shù)凤优。
let obj = {name: 'tony'};
function Child(name){
this.name = name;
}
Child.prototype = {
constructor: Child,
showName: function(){
console.log(this.name);
}
}
var child = new Child('thomas');
child.showName(); // thomas
// call,apply,bind使用
child.showName.call(obj);
child.showName.apply(obj);
let bind = child.showName.bind(obj); // 返回一個函數(shù)
bind(); // tony
復制代碼
我們拿別人的showName方法,并動態(tài)改變其上下文幫自己輸出了信息蜈彼,說到底就是實現(xiàn)了復用
bind
bind方法是事先把fn的this改變?yōu)槲覀円胍慕Y(jié)果筑辨,并且把對應的參數(shù)值準備好,以后要用到了幸逆,直接的執(zhí)行即可棍辕,也就是說bind同樣可以改變this的指向,但和apply还绘、call不同就是不會馬上的執(zhí)行(如上一個例子)
注意:bind這個方法在IE6~8下不兼容楚昭。
區(qū)別
上面看起來三個函數(shù)的作用差不多,干的事幾乎是一樣的拍顷,那為什么要存在3個家伙呢抚太,留一個不就可以。所以其實他們干的事從本質(zhì)上講都是一樣的動態(tài)的改變this上下文,但是多少還是有一些差別的..
- call昔案、apply與bind的差別
call和apply改變了函數(shù)的this上下文后便執(zhí)行該函數(shù),而bind則是返回改變了上下文后的一個函數(shù)尿贫。
- call、apply的區(qū)別
他們倆之間的差別在于參數(shù)的區(qū)別踏揣,call和aplly的第一個參數(shù)都是要改變上下文的對象庆亡,而call從第二個參數(shù)開始以參數(shù)列表的形式展現(xiàn),apply則是把除了改變上下文對象的參數(shù)放在一個數(shù)組里面作為它的第二個參數(shù)捞稿。
let arr1 = [1, 2, 19, 6];
//例子:求數(shù)組中的最值
console.log(Math.max.call(null, 1,2,19,6)); // 19
console.log(Math.max.call(null, arr1)); // NaN
console.log(Math.max.apply(null, arr1)); // 19 直接可以用arr1傳遞進去
復制代碼
例子2:
function fn() {
console.log(this);
}
// apply方法結(jié)果同下
fn.call(); // 普通模式下this是window又谋,在嚴格模式下this是undefined
fn.call(null); // 普通模式下this是window,在嚴格模式下this是null
fn.call(undefined); // 普通模式下this是window娱局,在嚴格模式下this是undefined
復制代碼
應用
- 將偽數(shù)組轉(zhuǎn)化為數(shù)組(含有l(wèi)ength屬性的對象搂根,dom節(jié)點, 函數(shù)的參數(shù)arguments)
js中的偽數(shù)組(例如通過document.getElementsByTagName獲取的元素、含有l(wèi)ength屬性的對象)具有l(wèi)ength屬性铃辖,并且可以通過0剩愧、1勾邦、2…下標來訪問其中的元素鸟廓,但是沒有Array中的push、pop等方法邀窃。就可以利用call犬第,apply來轉(zhuǎn)化成真正的數(shù)組锦积,就可以使用數(shù)組的方法了
case1: dom節(jié)點:
<div class="div1">1</div>
<div class="div1">2</div>
<div class="div1">3</div>
let div = document.getElementsByTagName('div');
console.log(div); // HTMLCollection(3) [div.div1, div.div1, div.div1] 里面包含length屬性
let arr2 = Array.prototype.slice.call(div);
console.log(arr2); // 數(shù)組 [div.div1, div.div1, div.div1]
復制代碼
但是這個不適用于IE6~8,會報錯:
SCRIPT5014: Array.prototype.slice: 'this' 不是 JavaScript 對象 (報錯)
復制代碼
那么在IE6~8下就只能通過循環(huán)一個個加到數(shù)組中了:
for (var i = 0; i < oLis.length; i++) {
ary[ary.length] = oLis[i];
}
復制代碼
基于IE6~8和標準瀏覽器中的區(qū)別歉嗓,抽取出類數(shù)組對象轉(zhuǎn)換為數(shù)組的工具類:
function listToArray(likeAry) {
var ary = [];
try {
ary = Array.prototype.slice.call(likeAry);
} catch (e) {
for (var i = 0; i < likeAry.length; i++) {
ary[ary.length] = likeAry[i];
}
}
return ary;
}
復制代碼
case2: fn內(nèi)的arguments
function fn10() {
return Array.prototype.slice.call(arguments);
}
console.log(fn10(1,2,3,4,5)); // [1, 2, 3, 4, 5]
復制代碼
注意:對于arguments借用數(shù)組的方法是不存在任何兼容性問題的丰介。
case3: 含有l(wèi)ength屬性的對象
let obj4 = {
0: 1,
1: 'thomas',
2: 13,
length: 3 // 一定要有l(wèi)ength屬性
};
console.log(Array.prototype.slice.call(obj4)); // [1, "thomas", 13]
復制代碼
-
數(shù)組拼接,添加
let arr1 = [1,2,3];
let arr2 = [4,5,6];//數(shù)組的concat方法:返回一個新的數(shù)組
let arr3 = arr1.concat(arr2);
console.log(arr3); // [1, 2, 3, 4, 5, 6]console.log(arr1); // [1, 2, 3] 不變
console.log(arr2); // [4, 5, 6] 不變
// 用 apply方法
[].push.apply(arr1,arr2); // 給arr1添加arr2
console.log(arr1); // [1, 2, 3, 4, 5, 6]
console.log(arr2); // 不變
復制代碼 -
判斷變量類型
let arr1 = [1,2,3];
let str1 = 'string';
let obj1 = {name: 'thomas'};
//
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
console.log(fn1(arr1)); // true// 判斷類型的方式,這個最常用語判斷array和object哮幢,null(因為typeof null等于object)
console.log(Object.prototype.toString.call(arr1)); // [object Array]
console.log(Object.prototype.toString.call(str1)); // [object String]
console.log(Object.prototype.toString.call(obj1)); // [object Object]
console.log(Object.prototype.toString.call(null)); // [object Null]
復制代碼 -
利用call和apply做繼承
function Animal(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}function Cat(name){
Animal.call(this, name);
}// Animal.call(this) 的意思就是使用this對象代替Animal對象带膀,那么
// Cat中不就有Animal的所有屬性和方法了嗎,Cat對象就能夠直接調(diào)用Animal的方法以及屬性了
var cat = new Cat("TONY");
cat.showName(); //TONY
復制代碼 -
多繼承
function Class1(a,b) { this.showclass1 = function(a,b) { console.log(`class1: ${a},$橙垢`); } } function Class2(a,b) { this.showclass2 = function(a,b) { console.log(`class2: ${a},$垛叨`); } } function Class3(a,b,c) { Class1.call(this); Class2.call(this); } let arr10 = [2,2]; let demo = new Class3(); demo.showclass1.call(this,1); // class1: 1,undefined demo.showclass1.call(this,1,2); // class1: 1,1 demo.showclass2.apply(this,arr10); // class2: 2,2