【視頻教程】你不知道的call&apply-冰山工做室-沙翼-web前端

圖片描述

視頻教程-call&&applyhtml

前情回顧

call、apply、bind三者均來自Function.prototype,被設計用來用於改變函數體內this的指向。前端

舉個例子node

// 有隻貓叫小黑,小黑會吃魚
const cat = {
name: '小黑',
    eatFish(...args) {
console.log('this指向=>', this);
console.log('...args', args);
console.log(this.name + '吃魚');
    },
}
// 有隻狗叫大毛,大毛會吃骨頭
const dog = {
name: '大毛',
    eatBone(...args) {
console.log('this指向=>', this);
console.log('...args', args);
console.log(this.name + '吃骨頭');
    },
}
 
console.log('=================== call =========================');
// 有一天大毛想吃魚了,但是它不知道怎麼吃。怎麼辦?小黑說我吃的時候餵你吃
cat.eatFish.call(dog, '汪汪汪', 'call')
// 大毛爲了表示感謝,決定下次吃骨頭的時候也喂小黑吃
dog.eatBone.call(cat, '喵喵喵', 'call')
 
console.log('=================== apply =========================');
cat.eatFish.apply(dog, ['汪汪汪', 'apply'])
dog.eatBone.apply(cat, ['喵喵喵', 'apply'])
 
console.log('=================== bind =========================');
// 有一天他們以爲每次吃的時候再喂太麻煩了。乾脆直接教對方怎麼吃
const test1 = cat.eatFish.bind(dog, '汪汪汪', 'bind')
const test2 = dog.eatBone.bind(cat, '喵喵喵', 'bind')
test1()
test2()

招式分解

Apply()的使用:

apply() 方法調用一個具備給定this值的函數,以及做爲一個數組(或相似數組對象)提供的參數。func.apply(thisArg, [argsArray])

thisArg 可選的參數。在 func 函數運行時使用的 this 值。注意,不必定是該函數執行時真正的 this 值:若是這個函數處於非嚴格模式下,則指定爲 null 或 undefined 時會自動指向全局對象(瀏覽器中就是window對象),當值爲原始值(1,‘string’,true)時 this 會指向該原始值的自動包裝對象(Number,String,Boole)。
argsArray 可選的參數。一個數組或者類數組對象(NodeList),其中的數組元素的每一項將做爲單獨的參數傳給 func 函數。若是該參數的值爲 null 或 undefined,則表示不須要傳入任何參數。從ECMAScript 5 開始可使用類數組對象。segmentfault

妙用一:數組拼接數組

var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]

解決了想修改原數組,不想用循環,還想傳遞一個數組的問題瀏覽器

妙用二:數組求最值app

/* 找出數組中最大/小的數字 */
var numbers = [5, 6, 2, 3, 7];
 
/* 應用(apply) Math.min/Math.max 內置函數完成 */
var max = Math.max.apply(null, numbers); /* 基本等同於 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */
var min = Math.min.apply(null, numbers);
console.log(max,min)
  1. 若是一個數組咱們已知裏面全都是數字,想要知道最大的那個數,因爲Array沒有max方法,Math對象上有
  2. 咱們能夠根據apply傳遞參數的特性將這個數組當成參數傳入
  3. 最終Math.max函數調用的時候會將apply的數組裏面的參數一個一個傳入,剛好符合Math.max的參數傳遞方式
  4. 這樣變相的實現了數組的max方法。min方法也同理
  5. 會有Argument length limited to 65536的限制

妙用三:僞數組轉換ide

var arrayLike = {
    0: 'xiaobai',
    1: 'xiaohei',
    2: 'xiaohong',
    length: 3
}
var arr = Array.prototype.slice.call(arrayLike);
console.log(arr)

妙用四:變量類型判斷函數

function isArray(obj) {
    return Object.prototype.toString.call(obj) == '[object Array]';
}
console.log(isArray([]))
console.log(isArray('qianduan'))
  • toString()方法容許被修改,以上假定未被修改
  • toString()爲Object的原型方法,而Array,function等類型做爲Object的實例,都重寫了toString方法。不一樣的對象類型調用toString方法時,調用的是對應的重寫以後的toString方法(function類型返回內容爲函數體的字符串,Array類型返回元素組成的字符串),而不會去調用Object上原型toString方法(返回對象的具體類型),因此採用obj.toString()不能獲得其對象類型,只能將obj轉化爲字符串類型;所以,在想要獲得對象的具體類型時,應該調用Object上原型toString方法。

妙用五:構造繼承this

function Animal(name, age) {
    this.name = name;
    this.age = age;
}
 
function Dog() {
    Animal.apply(this,['cat','5']);
    // Animal.call(this, 'cat', '5');
    this.say = function() {
        console.log(this.name + ":" + this.age);
    }
}
 
var dog = new Dog();
dog.say(); //cat:5
new的過程Dog中的this指向了建立的dog對象,而後執行構造函數中的代碼,執行了關鍵的apply,apply將當前環境的this(dog對象)指定給Animal,因此Animal中的this指向的就是dog對象,Animal中定義了name和age屬性,就至關於在dog中定義了這些屬性,所以dog對象便擁有了Animal中定義的屬性,從而達到了繼承的目的

Call()的使用:

call() 方法調用一個函數, 其具備一個指定的this值和分別地提供的參數(參數的列表)。apply的語法糖。fun.call(thisArg, arg1, arg2, ...)
  • thisArg 可選的參數。在 func 函數運行時使用的 this 值。注意,不必定是該函數執行時真正的 this 值:若是這個函數處於非嚴格模式下,則指定爲 null 或 undefined 時會自動指向全局對象(瀏覽器中就是window對象),當值爲原始值(1,‘string’,true)時 this 會指向該原始值的自動包裝對象(Number,String,Boole)。
  • argsArray 可選的參數。指定的參數列表。

妙用一:調用匿名函數

妙用一:調用匿名函數
兼容嚴格模式,嚴格模式下,匿名函數裏this會報錯。也實現了匿名函數針對不一樣的this,作不一樣的處理。

妙用二:字符串分隔鏈接

var temp = Array.prototype.join.call('hellow!', ',');

console.log(temp)
字符串沒有join方法,借用Array

妙用三:字符串取每一項

Array.prototype.map.call('foo', (item) => {
    console.log(item)
}).join('');
字符串沒有join方法,借用Array

Bind()的使用:

bind()方法建立一個新的函數,在調用時設置this關鍵字爲提供的值。並在調用新函數時,將給定參數列表做爲原函數的參數序列的前若干項。
function.bind(thisArg[, arg1[, arg2[, ...]]])
  • thisArg 調用綁定函數時做爲this參數傳遞給目標函數的值。 若是使用new運算符構造綁定函數,則忽略該值。當使用bind在setTimeout中建立一個函數(做爲回調提供)時,做爲thisArg傳遞的任何原始值都將轉換爲object。若是bind函數的參數列表爲空,執行做用域的this將被視爲新函數的thisArg。
  • argsArray 當目標函數被調用時,預先添加到綁定函數的參數列表中的參數。

解釋一下:

ES6新增的方法,這個方法會返回一個新的函數(函數調用的方式),調用新的函數,會將原始函數的方法當作傳入對象的方法使用,傳入新函數的任何參數也會一併傳入原始函數。

基本

function f(x) {
    console.log(this.a + x); //原始函數
}
var obj = {
    a: 1       //傳入對象
}
var newFn = f.bind(obj) //會將原始函數的方法當作傳入對象的方法使用
newFn(2) //調用新的函數
  
setTimeout(function() {
    console.log(this.name + ": Got it!")
}.bind(this), 30)
  
daily() {
    this.enjoy(function() {
        this.eat()
        this.drink()
        this.sleep()
    }.bind(this))
}

若是使用new運算符構造生成的綁定函數,則忽略綁定的this。

返回函數用做構造函數

function f(x) {
    this.a = 1;
    this.b = function() { return this.a + x }
}
var obj = {
    a: 10
}
var newObj = new(f.bind(obj, 2)) //傳入了一個實參2
console.log(newObj.a) //輸出 1, 說明返回的函數用做構造函數時obj(this的值)被忽略了
 
console.log(newObj.b()) //輸出3 ,說明傳入的實參2傳入了原函數original

不傳值

window.color = 'red';
 
function sayColor(){
  console.log(this.color);
}
var func2 = sayColor.bind();//this同理
// 輸出 "red", 由於傳的是'',全局做用域中this表明window。等於傳的是window。
func2();

屢次bind會發生什麼?

在Javascript中,屢次 bind() 是無效的。更深層次的緣由, bind() 的實現,至關於使用函數在內部包了一個 call / apply ,第二次 bind() 至關於再包住第一次 bind() ,故第二次之後的 bind 是沒法生效的。

屢次綁定

function say() {
    console.log(this.x);
};
var a = say.bind({x: 1});
var b = a.bind({x: 2});
b()
  
var a = function() {
    return say.apply({x: 1}); //改變say的this
};
  
var b = function() {
    return a.apply({x: 2});  //改變了a的this
};

妙用一:偏函數(使一個函數擁有預設的初始參數)

//在綁定this的同事傳入幾個值做爲默認參數,以後執行時再傳入的參數排在默認參數後面。
const obj = {}
function f(...args) {console.log(args)}
const newFn = f.bind(obj, '1', '2')
newFn('3', '4')
  
  
function add(arg1, arg2) {
    return arg1 + arg2
}
// 建立一個函數,它擁有預設的第一個參數
var addThirtySeven = add.bind(null, 37);
var result3 = addThirtySeven(5, 10);
console.log(result3)
// 37 + 5 = 42 ,第二個參數被忽略

妙用二:快捷調用

<div class="test">hello</div>
<div class="test">hello</div>
<div class="test">hello</div>
<div class="test">hello</div>
<script>
  function log() {
    console.log("hello")
  }
 
  //const forEach = Array.prototype.forEach
  //forEach.call(document.querySelectorAll(".test"), (test) => test.addEventListener("click", log))
  
  const unbindForEach = Array.prototype.forEach,
        forEach = Function.prototype.call.bind(unbindForEach)
  forEach(document.querySelectorAll(".test"), (test) => test.addEventListener("click", log))
</script>
  • 生成一個長期綁定的函數給nodeList使用,不須要每次都使用call

見招拆招

何時是用什麼方法

  • apply:傳遞參數很少,傳遞類數組
  • call:肯定參數個數,關注參數的對應關係
  • bind:不當即執行,生成一個新的函數,長期綁定某個函數給某個對象使用

融會貫通

  • a.func.bind(b).call(c) this指向哪裏?
  • 如何手寫一個完美的call(),bind()方法?

關於咱們

原始高清視頻下載

視頻講解-提取碼:eepx

QQ答疑交流羣:

600633658

咱們的連接:

知乎 掘金 今日頭條 新浪微博 前端網 思否 簡書 B站

相關文章
相關標籤/搜索