call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數。
這是MDN上關於call的描述,用通俗的語言解釋一下就是:javascript
可能這樣解釋仍是有些模糊,能夠看一個例子:java
function Test(name, age) { console.log(this.name); this.name = name; this.age = age; } var man = { name: 'Wei' }; Test.call(man, 'Willem', 18); // Wei console.log(man.name); // Willem console.log(man.age); // 18
在Test
函數中輸出this.name
以前實際上是沒有name屬性的,可是在執行Test.call(man, 'Willem', 18);
以後卻輸出了Wei;在man對象
中也是沒有age
屬性而且name
屬性的值爲Wei,輸出的值倒是Willem。說明在Test函數執行的過程當中this
的值指向了man對象
,而且能夠經過call函數向Test函數傳遞參數。es6
這一通分析下來無非就是一句話:用於函數的call函數能夠指定this並傳入多個參數。
數組
在開始實現call函數以前,能夠思考下如何修改this的指向,其實很簡單,直接把目標函數掛載到須要修改的上下文。相似於這種:app
var obj = { name: 'Willem', sayHi: function() { console.log(this.name); } }; obj.sayHi(); /* Willem */
那代碼如何實現就很清晰了啊。函數
Function.prototype.wcall = function(context) { context = context || window; // 不傳入this時,默認是window context.__fn__ = this; var args = []; for (var i = 1, len = arguments.length; i < len; i++) { args.push('"'+ arguments[i] +'"'); } var ret = eval('context.__fn__(' + args + ')'); delete context.__fn__; return ret; }
上述代碼中,爲了執行函數因此採用了eval,固然es6的擴展運算符也能夠實現相同的功能。對eval
或者js的類型轉換不太熟悉的童鞋可能會對eval那段代碼會有些疑惑。this
var args = ['a', 'b', 'c']; eval('context.__fn__(' + args + ')'); // 上面的代碼其實是兩句 // 這實際上要說到js的隱式類型轉換了,這裏就不過多贅述了 // 只說一點:'string ' + [1, 2] 字符串和數組使用 +, 會將數組轉換爲一個字符串,和join方法的效果差很少 // 因此'string' + [1, 2] === 'string 1,2' var str = 'context.__fn__(' + args + ')'; // === 'context.__fn__(a,b,c)' // eval()則是會將傳入的字符串當作JavaScript代碼進行執行 // eval(str) 就至關因而直接執行 context.__fn__(a,b,c) // 能夠發現a, b, c其實只是參數字符串而不是變量 // 因此在最開始咱們須要將args的參數都是用字符串進行包裹,wcall代碼中:args.push('"'+ arguments[i] +'"') // args = ["'a'", "'b'", "'c'"]; // 'context.__fn__(' + args + ')' === 'context.__fn__("a","b","c")' // 就至關於在直接執行 context.__fn__("a","b","c") eval(str);
以上就是關於call的實現。prototype
apply() 方法調用一個具備給定this值的函數,以及做爲一個數組(或相似數組對象)提供的參數。
原理啥的就很少說了,apply和call的做用是同樣的,區別是apply是將參數做爲一個數組傳入,而call是將參數一個一個的傳入。直接上代碼:code
Function.prototype.wapply = function(context, args) { context = context || window; context.__fn__ = this; var ret; if (!args || !(args instanceof Array) || args.length === 0) { ret = context.__fn__(); } else { var arr = []; for (var i = 0; i < args.length; i++) { arr.push('"' + args[i] + '"'); } ret = eval('context.__fn__(' + arr + ')'); } return ret; }
以上就是關於apply的實現。對象