日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

JavaScript apply、call、bind 函数详解

發布時間:2024/4/6 java 47 coder
生活随笔 收集整理的這篇文章主要介紹了 JavaScript apply、call、bind 函数详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

apply和call

apply和call非常類似,都是用于改變函數中this的指向,只是傳入的參數不同,等于間接調用一個函數,也等于將這個函數綁定到一個指定的對象上:

let name = 'window'

function getName(param1, param2) {
  console.log(this.name)
  console.log(param1, param2)
}
let obj = {
  name: 'easylee',
}
getName.call(obj, 123, 23)
getName.apply(obj, [123, 23])

如上面的例子,如果直接調用 getName 那么返回的是 window ,但是通過 call 方法,將函數綁定到了 obj 上,成為obj的一個函數,同時里面的 this 也指向了obj

兩者主要的區別在于,當函數有多個參數時,call 是直接傳入多個參數,而 apply 將多個參數組合成一個數組傳輸參數

手寫call

原理:

  1. 首先,通過 Function.prototype.myCall 將自定義的 myCall 方法添加到所有函數的原型對象上,使得所有函數實例都可以調用該方法。
  2. myCall 方法內部,首先通過 typeof this !== "function" 判斷調用 myCall 的對象是否為函數。如果不是函數,則拋出一個類型錯誤。
  3. 然后,判斷是否傳入了上下文對象 context。如果沒有傳入,則將 context 賦值為全局對象。這里使用了一種判斷全局對象的方法,先判斷是否存在 global 對象,如果存在則使用 global,否則判斷是否存在 window 對象,如果存在則使用 window,如果都不存在則將 context 賦值為 undefined
  4. 接下來,使用 Symbol 創建一個唯一的鍵 fn,用于將調用 myCall 的函數綁定到上下文對象的新屬性上。
  5. 將調用 myCall 的函數賦值給上下文對象的 fn 屬性,實現了將函數綁定到上下文對象上的效果。
  6. 調用綁定在上下文對象上的函數,并傳入 myCall 方法的其他參數 args
  7. 將綁定在上下文對象上的函數刪除,以避免對上下文對象造成影響。
  8. 返回函數調用的結果。
Function.prototype.myCall = function (context, ...args) {
  // 判斷調用myCall的是否為函數
  if (typeof this !== 'function') {
    throw new TypeError('Function.prototype.myCall - 被調用的對象必須是函數')
  }
  // 判斷是否傳入上下文對象,不傳入則指定默認全局對象
  context = context || (typeof global !== 'undefined' ? gloabl : typeof window !== 'undefined' ? window : undefined)

  // 在上下文對象上綁定當前調用的函數,作為屬性方法
  // 不能直接調用this方法函數,原因在于如果不將這個方法綁定到上下文對象上
  // 直接執行this函數,this函數里面的this上下文對象無法識別為綁定的對象
  let fn = Symbol('key')
  context[fn] = this
  const result = context[fn](...args)
  // 刪除這個函數,避免對上下文對象造成影響
  delete context[fn]
  return result
}
const test = {
  name: 'xxx',
  hello: function () {
    console.log(`hello,${this.name}!`)
  },
  add: function (a, b) {
    return a + b
  },
}
const obj = { name: 'world' }
test.hello.myCall(obj) //hello,world!
test.hello.call(obj) //hello,world!
console.log(test.add.myCall(null, 1, 2)) //3
console.log(test.add.call(null, 1, 2)) //3

手寫apply

Function.prototype.myApply = function (context, argsArr) {
  // 判斷調用myApply的是否為函數
  if (typeof this !== "function") {
    throw new TypeError("Function.prototype.myApply - 被調用的對象必須是函數");
  }

  // 判斷傳入的參數是否為數組
  if (argsArr && !Array.isArray(argsArr)) {
    throw new TypeError("Function.prototype.myApply - 第二個參數必須是數組");
  }

  // 如果沒有傳入上下文對象,則默認為全局對象
  //global:nodejs的全局對象
  //window:瀏覽器的全局對象
  context =
    context ||
    (typeof global !== "undefined"
      ? global
      : typeof window !== "undefined"
      ? window
      : undefined);

  // 用Symbol來創建唯一的fn,防止名字沖突
  let fn = Symbol("key");

  // this是調用myApply的函數,將函數綁定到上下文對象的新屬性上
  context[fn] = this;

  // 傳入myApply的多個參數
  const result = Array.isArray(argsArr)
    ? context[fn](...argsArr)
    : context[fn]();

  // 將增加的fn方法刪除
  delete context[fn];

  return result;
};

// 測試一下
const test = {
  name: "xxx",
  hello: function () {
    console.log(`hello,${this.name}!`);
  },
};
const obj = { name: "world" };
test.hello.myApply(obj); //hello,world!
test.hello.apply(obj); //hello,world!
const arr = [2,3,6,5,1,7,9,5,0]
console.log(Math.max.myApply(null,arr));//9
console.log(Math.max.apply(null,arr));//9

bind

最后來看看 bind,和前面兩者主要的區別是,通過 bind 綁定的不會立即調用,而是返回一個新函數,然后需要手動調用這個新函數,來實現函數內部 this 的綁定

let name = 'window'
function getName(param1, param2) {
  console.log(this.name)
  console.log(param1)
  console.log(param2)
}
let obj = {
  name: 'easylee',
}

let fn = getName.bind(obj, 123, 234)	// 通過綁定創建一個新函數,然后再調用新函數
fn()

除此之外, bind 還支持柯里化,也就是綁定時傳入的參數將保留到調用時直接使用

let sum = (x, y) => x + y
let succ = sum.bind(null, 1) // 綁定時沒有指定對象,但是給函數的第一個參數指定為1
succ(2) // 3, 調用時只傳遞了一個參數2,會直接對應到y,因為前面的1已經綁定到x上了

手寫bind

原理:

  1. 首先,通過 Function.prototype.myBind 將自定義的 myBind 方法添加到所有函數的原型對象上,使得所有函數實例都可以調用該方法。
  2. myBind 方法內部,首先通過 typeof this !== "function" 判斷調用 myBind 的對象是否為函數。如果不是函數,則拋出一個類型錯誤。
  3. 然后,判斷是否傳入了上下文對象 context。如果沒有傳入,則將 context 賦值為全局對象。這里使用了一種判斷全局對象的方法,先判斷是否存在 global 對象,如果存在則使用 global,否則判斷是否存在 window 對象,如果存在則使用 window,如果都不存在則將 context 賦值為 undefined
  4. 保存原始函數的引用,使用 _this 變量來表示。
  5. 返回一個新的閉包函數 fn 作為綁定函數。這個函數接受任意數量的參數 innerArgs。(關于閉包的介紹可以看這篇文章->閉包的應用場景)
  6. 在返回的函數 fn 中,首先判斷是否通過 new 關鍵字調用了函數。這里需要注意一點,如果返回出去的函數被當作構造函數使用,即使用 new 關鍵字調用時,this 的值會指向新創建的實例對象。通過檢查 this instanceof fn,可以判斷返回出去的函數是否被作為構造函數調用。這里使用 new _this(...args, ...innerArgs) 來創建新對象。
  7. 如果不是通過 new 調用的,就使用 apply 方法將原始函數 _this 綁定到指定的上下文對象 context 上。這里使用 apply 方法的目的是將參數數組 args.concat(innerArgs) 作為參數傳遞給原始函數。
Function.prototype.myBind = function (context, ...args) {
  // 判斷調用myBind的是否為函數
  if (typeof this !== "function") {
    throw new TypeError("Function.prototype.myBind - 被調用的對象必須是函數");
  }

  // 如果沒有傳入上下文對象,則默認為全局對象
  //global:nodejs的全局對象
  //window:瀏覽器的全局對象
 context =
    context || (typeof global !== "undefined"
      ? global
      : typeof window !== "undefined"
      ? window
      : undefined);

  // 保存原始函數的引用,this就是要綁定的函數
  const _this = this;

  // 返回一個新的函數作為綁定函數
  return function fn(...innerArgs) {
    // 判斷返回出去的函數有沒有被new
    if (this instanceof fn) {
      return new _this(...args, ...innerArgs);
    }
    // 使用apply方法將原函數綁定到指定的上下文對象上
    return _this.apply(context,args.concat(innerArgs));
  };
};

// 測試
const test = {
  name: "xxx",
  hello: function (a,b,c) {
    console.log(`hello,${this.name}!`,a+b+c);
  },
};
const obj = { name: "world" };
let hello1 = test.hello.myBind(obj,1);
let hello2 = test.hello.bind(obj,1); 
hello1(2,3)//hello,world! 6
hello2(2,3)//hello,world! 6
console.log(new hello1(2,3));
//hello,undefined! 6
// hello {}
console.log(new hello2(2,3));
//hello,undefined! 6
// hello {}

總結一下,這三個函數都是用于改變函數內 this 對象的指向,只是使用方式有不同,其中 apply 傳遞多個參數使用數組的形式,call 則直接傳遞多個參數,而 bind 則可以將綁定時傳遞的參數保留到調用時直接使用,支持柯里化,同時 bind 不會直接調用,綁定之后返回一個新函數,然后通過調用新函數再執行。

總結

以上是生活随笔為你收集整理的JavaScript apply、call、bind 函数详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。