<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
call和apply:函數調動call()方法在執行的時候,函數的裡面的this會指向第一個引數值,除第一個引數值後面的若干支都是傳進該函數,簡而言之就是改變函數執行時的this指向。
使用範例: fn.call(obj, args1, args2...), fn.apply(obj, [arg1, arg2 ...]), call與apply呼叫除了第二個傳參方式不一樣,其餘一樣。
舉個例子1:
var obj = { name: '程式設計師米粉' }; function fn() { console.log(this.name) } fn() // this.name => undefined fn.call(obj) // this.name => '程式設計師米粉'
總結:
1、fn函數調動call方法執行時候,函數的this指向了obj
2、call方法和fn函數都執行了。
如果還看不明白,fn.call(obj)執行的時候,可以看作在obj物件裡面有個fn函數執行。那麼fn函數裡面的this指向就是obj
再舉個例子2:
var obj = { name: '程式設計師米粉', fn: function() { console.log(this.name) } }; obj.fn(); // this.name => '程式設計師米粉'
總結:
1、例子1中的 fn.call(obj)執行的時候,相當於例子2 obj.fn(); call方法被呼叫的時候,會改變當前呼叫函數的執行上下文(就是改變this指向)。
例子1:
var obj = { name: '程式設計師米粉' }; function fn() { console.log(this.name) } fn.call(obj) // this.name => '程式設計師米粉'
例子2:
var obj = { name: '程式設計師米粉', fn: function() { console.log(this.name) } }; obj.fn(); // this.name => '程式設計師米粉'
要實現自己的myCall方法,首先觀察例子1和例子2中測 obj物件,有什麼不一樣。
不一樣:
1、例子2中obj新增了fn函數方法。
2、例子2執行方法是obj.fn()執行,而例子1中執行方法是 fn.call(obj)。
這就好辦了,我們如何把例子1改造成例子2呢?
我們總結一下步驟:
1、把fn函數設定為物件的一個屬性
2、呼叫fn函數執行。
3、呼叫完畢之後,從物件中刪除fn屬性(函數)。 (給物件增加了屬性,我們呼叫完就可以刪掉)
思路有了,那程式碼我們可以寫成這樣:
```js obj.fn = fn; obj.fn(); delete obj.fn; // 刪除屬性 ```
根據上述思路,那按照這個思路,寫一個我們自己的myCall方法:
```js Function.prototype.myCall = function(context) { context.fn = this; context.fn(); delete context.fn; }; var obj = { name: '程式設計師米粉' }; function fn() { console.log(this.name); } fn.myCall(obj) // this.name => '程式設計師米粉' ````
執行一下myCall方法,可以得出來,this指向obj物件,並列印了期望的值,所以這個方法第一步完成了!
繼續完善一下myCall方法,把fn方法改一下,增加2個引數, 再執行一下方法。
function fn(index, value) { console.log(this.name); console.log(index, value); } fn.myCall(obj, 111, '我是一個值'); // this.name => '程式設計師米粉' // undefined, undefined
執行一個fn函數,只列印一個值出來,傳進入的引數,沒有列印出來。那需要改造一下方法,使引數也能列印出來。我們可以從arguments物件中取值,arguments對像代表函數傳進來的物件,列印看看就知道了。
Function.prototype.myCall = function(context) { console.log(arguments); context.fn = this; context.fn(); delete context.fn; };
列印出來的 arguments物件:
我們可以看到 arguments 物件結構是:
{ '0': { name: '程式設計師米粉' }, '1': 111, '2': '我是一個值' }
0代表傳進來的第1個引數,1代表第2個引數,以此類推。我們看到arguments是一個類陣列,那就可以用陣列方法,儲存起來, 由於我們只是獲取引數,所以從1開始取值。
var args = []; for (var i = 1; i < arguments.length; i++) { args.push('arguments[' + i + ']'); } // args為 ["arguments[1]", "arguments[2]", "arguments[3]"]
取值的引數的問題解決了,我們下一步解決一下,如何把這些引數傳給函數執行。
// 思考一下,怎樣才能這樣傳參給fn函數執行 context.fn(arguments[1], arguments[2]);
可能有人想到如下方法:
// 把陣列裡的元素通過join方法,傳進函數的形參裡 context.fn(args.join(',')) // 這樣的話,其實 context.fn(args.join(','))執行變成:context.fn("arguments[1]", "arguments[2]"); // 變成了一個字串,變成了一個死值了,無論傳什麼,都變成字串 "arguments[1]", "arguments[2]" 。。。。
可以有人又想到用ES6方法來解決。這個call是ES3的方法,所以還是不用ES6方法解決。
其實我們可以用 eval方法來解決。查檔案得知 eval() 函數會將傳入的字串當做 JavaScript 程式碼進行執行。這到底是什麼意思?
eval() 的引數是一個字串。如果字串表示的是表示式,eval() 會對錶示式進行求值。如果參數列示一個或多個 JavaScript 語句,那麼eval() 就會執行這些語句。
舉個例子:
console.log(eval('2 + 2')); // 2 + 2 => 4, 最後輸出是2 console.log(eval('context.fn(' + args +')')) // 相當於執行 context.fn(arguments[1], arguments[2], ...),使用eval執行一串 JavaScript 語句。
args 會自動呼叫 Array.toString() 這個方法。通過eval自動執行變成context.fn(arguments[1], arguments[2])這樣執行。
程式碼如下:
var obj = { name: '程式設計師米粉' }; function fn(index, value) { console.log(this.name); // 程式設計師米粉 console.log(index, value); // 111 我是一個值 } Function.prototype.myCall = function(context) { context.fn = this; var args = []; for (var i = 1; i < arguments.length; i++) { args.push('arguments[' + i + ']'); } eval('context.fn(' + args + ')'); delete context.fn; }; fn.myCall(obj, 111, '我是一個值'); // this.name => '程式設計師米粉'
執行一下方法,輸入完全符合我們的預期,我們終於搞定了!
舉個例子:
var name = '程式設計師米粉' function fn() { console.log(this.name); // 程式設計師米粉 } fn.call(null); // this.name => '程式設計師米粉'
fn執行的時候,還是輸出 this.name = '程式設計師米粉',說明了什麼,只要第一個引數傳 null或者undefined,那函數呼叫call方法,this指向window
舉個例子:
var name = '程式設計師米粉' function fn() { console.log(this.name); // 程式設計師米粉 return { name: this.name } } fn.call(null); // this.name => '程式設計師米粉' // 執行fn函數直接返回物件 // { // name: '程式設計師米粉' // }
var obj = { name: '程式設計師米粉' }; function fn(index, value) { console.log(this.name); // 程式設計師米粉 console.log(index, value); // 111 我是一個值 return { name: this.name, index: index, value: value }; } Function.prototype.myCall = function(context) { var context = context || window; context.fn = this; var args = []; for (var i = 1; i < arguments.length; i++) { args.push('arguments[' + i + ']'); } var result = eval('context.fn(' + args + ')'); delete context.fn; return result; }; fn.myCall(obj, 111, '我是一個值'); // this.name => '程式設計師米粉' // 最終輸出 // { // name: "程式設計師米粉" // index: 111 // value: "我是一個值" // }
由於apply與call實現原理基本一樣,那就不詳細敘述了,直接上程式碼:
Function.prototype.myApply = function(context, arr) { var context = context || window; context.fn = this; var result; if (!arr) { result = context.fn(); } else { var args = []; for (var i = 0; i < arr.length; i++) { args.push('arr[' + i + ']'); } result = eval('context.fn(' + args + ')'); } delete context.fn; return result; };
希望看完這篇文章對你有幫助:
到此這篇關於JavaScript深入理解系列之call與apply的文章就介紹到這了,更多相關js call與apply內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45