1. 程式人生 > >javascript當中的this詳解

javascript當中的this詳解

  1. this是呼叫上下文,上下文被建立或者初始化時才確定
  2. 非嚴格模式:this是全域性物件;嚴格模式:this是undefined
  3. 函式呼叫

    a. 以函式形式呼叫的函式通常不使用this關鍵字,非嚴格模式下函式呼叫模式的this就是全域性物件。

    b. 以方法形式呼叫的函式的this指的是當前呼叫方法的物件

    c. 以建構函式形式呼叫的函式的this指的是被構造的物件,具體操作為:

      i. 建立一個新物件

      ii. 將建構函式的作用域賦給新物件(因此建構函式中的this就指向這個新物件)

      iii. 執行建構函式的程式碼(為這個新物件新增屬性,這個時候this才確定下來就是該物件)

      iv. 返回新物件

      v. 另外要注意的是,建構函式通常不使用return關鍵字,如果使用,則呼叫表示式的值就是return的這個物件,如果使用卻沒有指定返回值,或者返回一個原始值,那麼就忽略返回        值,同時使用構造出來的新物件作為返回結果。

  4. this是一個關鍵字,js的語法不允許給this賦值,關鍵字this沒有作用域的限制,巢狀的函式不會從呼叫它的函式中繼承this。即:如果巢狀函式作為方法呼叫,其this的值指向呼叫它的物件(與規則3相符合)

         如果巢狀函式作為函式呼叫,其this的值不是全域性物件(非嚴格模式)就是undefined(嚴格模式)。(與規則2、3符合)

例子:巢狀函式訪問外部函式的this(來自《javascript權威指南》P171)

var o={                                    //物件o
  m:function(){                          //物件中的方法m()
  var self=this;                         //將this的值儲存至一個變數中
  console.log(this===o);                 //輸出true,this就是這個物件
   f();                                  //
呼叫輔助函式f()(此時呼叫外部函式的m()時此處的f()呼叫巢狀函式就屬於函式呼叫了)   function f(){ //定義一個巢狀函式     console.log(this===o);       //"false":this的值是全域性物件或者undefined;     console.log(self===o);       //"true":self指外部函式的this值   } }

  5. 建構函式的原型模式中的this

   如果要在基本包裝型別String添加了一個名為startsWith()的方法。 

String.prototype.startsWith=function(text){
  return this.indexOf(text)==0;
}
 
var msg="hello world";
alert(msg.startsWith("Hello"));   //true

這裡的原型函式中的this也是指向呼叫者的,要知道prototype中的這些方法和屬性是所有例項物件所共享的,也就是說當新建立的物件msg以方法呼叫模式呼叫startsWith()方法,雖然在例項中沒有找到這個方法,但在原型物件中找到了這個方法,故返回的就是這個方法的值,注意,在原型中查詢值的過程是一次過程。

  6. 匿名函式中的this

  匿名函式屬於函式表示式,不能變數提升,函式表示式必須等到js引擎執行到它所在行時,才會從上而下一行一行地解析,匿名函式的執行環境具有全域性性,因此其this物件通常指向window,來看( function(){…} )()和( function (){…} () )這兩種立即執行函式的寫法,要知道在函式體後面加括號就能立即呼叫,則這個函式必須是函式表示式,不能是函式宣告。

  這樣的寫法有什麼用呢?

javascript中沒用私有作用域的概念,如果在多人開發的專案上,你在全域性或區域性作用域中聲明瞭一些變數,可能會被其他人不小心用同名的變數給覆蓋掉,根據javascript函式作用域鏈的特性,可以使用這種技術可以模仿一個私有作用域,用匿名函式作為一個“容器”,“容器”內部可以訪問外部的變數,而外部環境不能訪問“容器”內部的變數,所以( function(){…} )()內部定義的變數不會和外部的變數發生衝突,俗稱“匿名包裹器”或“名稱空間”。

例子:

  • 箭頭函式中的this
let name='windows'; 
let obj={
  name:'windows',
  stack: () => {
    console.log(this.name);   //undefine
  }
}
console.log(obj.stack());     //undefine

說明:箭頭函式表示式的語法比函式表示式更短,並且沒有自己的this,arguments,super或者new.target。這些函式表示式更適用於那些本來需要匿名函式的地方,並且它們不能用作建構函式。箭頭函式不會建立自己的this,它只會從作用鏈的上一層繼承this。

function Stack() { 
  this.age = 0; 
  setInterval(() => { 
    this.age++; // this正確地指向Stack物件 
  }, 1000); 
} 
var p = new Stack(); 
console.log(p.age);

箭頭函式不能用作構造器,和new一起會丟擲錯誤

let Foo = () => {};
let foo = new Foo(); // TypeError: Foo is not a constructor
 
let Stack=()=> {
  let name='outside';
  class Stack{
    constructor(){
      this.name=name;
    }
  }
  return Stack;
};
let b=new Stack();
console.log(b.name);//Stack is not a constructor
 
let Stack = () => {
  let name = 'outside';
  console.log(this);
  class Stack {
    constructor() {
      this.name = name;
    }
  }
  return Stack;
};
console.log(new Stack().name); //Stack is not a constructor

箭頭函式沒有prototype屬性

let Foo = () => {};
console.log(Foo.prototype); // undefined
  • 箭頭函式用於閉包
let Stack = () => {
  let name = 'outside';
  console.log(this);//Window
  class Stack {
    constructor() {
      //this.name = 'inside';
      this.name = name;
    }
  }
  return new Stack();
};
console.log(Stack().name);//outside

 

let Stack = () => {
  let name = 'outside';
  console.log(this);//Window
  class Stack {
    constructor() {
      this.name = 'inside';
    }
  }
  return Stack;
};
console.log(Stack().name);//Stack
  • 匿名函式用於閉包
let Stack = function() {
  let name = 'outside';
  console.log(this);//Window
  class Stack {
    constructor() {
      this.name = name;
    }
  }
  return Stack;
}();
let b = new Stack();
console.log(b.name);//outside
 
let Stack = function() {
  let name = 'outside';
  console.log(this);//Window
  class Stack {
    constructor() {
      this.name = name;
    }
  }
  return Stack;
};
//let b = new Stack();
let b = Stack();
let a=new b();
console.log(a.name);//outside