最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript中call、apply、bind實(shí)現(xiàn)原理詳解

 更新時(shí)間:2021年06月28日 09:22:05   作者:ClyingDeng  
其實(shí)在很多文章都會(huì)寫call,apply,bind,但個(gè)人覺著如果不弄懂原理,是很難理解透的,所以這篇文章主要介紹了JavaScript中call、apply、bind實(shí)現(xiàn)原理的相關(guān)資料,需要的朋友可以參考下

前言

眾所周知 call、apply、bind 的作用都是‘改變'作用域,但是網(wǎng)上對這這‘改變'說得含糊其辭,并未做詳細(xì)說明,‘改變'是直接替換作用域?誰替換誰?怎么產(chǎn)生效果?這些問題如果不理解清楚,就算看過手寫實(shí)現(xiàn),估計(jì)也記不長久

所以本文介紹了call、apply、bind的用法和他們各自的實(shí)現(xiàn)原理。

call

call() 方法使用一個(gè)指定的 this 值和單獨(dú)給出的一個(gè)或多個(gè)參數(shù)來調(diào)用一個(gè)函數(shù)。

即:可以改變當(dāng)前函數(shù)的this指向;還會(huì)讓當(dāng)前函數(shù)執(zhí)行。

用法

function fun() {
  console.log(this.name, arguments)
}
let obj = { name: 'clying' }
fun.call(obj, 'deng', 'deng')
// clying [Arguments] { '0': 'deng', '1': 'deng' }

實(shí)現(xiàn)

call和apply的實(shí)現(xiàn),都是使用將函數(shù)放到字面量obj的某個(gè)屬性中,使函數(shù)中的this指向obj這個(gè)字面量對象。

簡單的實(shí)現(xiàn)版本:

Function.prototype.mycall = function (context) {
  context = (context == null || context == undefined) ? window : new Object(context)
  context.fn = this
  context.fn()
  delete context.fn
}

給函數(shù)原型添加mycall方法,創(chuàng)建一個(gè)上下文對象context,如果傳入的對象不存在時(shí),將指向全局window。通過給context添加fn屬性,context的fn引用調(diào)用該方法的函數(shù)fun,并執(zhí)行fun。執(zhí)行完成之后刪除該屬性fn。

當(dāng)中需要先獲取傳入的參數(shù),那它變成字符串?dāng)?shù)組。

執(zhí)行方法使用的是eval函數(shù),再通過eval計(jì)算字符串,并執(zhí)行其中代碼,返回計(jì)算結(jié)果。

升級版:

給call中傳入?yún)?shù)。

Function.prototype.mycall = function (context) {
  context = (context == null || context == undefined) ? window : new Object(context)
  context.fn = this
  let arr = []
  for (let i = 1; i < arguments.length; i++) {
    arr.push('argument[' + i + ']') //  ["arguments[1]", "arguments[2]"]
  }
  let r = eval('context.fn(' + arr + ')') // 執(zhí)行函數(shù)fun,并傳入?yún)?shù)
  delete context.fn
  return r
}

此外,也可以通過解構(gòu)的語法來實(shí)現(xiàn)call。

Function.prototype.mycall = function (context, ...args) {
  context = (context == null || context == undefined) ? window : new Object(context)
  context.fn = this
  context.fn(...args)
  delete context.fn
}

如果想要能夠多次調(diào)用call方法,可以將context.fn(...args)保存到變量中,最后返回即可。

Function.prototype.mycall = function (context, ...args) {
  context = (context == null || context == undefined) ? window : new Object(context)
  context.fn = this
  let r = context.fn(...args)
  delete context.fn
  return r
}

apply

與call方法類似,call方法接收的是一個(gè)參數(shù)列表,而apply方法接收的是一個(gè)包含多個(gè)參數(shù)的數(shù)組。

用法

將函數(shù)中的this指向傳入的第一個(gè)參數(shù),第二個(gè)參數(shù)為數(shù)組。

function fun() {
  console.log(this.name, arguments);
}
let obj = {
  name: 'clying'
}
fun.apply(obj, [22, 1])
// clying Arguments(2) [22, 1]

實(shí)現(xiàn)

自己實(shí)現(xiàn)一個(gè)apply方法myapply。實(shí)現(xiàn)方法與call類似,不過在接收參數(shù)時(shí),可以使用一個(gè)args作為傳入的第二個(gè)參數(shù)。直接判斷如果未傳入第二個(gè)參數(shù),直接執(zhí)行函數(shù);否則使用eval執(zhí)行函數(shù)。

Function.prototype.myapply = function (context, args) {
 context = (context == null || context == undefined) ? window : new Object(context)
  context.fn = this
  if(!args) return context.fn()
  let r = eval('context.fn('+args+')')
  delete context.fn
  return r
}

bind

bind() 方法創(chuàng)建一個(gè)新的函數(shù),不自動(dòng)執(zhí)行,需要手動(dòng)調(diào)用bind() 。這個(gè)新函數(shù)的 this 被指定為 bind() 的第一個(gè)參數(shù),而其余參數(shù)將作為新函數(shù)的參數(shù),供調(diào)用時(shí)使用。

用法

將obj綁定到fun函數(shù)的this上,函數(shù)fun可以使用obj內(nèi)部的屬性,和傳入的變量。

function fun() {
  console.log(this.name, arguments);
}
let obj = {
  name: 'clying'
}
let b = fun.bind(obj,2)
b(3)
// clying Arguments(2) [2, 3]

此外,bind方法綁定的函數(shù)還可以new一個(gè)實(shí)例,不過此時(shí)的this會(huì)發(fā)生改變。

升級版-使用原型屬性用法:

function fun() {
  console.log(this.name, arguments);
}
let obj = {
  name: 'clying'
}
fun.prototype.age = 23
let b = fun.bind(obj, 3)
let instance = new b(4)
console.log(instance.age);
//undefined Arguments(2) [3, 4]
// 23

實(shí)現(xiàn)

基本版:

bind的實(shí)現(xiàn)可以基于call和apply的基礎(chǔ)上實(shí)現(xiàn)。

因?yàn)閎ind不是立即執(zhí)行的,所以可以通過返回一個(gè)函數(shù),讓用戶手動(dòng)執(zhí)行。在返回函數(shù)中利用call或者apply傳入指定的this對象和參數(shù)。

apply實(shí)現(xiàn)bind

Function.prototype.mybind = function (context) {
  let that = this
  let bindargs = Array.prototype.slice.call(arguments, 1)
  return function () {
    let args = Array.prototype.slice.call(arguments)
    return that.apply(context, bindargs.concat(args))
  }
}

利用apply方法,主要是在獲取處理bind傳入的參數(shù),以及用戶執(zhí)行函數(shù)傳入的參數(shù)。利用Array原型方法的slice方法,截取所需的參數(shù)。

在獲取bind傳入的參數(shù)時(shí),需要從第二個(gè)參數(shù)開始截取,所以開始位置為1。

call實(shí)現(xiàn)bind

Function.prototype.mybind = function (context, ...args1) {
  let that = this
  return function (...args2) {
    return that.call(context, ...args1, ...args2)
  }
}

call實(shí)現(xiàn)直接將參數(shù)拼接call方法的后面即可。

升級版:

bind除了可以改變this指向、用戶可以在bind后面?zhèn)魅雲(yún)?shù)也可以在用戶執(zhí)行時(shí)傳入?yún)?shù)外。還可以讓執(zhí)行函數(shù)進(jìn)行new操作。

當(dāng)一個(gè)綁定函數(shù)是用來構(gòu)建一個(gè)值的,原來提供的 this 就會(huì)被忽略。不過提供的參數(shù)列表仍然會(huì)插入到構(gòu)造函數(shù)調(diào)用時(shí)的參數(shù)列表之前。

apply

Function.prototype.mybind = function (context) {
  let that = this
  let bindargs = Array.prototype.slice.call(arguments, 1)
  function fBind() {
    let args = Array.prototype.slice.call(arguments)
    // 如果使用的是new,那么this會(huì)指向fBind實(shí)例,this作為當(dāng)前實(shí)例傳入 不是的話,使用context上下文對象
    return that.apply(this instanceof fBind ? this : context, bindargs.concat(args))
  }
  return fBind
}

在使用new操作符時(shí),注意的是需要改變this的指向問題,如果是new,那么this指向的是實(shí)例,不使用new則指向bind當(dāng)前傳入的第一個(gè)參數(shù)。

此外,還牽扯到原函數(shù)可以添加自身方法屬性。如果想要能夠使用fun自身的原型方法還需要使用fBind.prototype = this.prototype,實(shí)現(xiàn)原型共用。但是對于引用類型屬性值共享,不能在不改變其他實(shí)例情況下改變(一個(gè)原型方法或?qū)傩愿淖儯幸玫亩紩?huì)發(fā)生改變)。

Function.prototype.mybind = function (context) {
  let that = this
  let args = Array.prototype.slice.call(arguments, 1)
  function fBind() { // 執(zhí)行bind函數(shù)
    let bindargs = Array.prototype.slice.call(arguments)
    return that.apply(this instanceof fBind ? this : context, args.concat(bindargs))
  }
  function Fn(){} // 兩個(gè)類的原型并未公用,而是通過原型鏈的方式找到該原型方法
  Fn.prototype = this.prototype
  fBind.prototype = new Fn()
  return fBind
}

對于上述情況,可以使用一個(gè)函數(shù)中間件的形式,利用原型鏈去找到原函數(shù)原型方法或?qū)傩浴?/p>

call

call與apply的差別只是處理參數(shù)的不同,其他均類似。

Function.prototype.mybind = function (context, ...args1) {
  let that = this
  function fBind(...args2) {
    return that.call(this instanceof fBind ? this : context, ...args1, ...args2)
  }
  function Fn() { }
  Fn.prototype = this.prototype
  fBind.prototype = new Fn()
  return fBind
}

總結(jié)

到此這篇關(guān)于JavaScript中call、apply、bind實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)call、apply、bind原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

湾仔区| 天柱县| 肇源县| 朝阳区| 西宁市| 通榆县| 伽师县| 阿坝县| 崇州市| 江阴市| 华阴市| 万载县| 大庆市| 厦门市| 吐鲁番市| 安新县| 通辽市| 故城县| 儋州市| 荔浦县| 曲阳县| 辰溪县| 宁波市| 墨脱县| 思南县| 乐东| 克拉玛依市| 九龙城区| 峨山| 娄烦县| 永吉县| 嵩明县| 莫力| 海宁市| 广灵县| 福安市| 三江| 南宁市| 友谊县| 白银市| 天门市|