亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

apply和call

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

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 ,但是通過(guò) call 方法,將函數(shù)綁定到了 obj 上,成為obj的一個(gè)函數(shù),同時(shí)里面的 this 也指向了obj

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

手寫(xiě)call

原理:

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

  // 在上下文對(duì)象上綁定當(dāng)前調(diào)用的函數(shù),作為屬性方法
  // 不能直接調(diào)用this方法函數(shù),原因在于如果不將這個(gè)方法綁定到上下文對(duì)象上
  // 直接執(zhí)行this函數(shù),this函數(shù)里面的this上下文對(duì)象無(wú)法識(shí)別為綁定的對(duì)象
  let fn = Symbol('key')
  context[fn] = this
  const result = context[fn](...args)
  // 刪除這個(gè)函數(shù),避免對(duì)上下文對(duì)象造成影響
  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

手寫(xiě)apply

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

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

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

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

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

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

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

  return result;
};

// 測(cè)試一下
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

最后來(lái)看看 bind,和前面兩者主要的區(qū)別是,通過(guò) bind 綁定的不會(huì)立即調(diào)用,而是返回一個(gè)新函數(shù),然后需要手動(dòng)調(diào)用這個(gè)新函數(shù),來(lái)實(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)	// 通過(guò)綁定創(chuàng)建一個(gè)新函數(shù),然后再調(diào)用新函數(shù)
fn()

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

let sum = (x, y) => x + y
let succ = sum.bind(null, 1) // 綁定時(shí)沒(méi)有指定對(duì)象,但是給函數(shù)的第一個(gè)參數(shù)指定為1
succ(2) // 3, 調(diào)用時(shí)只傳遞了一個(gè)參數(shù)2,會(huì)直接對(duì)應(yīng)到y(tǒng),因?yàn)榍懊娴?已經(jīng)綁定到x上了

手寫(xiě)bind

原理:

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

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

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

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

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

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

相關(guān)文章

最新評(píng)論