例項物件與new命令
阿新 • • 發佈:2018-12-14
原文地址:https://wangdoc.com/javascript/
建構函式
JavaScript語言的物件體系不是基於類的,而是基於建構函式和原型鏈。JavaScript語言使用建構函式作為物件的模板。所謂建構函式就是專門用來生成例項物件的函式。建構函式就是一個普通的函式,但是有自己的特徵和用法。
var Vehicle = function () {
this.price = 1000;
};
上面程式碼中,Vehicle就是建構函式。為了與普通函式區別,建構函式名字的第一個字母通常大寫。
建構函式的特點有兩個。
- 函式體內部使用了this關鍵字,代表了所要生成物件的例項。
生成物件的時候,必須使用new命令。
new命令
基本用法
new命令的作用就是執行建構函式,返回一個例項物件。
var v = new Vehicle();
v.price() // 1000
使用new命令時,根據需要,建構函式也可以接受引數。
var Vehicle = function (p) {
this.price = p;
};
var v = new Vehicle(500);
如果忘了使用new命令,直接呼叫建構函式,建構函式就變成普通函式,並不會生成例項物件。而且由於後面會說到的原因,this這時代表全域性物件,將造成一些意想不到的結果。
為了保證建構函式與new
function Fubar(foo, bar) {
"use strict";
this._foo = foo;
this._bar = bar;
}
Fubar() // TypeError: Cannot set property "_foo" of undefined
由於嚴格模式中,函式內部的this不能指向全域性物件,預設等於undefined,導致不加new呼叫會報錯。
另一個解決方法,建構函式內部判斷是否使用new
function Fubar(foo, bar) {
if (!(this instanceof Fubar)) {
return new Fubar(foo, bar);
}
this._foo = foo;
this._bar = bar;
}
Fubar(1, 2)._foo // 1
(new Fubar(1, 2))._foo // 1
new命令的原理
使用new命令時,它後面的函式依次執行下面的步驟。
- 建立一個空物件,作為將要返回的物件例項。
- 將這個空物件的原型指向建構函式的prototype屬性。
- 將這個空物件賦值給函式內部的this關鍵字。
- 開始執行建構函式內部的程式碼。
也就是說,建構函式內部,this指的是一個新生成的空物件,所以針對this的操作,都會發生在空物件上。
如果建構函式內部有return語句,而且return後面跟著一個物件,那麼new命令會返回return語句指定的物件;否則,就會不管return語句,返回this物件。
但是如果return語句返回一個跟this無關的新物件,new命令會返回這個新物件,而不是this物件。這一點需要特別引起注意。
var Vehicle = function () {
this.price = 1000;
return {price: 2000};
};
(new Vehicle()).price // 2000
另一方面,如果對普通函式(內部沒有this關鍵字的函式)使用new命令,則會返回一個空物件。
function getMessage() {
return 'this is a message';
}
var msg = new getMessage();
msg // {}
typeof msg // "object"
new命令簡化的內部流程,可以用下面的程式碼表示。
function _new(constructor, params) {
// 將arguments物件轉為陣列
var args = [].slice.call(arguments);
// 取出建構函式
var constructor = args.shift();
// 建立一個空物件,繼承建構函式的prototype屬性
var context = Object.create(constructor.prototype);
// 執行建構函式
var result = constructor.apply(context, args);
// 如果返回結果是物件,就直接返回,否則返回 context物件
return (typeof result === "object" && result != null) ? result : context;
}
new.target
函式內部可以使用new.target屬性。如果當前函式是new命令呼叫,new.target指向當前函式,否則為undefined。
function f() {
console.log(new.target === f);
}
f() // false
new f() // true
使用這個屬性,可以判斷函式呼叫的時候,是否使用new命令。
function f() {
if (!new.target) {
throw new Error("請使用new命令呼叫!");
}
}
f() // Uncaught Error: 請使用new命令呼叫!
Object.create()建立例項物件
建構函式作為模板,可以生成例項物件。但是有時候拿不到建構函式,只能拿到一個現有的物件。我們希望以這個現有的物件為模板,生成新的例項物件,這時就可以使用Object.create方法。
var person1 = {
name: "zhang san",
age: 38,
greeting: function () {
console.log("hello world");
}
};
var person2 = Object.create(person1);