日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

定義

函數的 this 關鍵字在 JAVAScript 中的表現略有不同,此外,在嚴格模式和非嚴格模式之間也會有一些差別

在絕大多數情況下,函數的調用方式決定了 this 的值(運行時綁定)

this 關鍵字是函數運行時自動生成的一個內部對象,只能在函數內部使用,總指向調用它的對象

舉個例子:

function baz() {
    // 當前調用棧是:baz
    // 因此,當前調用位置是全局作用域
    
    console.log( "baz" );
    bar(); // <-- bar的調用位置
}

function bar() {
    // 當前調用棧是:baz --> bar
    // 因此,當前調用位置在baz中
    
    console.log( "bar" );
    foo(); // <-- foo的調用位置
}

function foo() {
    // 當前調用棧是:baz --> bar --> foo
    // 因此,當前調用位置在bar中
    
    console.log( "foo" );
}

baz(); // <-- baz的調用位置

同時,this在函數執行過程中,this一旦被確定了,就不可以再更改

var a = 10;
var obj = {
  a: 20
}

function fn() {
  this = obj; // 修改this,運行后會報錯
  console.log(this.a);
}

fn();

綁定規則

根據不同的使用場合,this有不同的值,主要分為下面幾種情況:

  • 默認綁定
  • 隱式綁定
  • new綁定
  • 顯示綁定

默認綁定

全局環境中定義person函數,內部使用this關鍵字

var name = 'Jenny';
function person() {
    return this.name;
}
console.log(person());  //Jenny

上述代碼輸出Jenny,原因是調用函數的對象在游覽器中位window,因此this指向window,所以輸出Jenny

注意:

嚴格模式下,不能將全局對象用于默認綁定,this會綁定到undefined,只有函數運行在非嚴格模式下,默認綁定才能綁定到全局對象

隱式綁定

函數還可以作為某個對象的方法調用,這時this就指這個上級對象

function test() {
  console.log(this.x);
}

var obj = {};
obj.x = 1;
obj.m = test;

obj.m(); // 1

這個函數中包含多個對象,盡管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象

var o = {
    a:10,
    b:{
        fn:function(){
            console.log(this.a); //undefined
        }
    }
}
o.b.fn();

上述代碼中,this的上一級對象為b,b內部并沒有a變量的定義,所以輸出undefined

這里再舉一種特殊情況

var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //undefined
            console.log(this); //window
        }
    }
}
var j = o.b.fn;
j();

此時this指向的是window,這里的大家需要記住,this永遠指向的是最后調用它的對象,雖然fn是對象b的方法,但是fn賦值給j時候并沒有執行,所以最終指向window

new綁定

通過構建函數new關鍵字生成一個實例對象,此時this指向這個實例對象

function test() {
 this.x = 1;
}

var obj = new test();
obj.x // 1

上述代碼之所以能過輸出1,是因為new關鍵字改變了this的指向

這里再列舉一些特殊情況:

new過程遇到return一個對象,此時this指向為返回的對象

function fn()  
{  
    this.user = 'xxx';  
    return {};  
}
var a = new fn();  
console.log(a.user); //undefined

如果返回一個簡單類型的時候,則this指向實例對象

function fn()  
{  
    this.user = 'xxx';  
    return 1;
}
var a = new fn;  
console.log(a.user); //xxx

注意的是null雖然也是對象,但是此時new仍然指向實例對象

function fn()  
{  
    this.user = 'xxx';  
    return null;
}
var a = new fn;  
console.log(a.user); //xxx

顯示修改

Apply()、call()、bind()是函數的一個方法,作用是改變函數的調用對象。它的第一個參數就表示改變后的調用這個函數的對象。因此,這時this指的就是這第一個參數

var x = 0;
function test() {
 console.log(this.x);
}

var obj = {};
obj.x = 1;
obj.m = test;
obj.m.apply(obj) // 1

關于apply、call、bind三者的區別,我們后面再詳細說

箭頭函數

在 ES6 的語法中還提供了箭頭函語法,讓我們在代碼書寫時就能確定 this 的指向(編譯時綁定)

舉個例子:

const obj = {
  sayThis: () => {
    console.log(this);
  }
};

obj.sayThis(); // window 因為 JavaScript 沒有塊作用域,所以在定義 sayThis 的時候,里面的 this 就綁到 window 上去了
const globalSay = obj.sayThis;
globalSay(); // window 瀏覽器中的 global 對象

雖然箭頭函數的this能夠在編譯的時候就確定了this的指向,但也需要注意一些潛在的坑

下面舉個例子:

綁定事件監聽

const button = document.getElementById('mngb');
button.addEventListener('click', ()=> {
    console.log(this === window) // true
    this.innerhtml = 'clicked button'
})

上述可以看到,我們其實是想要this為點擊的button,但此時this指向了window

包括在原型上添加方法時候,此時this指向window

Cat.prototype.sayName = () => {
    console.log(this === window) //true
    return this.name
}
const cat = new Cat('mm');
cat.sayName()

同樣的,箭頭函數不能作為構建函數

優先級

隱式綁定 VS 顯式綁定

function foo() {
    console.log( this.a );
}

var obj1 = {
    a: 2,
    foo: foo
};

var obj2 = {
    a: 3,
    foo: foo
};

obj1.foo(); // 2
obj2.foo(); // 3

obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2

顯然,顯示綁定的優先級更高

new綁定 VS 隱式綁定

function foo(something) {
    this.a = something;
}

var obj1 = {
    foo: foo
};

var obj2 = {};

obj1.foo( 2 );
console.log( obj1.a ); // 2

obj1.foo.call( obj2, 3 );
console.log( obj2.a ); // 3

var bar = new obj1.foo( 4 );
console.log( obj1.a ); // 2
console.log( bar.a ); // 4

可以看到,new綁定的優先級>隱式綁定

new綁定 VS 顯式綁定

因為new和apply、call無法一起使用,但硬綁定也是顯式綁定的一種,可以替換測試

function foo(something) {
    this.a = something;
}

var obj1 = {};

var bar = foo.bind( obj1 );
bar( 2 );
console.log( obj1.a ); // 2

var baz = new bar( 3 );
console.log( obj1.a ); // 2
console.log( baz.a ); // 3

bar被綁定到obj1上,但是new bar(3) 并沒有像我們預計的那樣把obj1.a修改為3。但是,new修改了綁定調用bar()中的this

我們可認為new綁定優先級>顯式綁定

綜上,new綁定優先級 > 顯示綁定優先級 > 隱式綁定優先級 > 默認綁定優先級

執行上下文

簡單的來說,執行上下文是一種對Javascript代碼執行環境的抽象概念,也就是說只要有Javascript代碼運行,那么它就一定是運行在執行上下文中

執行上下文的類型分為三種:

  • 全局執行上下文:只有一個,瀏覽器中的全局對象就是 window對象,this 指向這個全局對象
  • 函數執行上下文:存在無數個,只有在函數被調用的時候才會被創建,每次調用函數都會創建一個新的執行上下文
  • Eval 函數執行上下文: 指的是運行在 eval 函數中的代碼,很少用而且不建議使用

下面給出全局上下文和函數上下文的例子:

this對象的理解及JavaScript中執行上下文和執行棧是什么?

 

紫色框住的部分為全局上下文,藍色和橘色框起來的是不同的函數上下文。只有全局上下文(的變量)能被其他任何上下文訪問

可以有任意多個函數上下文,每次調用函數創建一個新的上下文,會創建一個私有作用域,函數內部聲明的任何變量都不能在當前函數作用域外部直接訪問

生命周期

執行上下文的生命周期包括三個階段:創建階段 → 執行階段 → 回收階段

創建階段

創建階段即當函數被調用,但未執行任何其內部代碼之前

創建階段做了三件事:

  • 確定 this 的值,也被稱為 This Binding
  • LexicalEnvironment(詞法環境) 組件被創建
  • VariableEnvironment(變量環境) 組件被創建

偽代碼如下:

ExecutionContext = {  
  ThisBinding = <this value>,     // 確定this 
  LexicalEnvironment = { ... },   // 詞法環境
  VariableEnvironment = { ... },  // 變量環境
}

This Binding

確定this的值我們前面講到,this的值是在執行的時候才能確認,定義的時候不能確認

詞法環境

詞法環境有兩個組成部分:

  • 全局環境:是一個沒有外部環境的詞法環境,其外部環境引用為null,有一個全局對象,this 的值指向這個全局對象
  • 函數環境:用戶在函數中定義的變量被存儲在環境記錄中,包含了arguments 對象,外部環境的引用可以是全局環境,也可以是包含內部函數的外部函數環境

偽代碼如下:

GlobalExectionContext = {  // 全局執行上下文
  LexicalEnvironment: {       // 詞法環境
    EnvironmentRecord: {     // 環境記錄
      Type: "Object",           // 全局環境
      // 標識符綁定在這里 
      outer: <null>           // 對外部環境的引用
  }  
}

FunctionExectionContext = { // 函數執行上下文
  LexicalEnvironment: {     // 詞法環境
    EnvironmentRecord: {    // 環境記錄
      Type: "Declarative",      // 函數環境
      // 標識符綁定在這里      // 對外部環境的引用
      outer: <Global or outer function environment reference>  
  }  
}

變量環境

變量環境也是一個詞法環境,因此它具有上面定義的詞法環境的所有屬性

在 ES6 中,詞法環境和變量環境的區別在于前者用于存儲函數聲明和變量( let 和 const )綁定,而后者僅用于存儲變量( var )綁定

舉個例子

let a = 20;  
const b = 30;  
var c;

function multiply(e, f) {  
 var g = 20;  
 return e * f * g;  
}

c = multiply(20, 30);

執行上下文如下:

GlobalExectionContext = {

  ThisBinding: <Global Object>,

  LexicalEnvironment: {  // 詞法環境
    EnvironmentRecord: {  
      Type: "Object",  
      // 標識符綁定在這里  
      a: < uninitialized >,  
      b: < uninitialized >,  
      multiply: < func >  
    }  
    outer: <null>  
  },

  VariableEnvironment: {  // 變量環境
    EnvironmentRecord: {  
      Type: "Object",  
      // 標識符綁定在這里  
      c: undefined,  
    }  
    outer: <null>  
  }  
}

FunctionExectionContext = {  
   
  ThisBinding: <Global Object>,

  LexicalEnvironment: {  
    EnvironmentRecord: {  
      Type: "Declarative",  
      // 標識符綁定在這里  
      Arguments: {0: 20, 1: 30, length: 2},  
    },  
    outer: <GlobalLexicalEnvironment>  
  },

  VariableEnvironment: {  
    EnvironmentRecord: {  
      Type: "Declarative",  
      // 標識符綁定在這里  
      g: undefined  
    },  
    outer: <GlobalLexicalEnvironment>  
  }  
}

留意上面的代碼,let和const定義的變量a和b在創建階段沒有被賦值,但var聲明的變量從在創建階段被賦值為undefined

這是因為,創建階段,會在代碼中掃描變量和函數聲明,然后將函數聲明存儲在環境中

但變量會被初始化為undefined(var聲明的情況下)和保持uninitialized(未初始化狀態)(使用let和const聲明的情況下)

這就是變量提升的實際原因

執行階段

在這階段,執行變量賦值、代碼執行

如果 Javascript 引擎在源代碼中聲明的實際位置找不到變量的值,那么將為其分配 undefined 值

回收階段

執行上下文出棧等待虛擬機回收執行上下文

執行棧

執行棧,也叫調用棧,具有 LIFO(后進先出)結構,用于存儲在代碼執行期間創建的所有執行上下文

this對象的理解及JavaScript中執行上下文和執行棧是什么?

 

當Javascript引擎開始執行你第一行腳本代碼的時候,它就會創建一個全局執行上下文然后將它壓到執行棧中

每當引擎碰到一個函數的時候,它就會創建一個函數執行上下文,然后將這個執行上下文壓到執行棧中

引擎會執行位于執行棧棧頂的執行上下文(一般是函數執行上下文),當該函數執行結束后,對應的執行上下文就會被彈出,然后控制流程到達執行棧的下一個執行上下文

舉個例子:

let a = 'Hello World!';
function first() {
  console.log('Inside first function');
  second();
  console.log('Again inside first function');
}
function second() {
  console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');

轉化成圖的形式

this對象的理解及JavaScript中執行上下文和執行棧是什么?

 

簡單分析一下流程:

  • 創建全局上下文請壓入執行棧
  • first函數被調用,創建函數執行上下文并壓入棧
  • 執行first函數過程遇到second函數,再創建一個函數執行上下文并壓入棧
  • second函數執行完畢,對應的函數執行上下文被推出執行棧,執行下一個執行上下文first函數
  • first函數執行完畢,對應的函數執行上下文也被推出棧中,然后執行全局上下文
  • 所有代碼執行完畢,全局上下文也會被推出棧中,程序結束

分享到:
標簽:對象
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定