【面試須要】掌握JavaScript中的this,call,apply的原理

做者 |  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...

最後

  • 歡迎加我微信(xiaoda0423),拉你進技術羣,長期交流學習...
  • 歡迎關注「達達前端」,認真學前端,作個有專業的技術人...

在博客平臺裏,將來的路還很長,也但願本身之後的文章你們能多多支持,多多批評指正,咱們一塊兒進步,一塊兒走花路。

很是感謝讀者能看到這裏,若是這個文章寫得還不錯,以爲「達達」我有點東西的話,以爲我可以堅持的學習,以爲此人能夠交朋友的話, 求點贊???? 求關注❤️ 求分享???? 對暖男我來講真的

很是有用!

2019年的每一天日更只爲等待她的出現,好好過餘生,慶餘年 | 掘金年度徵文

一篇文章帶你瞭解JavaScript中的變量,做用域和內存問題

一篇文章帶你瞭解JavaScript中的語法,數據類型,流程控制語句以及函數

一篇常作錯的經典JS閉包面試題

感謝閱讀,原創不易,喜歡就點個[在看] or [轉發朋友圈],這是我寫做最大的動力。

意見反饋

若本號內容有作得不到位的地方(好比:涉及版權或其餘問題),請及時聯繫咱們進行整改便可,會在第一時間進行處理。

這是一個有質量,有態度的公衆號

點關注,有好運

相關文章
相關標籤/搜索