Javascript - this and prototype

簡單介紹 Javascript 裡面的 this and prototype

在Javascript 裡面 function 有四種的調用方式,只有在function裡面才會用到 this

1.當作函式調用
2.當作方法調用
3.間接調用
4.當作建構式調用

1.當作函式調用:

最常見的function用法,定義一個function ,然後直接調用

function Demo1()
{
  console.log('Demo1');
  console.log(this);
}

Demo1();

當作函式調用的情境下,this又分為兩種模式

在非嚴格模式下,this 表示全域"跟"物件,

誰呼叫這個物件=>也就是window

上面的寫法就是會印出window

在 嚴格模式 下,this = undefined,如下面的範例:

 (function(){
   'use strict';    
    function Demo1()
    {
      console.log('Demo1');
      console.log(this);
    }
    Demo1();
 })();

2.當作方法調用:

function 當作方法調用的情境如下:

使用 object literal 的方式件建立名為 Article  的物件

在物件內定義方法,調用物件的方法,這時候的this ,代表的是 function 執行時期的所屬物件,

調用這個function的是物件=>Article

var Article = {
  id:'1',
  name:'Test Article',
  Printf:function(){
    // 在這個function 裡面,this 代表執行時期的所屬物件
    console.log(this);
    console.log(this.id);
  }
};

Article.Printf();

執行結果:

第一個console.log=> 呈現Article整個物件(1對應結果區的2)

第二個console.log=> 印出這個物件的屬性id(3對應結果區的4)

3.間接調用

只要是function ,就會有內建的call()、apply()

假設我定義了一個function:

這個function 有三個參數:x,y,z

function Demo1(x, y, z) {
  console.log(this);  
  console.log(`X value is ${x},Y value is${y},Z value is ${z}`);
}

如果是用call() 來間接調用這個function,

我可以在第一個參數傳入一個物件,function內部的this指的就是這個物件:

例如:

Demo1.call({
  id: 1,
  Name: 'Test Call'
}, 'x1', 'x2', 'x3');

我先傳入一個物件 {id:1,Name:'Test Call'},然後在依序傳入這個function 所需要的參數x,y,z

由執行結果可以看到

1的執行結果就是對應2,3的執行結果就是對應4

4.當作建構式調用:

把function 當作是一個物件,還可以透過prototype繼續擴充物件的方法或是屬性

裡面的this,在每次new的時候都會產生一個全新的物件

function Car()
{
  this.name = 'Car Name';
}

// 透過 prototype 擴充屬性ID
Car.prototype.ID = 'ID';

// 透過 prototype 擴充方法
Car.prototype.Boot = function(){
  console.log(this.ID);
  console.log(this.name);
}
var car1 = new Car();
var car2 = new Car();

// 修改 car1 物件內的屬性,不影響到car2,證明new 出來的實體不屬於同一個物件
car1.name = 'Car 1 Name';

car1.Boot();
car2.Boot();

執行結果如下:

這時候可以看一下 Car  這個Function 是看不到透過prototype擴充的屬性,

需要用 Car.prototype 才可以看到,

New出來的物件(car1、car2)則是可以使用 __propt__ 看看一共擴充了那些屬性

使用prototype去擴充方法有一個好處,就是不管你new出幾個物件,

都是共用相同的記憶體,

如果是把function 寫在Car裡面的話,則是不同的記憶體,在New的時候就重新產生全新的記憶體

可以用以下的Code測試一下

function Car()
{
  this.name = 'Car Name';

  // 把boot2 寫在Car裡面
  this.boot2 = function(){
    console.log('boot2');
  }
}

// 透過 prototype 擴充屬性ID
Car.prototype.ID = 'ID';

// 透過 prototype 擴充方法
Car.prototype.Boot = function(){
  console.log(this.ID);
  console.log(this.name);
}
var car1 = new Car();
var car2 = new Car();

console.log(car1.boot2 === car2.boot2);

console.log(car1.Boot === car2.Boot);

執行結果:

boot2 不相等,因為每次New的時候都會產生一個全新的記憶體,Javascript在比對物件是否相同的時候是比較記憶體位置

Boot 則是相等,因為擴充在ptototype的方法只會產生一次,節省記憶體空間。