JS筆記(11): call & apply & bind

  • call & apply & bind
  • 用來改變某一個函數中的this關鍵字指向

1、call

  • 語法:[fn].call([this],[param]...)
  • fn.call =>當前實例(函數fn)經過原型鏈的查找機制,找到Function.prototype上的call方法 =>function call(){ [native code] }
  • fn.call() 把找到的call方法執行
  • 當call方法執行的時候,內部處理了一些事情
    • 1.首先把要操做的函數中的this變爲cal方法第一個傳遞的實參值
    • 2.把call方法第二個及第二個之後的實參獲取到
    • 3.把要操做的函數執行,而且把第二個之後傳遞進來的實參傳給函數

內置call的原理

Function.prototype.call = function () {
    let param1 = arguments[0];
    let paramOther = [ ]; //把arg中除了第一個之外的實參獲取到
    // this:fn 當前要操做的函數(函數類的一個實例)
    // 把fn中的this關鍵字修改成param1 => 把this(call中)中的this關鍵字修改成param1

    // 把fn執行,把paramOther分別傳遞給fn
    // this(paramOther)
};
fn.call(obj);
複製代碼
let sum = function (a,b) {
    console.log(this);
};
let opt = { n: 20 };

// sum.call(opt, 20, 30); //=>call執行 call中的this是sum 把sum中的this關鍵字改成opt => sum中的this: opt a=20 b=30
sum.call.call(opt);

// 1.sum.call => 找到Function.prototype上的call方法(也是一個函數,也是函數類的一個實例,也能夠繼續調用call/apply等方法) ==>A(函數)
// 2.A.call(opt) => 繼續找原型上的call方法,把call方法執行:把A中的this關鍵字修改成opt,把A執行
複製代碼

關於call執行題:

  • 若是不懂能夠記住如下規律
    • call => 就是讓call中的this執行
    • 一個call,是讓call前面的元素執行
    • 兩個及兩個以上的call,是讓call後面的元素執行
// 原理分析:
Function.prototype.call = function callAA(){
    //1.把this中的「this」關鍵字修改成第一個參數值obj
    //2.把this(fn)執行,把第二個及之後接受的參數值傳遞給函數(10,20)
    //this(10,20)
};
fn.call(obj,10,20);

複製代碼
//執行題
 function fn1(){
    console.log(1); 
};
function fn2(){
    console.log(2);
};

fn1.call(fn2); //找到callAA 把它執行,callAA中的this是fn1,第一個參數傳遞的是fn2 => 在callAA中執行的是fn1 => 1

fn1.call.call(fn2); // 找到callAA讓它執行,callAA中的this是fn1.call,第二個參數是fn2 (把fn1.call中的this變爲fn2,再讓fn1.call執行 => 先找到callAA,把它執行,只不過此時它中的this是fn2 => 讓fn2中的this變爲undefined,由於執行fn1.call的時候沒有傳遞參數值,而後讓fn2執行) => 2

Function.prototype.call(fn1); // 找到callAA讓它執行,它中的this是Function.prototype => 讓Function.prototype中的this變爲fn1,而後讓Function.prototype執行 => F.P是個匿名函數也是一個空函數,因此執行沒有任何輸出

Function.prototype.call.call(fn1); //它中的this是F.P.call => 把F.P.call中的this修改成fn1,讓F.P.call執行 => F.P.call(callAA)第二次把它執行(此時它裏面的this已是fn1 => 這一次在callAA中是讓fn1執行 => 1
複製代碼

call中的細節:

  • 1.非嚴格模式下,若是不傳參數,或者第一個參數傳的是null或者undefined,this都指向window
  • 2.在嚴格模式下,第一個參數是誰,this就指向誰(包括null和undefined),不傳this就是undefined
//非嚴格模式
let fn = function (a, b) {
    console.log(this, a, b);
};
let obj = {
    name: 'obj'
};
fn.call(obj, 10, 20);  // this:{...} a:10 b:20
fn.call(10, 20); // this:{10} a:20 b:undefined
fn.call(); // this:window a,b都是undefined
fn.call(null); // this:window a,b都是undefined
fn.call(undefined); // this:window a,b都是undefined
複製代碼
"use strict" //嚴格模式
let fn = function (a, b) {
    console.log(this, a, b);
};
let obj = {
    name: 'obj'
};
fn.call(obj, 10, 20);  // this:{...} a:10 b:20
fn.call(10, 20); // this:{10} a:20 b:undefined
fn.call(); // this:undefined a,b都是undefined
fn.call(null); // this:null a,b都是undefined
fn.call(undefined); // this:undefined a,b都是undefined
複製代碼

2、apply

  • apply:和call基本如出一轍,只有一個區別:傳參方式
    • fn.call(obj,10,20)
    • fn.apply(obj,[10,20])
  • apply 把須要傳遞給fn的參數放到一個數組或者類數組中傳遞進去,雖然寫的是一個數組,可是也至關於給fn一個個傳遞的

基於apply獲取數組中的最值

  • 需求:獲取數組中的最大值&最小值
1.先排序(從小到大)=>sort
const arr = [9, 6, 5, 60, 1, 20, 4, 16];
arr.sort(function (a, b) { //原地排序 不會返回新數組
    return a - b;
});
console.log(arr);
console.log(arr[0]); // 最小值1
console.log(arr[arr.length - 1]); // 最大值60
複製代碼
2.假設循環比較
const ary = [9, 6, 5, 60, 1, 20, 4, 16];
let min = ary[0]; // 假設第一個是最小的
let max = ary[0]; // 假設第一個是最大的
for (let i = 1; i < ary.length; i++) {
    let item = ary[i];
    item < min ? min = ary[i] : null;
    item > max ? max = ary[i] : null;
};
console.log(min);
console.log(max);
複製代碼
3.Math

獲取一堆數中的最大值&最小值, 咱們須要將要比較的數字,要以一個一個實參的形式 傳遞到Math.max的方法裏 才能正常使用javascript

    1. ES6擴展運算符 ...對象
    1. apply 利用apply傳參形式(數組或者類數組形式) apply讓Math.min執行,而且以數組的形式給傳參
    1. 數組轉換成字符串,再進行字符串拼接
    • evel 至關於小型JS解釋器:把字符串當作JS代碼執行
    • 數組和字符串拼接的時候 會默認調用自身的toString方法將本身轉換成字符串 再拼接
//ES6擴展運算符 ...對象
var ary1 = [9, 6, 5, 60, 1];
console.log(Math.min(ary1)); // NaN
console.log(Math.min(...ary1)); // 1
console.log(Math.max(...ary1)); // 60
複製代碼
//利用apply傳參形式
var ary2 = [9, 6, 5, 60, 1];
console.log(Math.min.apply(null, ary2)); // 1
console.log(Math.max.apply(null, ary2)); // 60
console.log(Math.max.apply(null, [2, 6, 9])); // 9
複製代碼
//數組轉換成字符串,再進行字符串拼接
var ary3 = [9, 6, 5, 60, 1];
// var str = ary3.toString(); //'9, 6, 5, 60, 1' 
var str2 = `Math.min(${ary3})`; //=>Math.min(9, 6, 5, 60, 1)
// var str2 = "Math.min("+ary3+")"; //=>Math.min(9, 6, 5, 60, 1)
console.log(eval(str2));
複製代碼

3、bind

  • bind:語法和call如出一轍,惟一的區別在於當即執行仍是等待執行
    • fn.call(obj,10,20); //=> 改變fn中的this,而且把fn當即執行
    • fn.bind(obj,10,20); //=> 改變fn中的this,可是此時的fn並無執行(不兼容IE6~8)
"use strict"
let fn = function (a, b) {
    console.log(this);
};
let obj = {
    name: 'obj'
};  

// document.onclick = fn; //=> 把fn綁定給點擊事件,點擊的時候執行fn,此時this:
// document.onclick = fn(); //=> 在綁定的時候,先把fn執行,把執行的返回值(此處的返回值是undefined)綁定給事件,當點擊的時候執行的是undefined

//=> 需求:點擊的時候執行fn,讓fn中的this是obj
// document.onclick = fn; //=> this:obj
// document.onclick = fn.call(obj); //=> 雖然this確實改成obj,可是綁定的時候就把fn執行了(call是當即執行函數),點擊的時候執行的是fn的返回值undefined
document.onclick = fn.bind(obj); //=> bind屬於把fn中的this預處理爲obj,此時fn沒有執行,當點擊的時候纔會把fn執行 此時this:{name: "obj"}
複製代碼
相關文章
相關標籤/搜索