javascript:建構函式模式(為什麼這種方式也可以建立物件)

南郭竽發表於2018-04-22

ECMAScript中建構函式可以用來建立特定型別的物件。像ObjectArray這樣的原生建構函式,在執行時會自動出現在執行環境中。此外,也可以建立自定義的建構函式,從而定義自定義物件型別的屬性和方法。例如,可以使用建構函式模式將前面的例子重寫。如下:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        return this.name;
    };
}

var p1 = new Person("Ann" , 23 , "actress");
var p2 = new Person("Rose" , 23 , "writer");

當我看到上面的內容的時候,我震驚了。javascript中呼叫函式可以建立一個新的物件?使用new操作符,然後呼叫函式就可以了?

雖然這種方式很奇怪(相對於java,c#而言),但是,的確可以。

因為根據以往的經驗,函式中的this是當前的執行環境變數。一般來說,如果是在網頁上的全域性環境裡面呼叫函式,則函式內部的this物件就是window物件。但是,這裡的this好像是一個新的型別(Person?)的物件。為什麼?

下面是我的小測驗,檢視一下通過這種方式呼叫函式,是不是真的改變了this物件。

1 使用new呼叫建構函式的程式碼:

console.log("<>>>before this是 Window 型別嗎? " + (this instanceof Window));
function Person(name, age, job) {
    console.log("inner this是 Window 型別嗎? " + (this instanceof Window));
    console.log("inner this是 Person 型別嗎? " + (this instanceof Person));
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        return this.name;
    };
}
var p1 = new Person("Ann", 23, "actress"); 
// todo: 使用了 new 操作符
console.log("outer this是 Window 型別嗎? " + (this instanceof Window));
console.log("outer this是 Person 型別嗎? " + (this instanceof Person));

// 在瀏覽器執行的輸出結果如下

/**
 <pre>
 // todo: 在瀏覽器執行的輸出結果如下 :
 <>>>before this是 Window 型別嗎? true
 inner this是 Window 型別嗎? false
 inner this是 Person 型別嗎? true
 outer this是 Window 型別嗎? true
 outer this是 Person 型別嗎? false
 </pre>
 */

2 不使用new,直接呼叫建構函式的程式碼:

console.log("<>>>before this是 Window 型別嗎? " + (this instanceof Window));
function Person(name, age, job) {
    console.log("inner this是 Window 型別嗎? " + (this instanceof Window));
    console.log("inner this是 Person 型別嗎? " + (this instanceof Person));
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        return this.name;
    };
}
var p1 = Person("Ann", 23, "actress"); // 注意:這裡沒有 new
console.log("outer this是 Window 型別嗎? " + (this instanceof Window));
console.log("outer this是 Person 型別嗎? " + (this instanceof Person));

/*
 <pre>
 // todo: 在瀏覽器執行的輸出結果如下 :
 <>>>before this是 Window 型別嗎? true
 inner this是 Window 型別嗎? true
 inner this是 Person 型別嗎? false
 outer this是 Window 型別嗎? true
 outer this是 Person 型別嗎? false
 </pre>
 */

同樣的程式碼,只是在呼叫的時候,一次使用了new操作符來呼叫Person()函式,一次沒有。然後輸出的效果並不相同。
log可以看到:

  • 在函式外部,this一直是 Window型別的物件。(在全域性環境中呼叫的)[即使函式內部的this不是Window型別。]
  • 在函式內部:
    • 如果當前函式被new funcName(args);的方式呼叫,this為當funcName型別的物件;
    • 如果當前函式沒有使用new操作符呼叫,只是普通呼叫(var result = funcName(args);),則thisWindow型別的物件。

所以,通過建構函式模式確實建立了一個新的物件,而且,這個物件有自己的型別,可以被型別識別到了。

總結

  • 任何函式,不使用new去呼叫,就是普通的函式呼叫,函式中的this是當前函式呼叫的執行環境變數。(誰呼叫我,我的this就是誰。)。
  • 任何函式,使用new去呼叫,就成了建構函式,函式會建立一個新的物件型別,新的物件型別名就是當前函式名,函式中的this型別也就是這個新的物件型別。

相關文章