javascript之模擬call以及apply實現

Alex丶Cheng發表於2019-02-15

call

使用一個指定的上下文物件和若干個指定的引數值的前提下呼叫某個函式或方法。如果傳入null, 則this指向window

  • 注意兩點:

  1. 改變方法內部的this指向
  2. 執行方法
  • 實現步驟一

  1. 將函式設為物件的方法
  2. 執行該方法
  3. 刪除該方法
Function.prototype.myCall = function(context) {
    context.fn = this // this代表sayValue方法
    context.fn() // 執行sayValue方法
    delete context.fn // 刪除物件上的方法
}

var obj = {
    value: 'hello javascript'
}

function sayValue() {
    console.log(this.value)
}

sayValue.myCall(obj)

複製程式碼
  • 實現步驟二 (傳入引數)

  1. 通過函式的arguments物件,來獲取不定引數
Function.prototype.myCall = function (ctx) {
    ctx.fn = this
    // 將arguments轉化成陣列,通過slice()方法擷取陣列除第一項以外的所有項,並返回擷取的陣列
    var newArr = Array.prototype.slice.call(arguments, 1)
    ctx.fn(...newArr) // ES6中的 '...' 展開運算子,可以展開陣列
    delete ctx.fn
}
var obj = {
    value: 'hello, obj'
}
function saySth(name, age) {
    console.log('name: ', name)
    console.log('age: ', age)
    console.log('obj.value: ', this.value)
}

saySth.myCall(obj, 'alex.cheng', 18)

// 執行試試 !
複製程式碼
  • 實現步驟三

  1. 函式的返回值,看以下栗子:
var obj = {}
obj.value = 'alex.cheng'
function bar() {
	return {
		value: this.value
	}
}
bar.call(obj)

>>> {value: "alex.cheng"}
複製程式碼
  1. 最終實現
Function.prototype.myCall = function (ctx) {
    var ctx = ctx || window // 如果傳入的ctx是null的話,就將ctx指向window
    ctx.fn = this
    // 將arguments轉化成陣列,通過slice()方法擷取陣列除第一項以外的所有項,並返回擷取的陣列
    var newArr = Array.prototype.slice.call(arguments, 1)
    var result = ctx.fn(...newArr) // ES6中的 '...' 展開運算子,可以展開陣列
    delete ctx.fn
    return result // 返回ctx.fn的返回值
}

var value = 'hey, this is a global_value .' // 定義一個全域性變數

var obj = {
    value: 'hello, obj.value'
}
function saySth(name, age) {
    return {
        name,
        age,
        value: this.value
    }
}

saySth.myCall(obj, 'alex.cheng', 18) // 傳入obj,saySth函式的this指向obj
>>> {name: "alex.cheng", age: 18, value: "hello, obj.value"}

saySth.myCall(null, 'alex.cheng', 18) // 傳入null,saySth函式的this指向window
{name: "haolun", age: 18, value: "hey, this is a global_value ."}


// 自己動手試一試 @.@
複製程式碼

apply

apply的用法和call的用法一樣,唯一的區別,就是傳入的引數不同,apply需要傳入陣列,例如 fn.apply( null, [ ] )

  1. 模擬實現也和call 相似,實現如下 :
Function.prototype.myApply = function (ctx) {
	var ctx = ctx || window
	ctx.fn = this
	var result
	var args = arguments[1] // 獲取傳入的陣列
	if (!args) { // 如果沒有傳入陣列引數,則直接呼叫ctx.fn方法
		result = ctx.fn()
	} else {
		result = ctx.fn(...args) // 如果傳入了陣列引數,將陣列引數採用ES6的'...'擴充套件運算子將其展開並傳入ctx.fn方法
	}
	delete ctx.fn
	return result
}

// 親自試一試 :)
Math.max.myApply(null, [1,2,3,4,55,44,33,22,11])
複製程式碼

相關文章