做者 | Jesksonjavascript
掘金 | https://juejin.im/user/5a16e1...css
2020年01月10日html
前言,爲何要學習在掌握JavaScript中的this,call,apply,由於面試官會問啊!因此咱們 必須掌握才能答啊!那麼this是什麼,Function.prototype.call和前端
Function.prototype.apply這兩個方法又是如何使用在JavaScript中的呢。java
學習掌握this是必須的,咱們經常在編寫JavaScript中的代碼時,會經常使用到它。面試
this的指針做用域,在全局環境中執行this,表示Global對象,在瀏覽器中表示window對象;當經過new運算符來調用函數時,函數被當作爲一個構造函數,this的指向構造函數建立出來的對象;當在函數的執行環境中使用this時,狀況有些不一樣,如函數沒有做爲一個非window對象的屬性,那麼只是定義了在這個函數,無論這個函數是否是定義在另外一個函數中,其函數中的this仍表示爲window對象;若是函數表示做爲一個非window對象的屬性,則表示函數中的this就表明爲 這個對象。sql
如截圖狀況下,在全局執行環境中使用this,表示Global對象,在瀏覽器中表示爲window對象。數組
function A(){ //在A函數中定義一個B函數 function B(){ console.log(this); //Window console.log(typeof this); //object console.log(this === window); //true } //在A函數內部調用B函數 B(); } //調用A函數 A();
在函數執行環境中使用this時,若是函數沒有明顯的做爲非window對象的屬性,而只是定義了函數,無論這個函數是否是定義在另外一個函數中,這個函數中的this仍然表示window對象。瀏覽器
//定義一個對象obj,添加屬性name,添加方法objFun var obj = { name: 'dada', objFun: function(){ console.log(this); // Object {name: "dada"} console.log(typeof this); //object console.log(this === window); //false console.log(this.name); //dada } }; //調用obj對象的方法 obj.objFun(); //this 綁定到當前對象,也就是obj對象
//定義一個對象obj,添加屬性name,添加方法objFun var obj = { name: 'dada', objFun: function(){ console.log(this); //window console.log(typeof this); //object console.log(this === window); //true console.log('dada的,名字'+this.name+'大帥哥'); } }; var test = obj.objFun; test();
能夠看出函數內部中this值不是靜態的,是動態的,能夠改變的,每次調用一個函數時,它老是在從新求值。函數內部中的this值,其實是由函數被調用的父做用域提供,依賴實際函數的語法。微信
// 第一種狀況 //調用obj對象的方法 obj.objFun(); //this 綁定到當前對象,也就是obj對象 // 第二種狀況 var test = obj.objFun; test();
var test = obj.objFun // 這裏沒有調用函數 test() // 這裏調用了函數 // test不是一個對象的引用,因此this值表明全局對象。
當經過new運算符來調用函數時,函數被當作一個構造函數,this指向構造函數建立出來的對象。
new建立出來了一個構造函數,這個時候this的值,指向構造函數建立出來的對象。
var name = 'dada'; function A(){ console.log(this.name); } A(); // dada var B = new A(); //undefined (由於B並無name屬性) VM162:3 dada VM162:3 undefined undefined
var name = 'windowdada'; var obj = { name: 'objdada', objB: { name: 'objBdada', bFun: function(){ console.log(this.name); } } }; var test = obj.objB.bFun(); var test2 = obj.objB.bFun; test2(); var test3 = obj.objB; var test4 = test3.bFun; test4();
注意()的近鄰的左邊,若是這個的左邊是一個引用,那麼傳遞給調用函數的this值爲引用所屬的這個對象,不然this指向爲全局對象。
var test = obj.objB.bFun(); // ()左邊是bFun引用,它指向objB這個對象,全部打印出objBdada
var test2 = obj.objB.bFun; test2(); // ()的左邊爲test2,它不是某個對象的引用,因此是全局對象 // 打印出 objBdada
var test4 = test3.bFun; test4(); // 同理這個也是
JavaScript中this的原理
var name = 'windowDada'; var obj = { name: 'dada', foo: function () { console.log(this.name); } }; var foo = obj.foo; // 寫法一 obj.foo() // 寫法二 foo() VM593:5 dada VM593:5 windowDada
這個時候我相信你已經看懂了。this指向的是函數運行時所在的環境,對於obj.foo()來講,foo運行在obj環境中,因此這個時候的this指向爲obj這個對象,對於foo()來講,foo運行是全局環境,這個this的指向爲全局環境。(你會問爲何呢?一個指向obj這個對象,一個運行環境爲全局環境,這裏能夠運用()左邊方法)
對呀爲何呢?函數的運行環境是怎麼決定在哪一種狀況的?
爲何obj.foo()的環境就在obj這個環境中,而做爲
var foo = obj.foo,foo()的運行環境就變成了全局的執行環境呢?
this的指向設計,跟內存的數據結構有關。
var obj = { name: 'dada' };
當一個對象賦值給一個變量obj的時候,JavaScript引擎會在內存裏,先生成一個對象爲 { name: 'dada' },而後才把這個對象的內存地址賦值給這個變量 obj。
咱們說過了不少不少遍了,都知道這個變量obj就是一個地址,這個時候若是要讀 obj.foo,那麼引擎就會從這個變量 obj中拿內存地址,而後再從這個地址 讀取原始對象,返回它的foo屬性。
注意:原始的對象(開始建立的對象 { name: 'dada' })以字典結構保存的,每一個屬性名都對應一個屬性描述對象。foo屬性的值保存在屬性描述對象的value屬性裏面。
this指包含它的函數做爲方法被調用時所屬的對象。
this,第一包,含它的函數,第二,做爲方法被調用時,第三,所屬的對象。
function da(){ console.log(this); //window } da();
會調用我,我就是誰的,誰最後調用我,我就是誰的。
testFunda()函數是在全局中被window對象所調用的,this的指向爲window對象,而nameda變量在testFunda函數中,window對象中沒有這個變量,因此打印不出來。
注意()的左邊爲testFunda
而testFunda()函數是在全局中被window對象所調用的哦!
所以this的指向就是window對象哦!
var namedada = 'dada' function testFundada () { var namedada="hello dada!"; console.log(this.namedada); } testFundada(); VM717:4 dada
看這個代碼固然打印出的是dada啦,由於從全局調用,全局中有這個屬性,就打印這個屬性。
this被包含中一個函數中,可是這個函數被另個函數包含的狀況下,這個this的指向爲頂部的函數。
var obj={ a:"da", b:function(){ var a="dada"; console.log(this.a); } }; obj.b(); VM726:5 da
this被包含在函數b()中,由於是被obj對象所調用的,因此這個this屬於這個obj對象,打印出來的就是da這個字符串了。
誰最後調用我,我就屬於誰!
var obj = { a: 1, b:{ fn:function(){ console.log(this.a); //undefined } } }; obj.b.fn(); VM730:5 undefined
對象obj是在window上定義的,因此以下顯示:
obj.b.fn()=window.obj.b.fn()
誰先調用我不算,誰最後調用我纔算,window,那麼this不是指向全局的對象了嗎,可是最後的是被fn()調用,()左邊爲b對象,因此this就指向這個b對象了,由於函數中沒有這個變量,因此爲undefined。
出一道考題
結果是啥?我知道爲2,你知道嗎?那看看執行結果吧!
var obj = { name: 1, b:{ name: 2, fn:function(){ var name = 3 console.log(this.name); } } }; obj.b.fn();
函數狀況,屬性的值爲一個函數
var obj = { foo: function () {} };
在JavaScript引擎中會將函數單獨保存在內存中,再將函數的地址賦值給foo屬性的value屬性。
{ foo: { [[value]]: 函數的地址 ... } }
var f = function () {}; var obj = { f: f }; // 單獨執行 f() // obj 環境執行 obj.f()
var fda = function () { console.log('da'); }; var objDada = { f: fda }; // 單獨執行 fda() // objDada 環境執行 objDada.fda() VM858:2 da
環境的考慮,在JavaScript中運行在函數體內部,引用當前環境的其餘變量。在JavaScript中,因爲函數能夠在不一樣的運行環境執行,就要一種機制,使可以在函數體內部獲取當前的運行環境。
this的出現,目的在於就是指代函數當前的運行環境。
this 指代全局對象
function test(){ this.x = 1; alert(this.x); } test(); // 1
this 指代上級對象
function test(){ alert(this.x); } var o = {}; o.x = 1; o.m = test; o.m(); // 1
this 指代 new 出的對象
var x = 3; function test(){ this.x = 1; } var o = new test(); alert(x); // 3 alert(o.x); // 1
函數的不一樣使用場合,this 有不一樣的值,this是函數運行時所在的環境對象。
call的用法
call(thisObj,arg1,arg2,arg...)
調用一個對象的方法,以另外一個對象替換當前對象,call方法用來代替另外一個對象調用一個方法,該方法能夠將一個函數對象的上下文改變爲由this obj指定的新對象。
call方法的參數,若是是不傳,或是null,undefined的狀況下,函數中的this指向就是指window對象,若是傳遞的是另外一個函數的函數名,函數中的this指向就是這個函數的引用,若是參數傳遞的是基本類型數據時,函數中的this指向就是其對應的 包裝對象了,若是參數傳遞的是一個對象時,函數中的this就指向這個對象。
一個函數的函數名,函數名是引用,因此指向是指這個函數的引用
一個對象,因此this就指向這個對象
基本類型數據,就指向這個包裝對象
Function.prototype.call(thisArg[,arg1[,arg2, ...]])
當以thisArg和可選擇的arg1,arg2等等做爲參數在一個func對象上調用call方法。
var da = { name: "da", sayHello: function (age) { console.log("hello, i am ", this.name + " " + age + " years old"); } }; var jeskson = { name: "jeskson", }; da.sayHello(12); VM891:4 hello, i am da 12 years old undefined da.sayHello.call(jeskson,13); VM891:4 hello, i am jeskson 13 years old undefined
在JavaScript中,call和apply做用是同樣的
爲了改變某個函數運行時的上下文(context)而存在的,就是爲了改變函數體內部this的指向。
每一個函數都包含兩個非繼承而來的方法:
call()和apply()
apply的用法
apply(thisObj,argArray)
apply方法應用於某一個對象的一個方法
用另外一個對象替換當前對象。
區別:參數書寫方式不一樣
call() 方法分別接受參數。
apply() 方法接受數組形式的參數。
Math.max(1,2,3); // 會返回 3 Math.max.apply(null, [1,2,3]); // 也會返回 3 Math.max.apply(Math, [1,2,3]); // 也會返回 3 Math.max.apply(" ", [1,2,3]); // 也會返回 3 Math.max.apply(0, [1,2,3]); // 也會返回 3
call(thisObj, arg1, arg2, arg3, arg4); apply(thisObj, [args]); thisObj: call和apply第一個參數是同樣的 該參數將替代Function類裏面的this對象 arg1,arg2.... 是一個個的參數 args 一個數組或類數組,是一個參數列表
JavaScript 嚴格模式
若是 apply() 方法的第一個參數不是對象,它將成爲被調用函數的全部者(對象)。
在「非嚴格」模式下,它成爲全局對象。
參考:
http://www.ruanyifeng.com/blo...
最後
在博客平臺裏,將來的路還很長,也但願本身之後的文章你們能多多支持,多多批評指正,咱們一塊兒進步,一塊兒走花路。
很是感謝讀者能看到這裏,若是這個文章寫得還不錯,以爲「達達」我有點東西的話,以爲我可以堅持的學習,以爲此人能夠交朋友的話, 求點贊???? 求關注❤️ 求分享???? 對暖男我來講真的
很是有用!!!
推薦閱讀:
2019年的每一天日更只爲等待她的出現,好好過餘生,慶餘年 | 掘金年度徵文
一篇文章帶你瞭解JavaScript中的變量,做用域和內存問題
一篇文章帶你瞭解JavaScript中的語法,數據類型,流程控制語句以及函數
感謝閱讀,原創不易,喜歡就點個[在看] or [轉發朋友圈],這是我寫做最大的動力。
意見反饋
若本號內容有作得不到位的地方(好比:涉及版權或其餘問題),請及時聯繫咱們進行整改便可,會在第一時間進行處理。
這是一個有質量,有態度的公衆號
點關注,有好運