欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

玩轉(zhuǎn)JavaScript函數(shù):apply/call/bind技巧

 更新時間:2024年01月10日 08:53:31   作者:easylee  
歡迎來到這篇關(guān)于JavaScript中apply、call、bind函數(shù)的指南,這里充滿了實用技巧和深入理解,讓你的編程之旅更加游刃有余,趕快翻開這個神秘的“魔法書”,讓我們一起探索吧!

apply和call

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

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])

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

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

手寫call

原理:

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

  // 在上下文對象上綁定當前調(diào)用的函數(shù),作為屬性方法
  // 不能直接調(diào)用this方法函數(shù),原因在于如果不將這個方法綁定到上下文對象上
  // 直接執(zhí)行this函數(shù),this函數(shù)里面的this上下文對象無法識別為綁定的對象
  let fn = Symbol('key')
  context[fn] = this
  const result = context[fn](...args)
  // 刪除這個函數(shù),避免對上下文對象造成影響
  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) {
  // 判斷調(diào)用myApply的是否為函數(shù)
  if (typeof this !== "function") {
    throw new TypeError("Function.prototype.myApply - 被調(diào)用的對象必須是函數(shù)");
  }

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

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

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

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

  // 傳入myApply的多個參數(shù)
  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,和前面兩者主要的區(qū)別是,通過 bind 綁定的不會立即調(diào)用,而是返回一個新函數(shù),然后需要手動調(diào)用這個新函數(shù),來實現(xiàn)函數(shù)內(nèi)部 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)	// 通過綁定創(chuàng)建一個新函數(shù),然后再調(diào)用新函數(shù)
fn()

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

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

手寫bind

原理:

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

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

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

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

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

到此這篇關(guān)于玩轉(zhuǎn)JavaScript函數(shù):apply/call/bind技巧的文章就介紹到這了,更多相關(guān)JavaScript apply、call、bind 函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • js生成隨機數(shù)的過程解析

    js生成隨機數(shù)的過程解析

    這篇文章主要介紹了js生成隨機數(shù)的過程,如何生成[n,m]的隨機整數(shù),感興趣的小伙伴們可以參考一下
    2015-11-11
  • javaScript canvas實現(xiàn)(畫筆大小 顏色 橡皮的實例)

    javaScript canvas實現(xiàn)(畫筆大小 顏色 橡皮的實例)

    下面小編就為大家分享一篇javaScript canvas實現(xiàn)(畫筆大小 顏色 橡皮的實例),具有很好的參考價值,希望對大家有所幫助
    2017-11-11
  • js實現(xiàn)特定位取反原理及示例

    js實現(xiàn)特定位取反原理及示例

    循環(huán)輸入每組兩個數(shù)hex和n(0<=n<31),hex是一個16進制的數(shù)字,我們要做的是將hex的第n位取反,然后以16進制的形式輸出對應的結(jié)果
    2014-06-06
  • 使用JavaScript執(zhí)行表單驗證的方法

    使用JavaScript執(zhí)行表單驗證的方法

    在Web前端開發(fā)中,表單驗證是一個常見的任務,用于確保用戶輸入的數(shù)據(jù)符合預期的要求,通過使用JavaScript,可以實現(xiàn)強大的表單驗證功能,提升用戶體驗和數(shù)據(jù)的可靠性,本文將詳細介紹如何使用JavaScript執(zhí)行表單驗證,包括基本概念、作用說明、實現(xiàn)方法和實際開發(fā)中的使用技巧
    2024-11-11
  • js前端實現(xiàn)圖片懶加載(lazyload)的兩種方式

    js前端實現(xiàn)圖片懶加載(lazyload)的兩種方式

    本篇文章主要介紹了js前端實現(xiàn)圖片懶加載(lazyload)的兩種方式 ,使用圖片懶加載可以提高網(wǎng)頁運行速度,有興趣的可以了解一下。
    2017-04-04
  • 《javascript設計模式》學習筆記三:Javascript面向?qū)ο蟪绦蛟O計單例模式原理與實現(xiàn)方法分析

    《javascript設計模式》學習筆記三:Javascript面向?qū)ο蟪绦蛟O計單例模式原理與實現(xiàn)方法分析

    這篇文章主要介紹了Javascript面向?qū)ο蟪绦蛟O計單例模式原理與實現(xiàn)方法,結(jié)合實例形式分析了《javascript設計模式》中Javascript面向?qū)ο髥卫J较嚓P(guān)概念、原理、用法及操作注意事項,需要的朋友可以參考下
    2020-04-04
  • JavaScript 大數(shù)據(jù)相加的問題

    JavaScript 大數(shù)據(jù)相加的問題

    寫一個函數(shù)處理大數(shù)據(jù)的相加問題,所謂的大數(shù)據(jù)是指超出了整型,長整型之類的常規(guī)數(shù)據(jù)類型表示范圍的數(shù)據(jù)。實現(xiàn)語言不限。
    2011-08-08
  • 淺談pc端rem字體設置的問題

    淺談pc端rem字體設置的問題

    下面小編就為大家?guī)硪黄獪\談pc端rem字體設置的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • webpack3里使用uglifyjs壓縮js時打包報錯的解決

    webpack3里使用uglifyjs壓縮js時打包報錯的解決

    這篇文章主要介紹了webpack3里使用uglifyjs壓縮js時打包報錯的解決,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • el-upload實現(xiàn)上傳文件并展示進度條功能

    el-upload實現(xiàn)上傳文件并展示進度條功能

    這篇文章主要介紹了el-upload實現(xiàn)上傳文件并展示進度條,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05

最新評論