關於call, apply, bind方法的區別與內部實現
關於這三個方法,很多大神已經總結的很到位了,寫這篇文章只是想加深自己的理解。
一開始,我是在看設計模式這本書的時候,看到如下程式碼(new運算的過程):
// new運算的過程
/**
* 1、建立一個空物件;
* 2、該空物件的原型指向建構函式(連結原型):將建構函式的 prototype 賦值給物件的 __proto__屬性;
* 3、繫結 this:將物件作為建構函式的 this 傳進去,並執行該建構函式;
* 4、返回新物件:如果建構函式返回的是一個物件,則返回該物件;否則(若沒有返回值或者返回基本型別),返回第一步中新建立的物件;
*/
var Person = function(name) {
this.name = name
console.log('name is ', this.name)
}
Person.prototype.getName = function() {
return this.name
}
var objectFactory = function() {
// 1、建立一個空物件
var obj = new Object()
console.log('before shift arguments = ',arguments)
獲取建構函式
Constructor = [].shift.call(arguments)
console.log('after shift arguments = ', arguments)
console.log(`Constructor = ${Constructor}`)
// 2、該空物件的原型指向建構函式: 將建構函式的prototype 賦值給空物件的 __proto__屬性;
obj.__proto__ = Constructor.prototype
// 3、將空物件作為建構函式的this傳進去,並執行該建構函式
var ret = Constructor.apply(obj, arguments)
// 4、返回新物件:如果建構函式返回的是一個物件,則返回該物件;否則(若沒有返回值或者返回基本型別),返回第一步中新建立的物件;
return typeof ret == 'object' ? ret : obj
}
var a = objectFactory(Person, 'yandong')
console.log('執行後的name = ', a.name)
查詢一番資料之後,才明白過來。
原來,arguments是類陣列,並不是真正的陣列,所以不能直接呼叫陣列的shiftf方法,但是可以通過call呼叫。
call方法,表示傳入的物件引數呼叫call前面物件的方法,並且被呼叫的函式會被執行
,call方法的引數是當前上下文的物件以及引數列表
apply也是如此,只不過它傳入的引數是物件和引數陣列
而bind,用法與apply, call一樣,但是它被物件繫結的函式不會被執行
,而是返回這個函式
,需要你手動去呼叫返回的函式,才會返回結果。
那我們來看看這句程式碼:Constructor = [].shift.call(arguments)
意思就是:arguments物件呼叫陣列的shift()方法。
而shift()方法會刪除並返回陣列的第一個元素
當我們執行objectFactory(Person, 'anne')的時候
跳轉到objectFactory函式內部,arguments這個類陣列會全部接收Person引數以及‘anne’引數
我們可以看到,arguments類陣列物件的值。
所以
Constructor = [].shift.call(arguments)
這句程式碼刪除並且返回的就是傳入call方法或者apply方法的第一個物件引數,也就是Person。執行了shift之後,arguments就只剩下執行函式所需的引數列表或者引數陣列了。如下圖:
以上就是通過new返回一個物件例項的過程。
call, apply, bind呼叫的區別
// call, apply, bind的區別
var a = {value: 1}
function getValue(name, age) {
console.log('arguments in fn = ', arguments)
console.log(name, age)
console.log(this.value)
}
getValue.call(a,'yandong1', 17)
let bindFoo = getValue.bind(a, 'testBind', 45)
console.log('bindFoo = ',bindFoo)
bindFoo()
getValue.apply(a,['yandong2', 18])
var returnedFunc = getValue.bind(a,'yandong3', 19)
console.log(returnedFunc)
returnedFunc()
執行結果:
我們可以看到,call, apply都是直接返回函式執行後的結果,而bind是返回一個函式,之後手動執行之後才會將結果返回。
手動實現call方法
// 手寫模擬call方法的思想
/**
* call方法思想:改變this指向,讓新的物件可以執行這個方法
* 實現思路:
* 1、給新的物件新增一個函式(方法),並讓this(也就是當前繫結的函式)指向這個函式
* 2、執行這個函式
* 3、執行完以後刪除這個方法
* 4、可以將執行結果返回
*/
Function.prototype.myCall = function(funcCtx) {
// funcCtx是當前要呼叫函式的物件
console.log('funcCtx = ',funcCtx)
// this指被呼叫的函式
console.log('this = ',this)
if(typeof this != 'function') {
throw new TypeError('Erorr')
}
let ctx = funcCtx || global
console.log('arguemnets = ', arguments)
let args = [...arguments].slice(1)
console.log(`args = ${args}`)
ctx.fn = this // 為當前物件新增一個函式fn, 值為要已經定義的要呼叫的函式
console.log('ctx.fn = ', ctx.fn)
// 執行新增的函式fn
var result = ctx.fn(...args)
// 執行完以後刪除
delete ctx.fn
return result
}
getValue.myCall(a,'test', 20)
執行結果:
手動實現apply方法
與call方法的思想類似,只不過它需要判斷一下引數陣列是否存在
// apply
Function.prototype.myApply = function(funcCtx) {
console.log(this)
if(typeof this != 'function') {
throw new TypeError('Erorr')
}
let ctx = funcCtx || global
ctx.fn = this
console.log('arguemnets = ', arguments)
let result
if(arguments[1]) {
result = ctx.fn(...arguments[1])
} else {
result = ctx.fn()
}
delete ctx.fn
return result
}
getValue.myApply(a, ['eo', 50])
手動實現bind方法
//bind實現
/**
* 實現思想:
* 1、返回一個函式,其他與call, apply類似
* 2、如果返回的函式作為建構函式,bind時指定的 this 值會失效,但傳入的引數依然生效。
*/
Function.prototype.myBind = function(funcCtx) {
let ctx = funcCtx || global
console.log(this)
let _this = this
let args = [...arguments].slice(1)
// 作為建構函式使用
let Fbind = function() {
let self = this instanceof Fbind ? this : ctx
return _this.apply(self,args.concat(...arguments))
}
let f = function() {}
f.prototype = this.prototype
Fbind.prototype = new f()
return Fbind
}
var value = 2
var foo = {
value: 1
}
function bar(name, age) {
this.habbit = 'shopping'
console.log('bar this.value = ', this.value)
console.log(name, age)
}
bar.prototype.friend = 'shuaige'
var bindFoo = bar.myBind(foo, 'testbind',111)
// 返回的函式直接呼叫
bindFoo()
執行結果
// 當 bind 返回的函式作為建構函式的時候,bind 時指定的 this 值會失效,但傳入的引數依然生效。
var obj = new bindFoo('18')
console.log('obj = ', obj)
console.log(obj.friend)
console.log(obj.habbit)
執行結果
相關文章
- bind、call、apply的區別與實現原理APP
- bind、call、apply區別?如何實現?APP
- call、apply、bind 區別APP
- call apply bind區別APP
- call,apply和bind的區別APP
- javascript -- apply/call/bind的區別JavaScriptAPP
- apply call bind的用法與實現APP
- JavaScript中apply、call、bind的區別與用法JavaScriptAPP
- JS每日一題: Call,Apply,Bind的使用與區別,如何實現一個bind?JS每日一題APP
- js中call、apply、bind的區別JSAPP
- js apply/call/caller/callee/bind使用方法與區別分析JSAPP
- js call、apply、bind的實現JSAPP
- this與new、call、apply、bind的關係APP
- apply 、call 以及 bind 的使用和區別APP
- 【JavaScript】深入理解call,以及與apply、bind的區別JavaScriptAPP
- call,apply,bind,new實現原理APP
- 模擬實現apply/call/bindAPP
- bind,call,apply模擬實現APP
- this指向與call,apply,bindAPP
- js中call,apply和bind方法的區別和使用場景JSAPP
- JavaScript方法call,apply,caller,callee,bind的使用詳解及區別JavaScriptAPP
- JavaScript之call, apply, bind, new的實現JavaScriptAPP
- JS中的call、apply、bind方法JSAPP
- 也談如何實現bind、apply、callAPP
- JavaScript進階教程(4)-函式內this指向解惑call(),apply(),bind()的區別JavaScript函式APP
- 【筆記】apply, call 與 bind筆記APP
- this、apply、call、bindAPP
- call apply bind的作用及區別? 應用場景?APP
- 讓你弄懂 call、apply、bind的應用和區別APP
- 理解JS中的call、apply、bind方法(********************************************************JSAPP
- js深入之實現call、apply和bindJSAPP
- JavaScript自我實現系列(2):call,apply,bindJavaScriptAPP
- JS中改變this的指向 call、apply 和 bind 的區別JSAPP
- apply,call,bind的用法APP
- 「乾貨」細說 call、apply 以及 bind 的區別和用法APP
- 詳解 new/bind/apply/call 的模擬實現APP
- js之call,apply和bind的模擬實現JSAPP
- JavaScript 之 call和apply,bind 的模擬實現JavaScriptAPP