在上一篇文章(《javascript高級程序設計》筆記:Function類型)中稍微提及了一下函數對象的屬性—this,在這篇文章中有深入的說明:
函數的三種簡單調用模式1 函數模式
定義的函數,如果多帶帶調用,不將其與任何對象關聯,那么就是函數調用模式
function fn(num1, num2) { console.log(this); } // 直接在全局調用 fn();// window // 在某個函數內部調用 !function(){ fn(); // window }()
函數模式this指向window
2 方法模式
定義的函數, 如果將函數作為一個對象的成員,那么利用對象調用它就是方法模式
var obj = { say: function() { console.log(this); } }; // 直接在全局調用 obj.say(); // obj // 在某個函數內部調用 !function(){ obj.say(); // obj }()
方法模式this指向調用方法的對象
3 構造器模式
定義的函數, 使用 new 來調用創建對象就是構造器模式
var Person = function(name) { this.name = name;// this指向當前實例對象 }; var p1 = new Person("xiong"); var p2 = new Person("lee");
構造器模式this指向new創建的實例對象
面試題var age = 38; var obj = { age: 18, getAge: function() { console.log(this.age); } }; var a = obj.getAge();
分析:函數getAge定義在obj對象中,屬于方法模式,其this指向調用方法的函數obj,結果為 18;
var age = 38; var obj = { age: 18, getAge: function() { function foo() { console.log(this.age); } foo(); } }; console.log(obj.getAge());
分析:obj.getAge()等同于直接執行
function foo() { console.log(this.age); }
屬于函數模式,其this指向window,結果為 38;
var age = 38; var obj = { age: 18, getAge: function() { console.log(this.age); } }; var f = obj.getAge; f();
分析:分析函數的this指向要看函數的執行環境執行的,
f = obj.getAge = function(){console.log(this.age)}
同樣的,屬于函數模式,其this指向window,結果為 38;
var length = 10; function fn(){ console.log(this.length); } var obj = { length: 5, method: function (fn) { fn();// arguments[0]();// } }; obj.method(fn, 123);
分析:坑!坑!坑!
執行fn();時,屬于函數模式,this指向為window,結果為 10;
執行arguments[0]();時,arguments[0]也是指向fn,然后fn();屬于函數模式,this指向為window,結果為 10 ?
真的是這樣嗎,我們打印出來發現結果為 2,為什么呢?
看過上一篇文章的都應該知道,arguments是函數內部的屬性,是一個偽數組(對象),索引0是其屬性
arguments: { "0": function fn(){ console.log(this.length); } }
因此:調用方式屬于方法模式,其this指向調用方法的函數obj,this.length為其實參個數,結果為 2;
借用方法調用模式上面的三種函數調用方式中,this的指向都是確定的,但是如果對象A中包含了某個方法,對象B也想調用同樣的方法,那么在不定義該方法的前提下有什么方式能夠實現方法的共用呢?借用方法調用模式能夠非常優雅的解決這個問題
1 apply()與call()方法
兩個方法均能改變函數運行時的this指向,二者的作用完全一樣,只是接受參數的方式不太一樣
apply()接受兩個參數,第一個是其中運行函數的作用域,另一個是參數數組
call()接受多個參數,第一個是其中運行函數的作用域,其他的參數都直接傳給數組
fn.call(contextObj, arg1, arg2, arg3...) fn.apply(contextObj, [arg1, arg2, aeg3...]) // 等效于 contextObj.fn(arg1,arg2,arg3)
網上有一個簡單的等效,幫助理解:
foo.call(this,arg1,arg2,arg3) == foo.apply(this, arguments)==this.foo(arg1, arg2, arg3)
下面有幾個實際應用例子:
(1)偽數組轉換成數組
// arguments // 返回值為數組,arguments保持不變 var arg = [].slice.call(arguments); // nodeList var nList = [].slice.call(document.getElementsByTagName("li"));
(2) 方法借用
var arr = [34,5,3,6,54,6,-67,5,7,6,-8,687]; // max、min 是 Math 中的靜態方法,因此必然是沒有使用上下文的必要的 Math.max.call(null, 34,5,3,6,54,6,-67,5,7,6,-8,687); Math.max.apply(null, arr);
(3) 判斷變量類型
// 判斷是否為數組 function isArray(obj){ return Object.prototype.toString.call(obj) == "[object Array]"; } isArray([]) // true isArray(123) // false
(4)實現繼承
function Animal(name){ this.name = name; this.showName = function(){ alert(this.name); } } function Cat(name){ Animal.apply(this,[name]); // call的用法 Animal.call(this,name); } var cat = new Cat("咕咕"); cat.showName();
2 bind()方法
bind()方法與上面兩個方法最大的不同是:
bind是返回對應函數,便于稍后調用;apply、call則是立即調用
bind()方法會創建一個新函數,稱為綁定函數,當調用這個綁定函數時,綁定函數會以創建它時傳入 bind()方法的第一個參數作為 this,傳入 bind() 方法的第二個以及以后的參數加上綁定函數運行時本身的參數按照順序作為原函數的參數來調用原函數。
var func = function(){ console.log(this.x); console.log(arguments); } func(); // undefined, {} var obj = { x: 2 } var bar = func.bind(obj,1); bar(); // 2 , {"0":1}
一個有意思的事:
var bar = function() { console.log(this.x); } var foo = { x: 3 } var sed = { x: 4 } var func = bar.bind(foo).bind(sed); func(); //3 var fiv = { x: 5 } var func = bar.bind(foo).bind(sed).bind(fiv); func(); //3
分析:在Javascript中,多次 bind() 是無效的。更深層次的原因, bind() 的實現,相當于使用函數在內部包了一個 call / apply ,第二次 bind() 相當于再包住第一次 bind() ,故第二次以后的 bind 是無法生效的
注意:
(1)借用方法調用模式中,如果第一個參數不是對象:number string boolean,此時,會自動的被轉化為其包裝對象
number -> new Number() string -> new String() boolean -> new Boolean()
(2)如果第一個參數為null,則this指向window(在node環境中則指向global)
say.call(null); //this 指向window say.call(window); //this同樣指向window
參考:
1 前端基礎:call,apply,bind的的理解
2 call/apply的理解與實例分享
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88466.html
下一篇:《你不知道的javascript》筆記_對象&原型 寫在前面 上一篇博客我們知道詞法作用域是由變量書寫的位置決定的,那this又是在哪里確定的呢?如何能夠精準的判斷this的指向?這篇博客會逐條闡述 書中有這樣幾句話: this是在運行時進行綁定的,并不是在編寫時綁定,它的上下文取決于函數調用時的各種條件this的綁定和函數聲明的位置沒有任何關系,只取決于函數的調用方式當一個函數被調用時...
摘要:上一篇你不知道的筆記寫在前面這是年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響年是向上的一年在新的城市穩定連續堅持健身三個月早睡早起游戲時間大大縮減,學會生活。 上一篇:《你不知道的javascript》筆記_this 寫在前面 這是2019年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響2018年是向上的一年:在新的城市穩定、...
摘要:理解元編程和是屬于元編程范疇的,能介入的對象底層操作進行的過程中,并加以影響。元編程中的元的概念可以理解為程序本身。中,便是兩個可以用來進行元編程的特性。在之后,標準引入了,從而提供比較完善的元編程能力。 導讀 幾年前 ES6 剛出來的時候接觸過 元編程(Metaprogramming)的概念,不過當時還沒有深究。今天在應用和學習中不斷接觸到這概念,比如 mobx 5 中就用到了 Pr...
摘要:忍者級別的函數操作對于什么是匿名函數,這里就不做過多介紹了。我們需要知道的是,對于而言,匿名函數是一個很重要且具有邏輯性的特性。通常,匿名函數的使用情況是創建一個供以后使用的函數。 JS 中的遞歸 遞歸, 遞歸基礎, 斐波那契數列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執行機制 本文的目的就是要保證你徹底弄懂javascript的執行機制,如果...
摘要:然而事實上并不是。函數本身也是一個對象,但是給這個對象添加屬性并不能影響。一圖勝千言作者給出的解決方案,沒有麻煩的,沒有虛偽的,沒有混淆視線的,原型鏈連接不再赤裸裸。所以是這樣的一個函數以為構造函數,為原型。 注意:本文章是個人《You Don’t Know JS》的讀書筆記。在看backbone源碼的時候看到這么一小段,看上去很小,其實忽略了也沒有太大理解的問題。但是不知道為什么,我...
閱讀 1982·2019-08-30 15:54
閱讀 3532·2019-08-30 15:52
閱讀 1822·2019-08-29 17:20
閱讀 2513·2019-08-29 17:08
閱讀 2346·2019-08-26 13:24
閱讀 780·2019-08-26 11:59
閱讀 2780·2019-08-23 14:50
閱讀 611·2019-08-23 14:20