在j**ascript中,建立物件的方式包括兩種:物件字面量和使用new表示式。
1.1物件字面量是一種靈活方便的書寫方式,例如:
var o1 =}
這樣,就用物件字面量建立了乙個物件o1,它具有乙個成員變數p以及乙個成員方法alertp。
這種寫法的缺點是:每建立乙個新的物件都需要寫出完整的定義語句,不便於建立大量相同型別的物件,不利於使用繼承等高階特性。
1.2new表示式是配合建構函式使用的,例如new string(「a string」),呼叫內建的string函式構造了乙個字串物件。
下面我們用建構函式的方式來重新建立乙個實現同樣功能的物件,首先是定義建構函式,然後是呼叫new表示式:
functionco()
}var o2 = newco();
那麼,在使用new操作符來呼叫乙個建構函式的時候,發生了什麼呢?其實很簡單,就發生了四件事:
var obj ={};obj.__proto__ =co.prototype;
co.call(obj);
return obj;
第一行,建立乙個空物件obj。
第二行,將這個空物件的__proto__成員指向了建構函式物件的prototype成員物件,這是最關鍵的一步,具體細節將在下文描述。
第三行,將建構函式的作用域賦給新物件,因此ca函式中的this指向新物件obj,然後再呼叫co函式。於是我們就給obj物件賦值了乙個成員變數p,這個成員變數的值是」 i』min constructed object」。
第四行,返回新物件obj。當建構函式裡包含返回語句時情況比較特殊,這種情況會在下文中說到。
不同於其它的主流程式語言,j**ascript的建構函式並不是作為類的乙個特定方法存在的;
當任意乙個普通函式用於建立一類物件時,它就被稱作建構函式,或構造器。
乙個函式要作為乙個真正意義上的建構函式,需要滿足下列條件:
上文定義的建構函式co就是乙個標準的、簡單的建構函式。
下面例子定義的函式c1返回了乙個物件,我們可以使用new表示式來呼叫它,該表示式可以正確返回乙個物件:
functionc1()
returno;}
var o1 = new
c1();
alert(o1.p);
// hello world
但這種方式並不是值得推薦的方式,因為物件o1的原型是函式c1內部定義的物件o的原型,也就是object.prototype。
這種方式相當於執行了 正常new表示式的前三步,而在第四步的時候返回了c1函式的返回值。
該方式同樣不便於建立大量相同型別的物件,不利於使用繼承等高階特性,並且容易造成混亂,應該摒棄。
乙個建構函式在某些情況下完全可以作為普通的功能函式來使用,這是j**ascript靈活性的乙個體現。
下例定義的c2就是乙個「多用途」函式:
functionc2(a, b)
return
this.p;//
此返回語句在c2作為建構函式時沒有意義
}var c2 = new c2(2,3);
c2.alertp();
//結果為5
alert(c2(2, 3)); //
結果為5
該函式既可以用作建構函式來構造乙個物件,也可以作為普通的函式來使用。
用作普通函式時,它接收兩個引數,並返回兩者的相加的結果。
為了**的可讀性和可維護性,建議作為建構函式的函式不要摻雜除構造作用以外的**;
同樣的,一般的功能函式也不要用作構造物件。
根據上文的定義,在表面上看來,建構函式似乎只是對乙個新建立的物件進行初始化,增加一些成員變數和方法;然而建構函式的作用遠不止這些。
為了說明使用建構函式的意義,我們先來回顧一下前文提到的例子。
執行 var o2 = new co();
建立物件的時候,發生了四件事情:
var obj ={};obj.__proto__ =co.prototype;
co.call(obj);
return obj;
我們說最重要的是第二步,將新生成的物件的__prop__屬性賦值為建構函式的prototype屬性,使得通過建構函式建立的所有物件可以共享相同的原型。
這意味著同乙個建構函式建立的所有物件都繼承自乙個相同的物件,因此它們都是同乙個類的物件。
在j**ascript標準中,並沒有__prop__這個屬性,不過它現在已經是一些主流的j**ascript執行環境預設的乙個標準屬性,用於指向建構函式的原型。
該屬性是預設不可見的,而且在各執行環境中實現的細節不盡相同,例如ie瀏覽器中不存在該屬性。我們只要知道j**ascript物件內部存在指向建構函式原型的指標就可以了,這個指標是在呼叫new表示式的時候自動賦值的,並且我們不應該去修改它。
在構造物件的四個步驟中,我們可以看到,除第二步以外,別的步驟我們無須借助new表示式去實現,因此new表示式不僅僅是對這四個步驟的簡化,也是要實現繼承的必經之路。
關於j**ascript的
建構函式,有乙個容易混淆的地方,那就是原型的constructor屬性。
在j**ascript中,每乙個函式都有預設的原型物件屬性
prototype,該物件預設包含了兩個成員屬性:constructor和__proto__。
按照物件導向的習慣性思維,我們說建構函式相當於「類」的定義,從而可能會認為constructor屬性就是該類實際意義上的建構函式,在new表示式
建立乙個物件的時候,會直接呼叫constructor來初始化物件,那就大錯特錯了。
new表示式執行的實際過程已經在上文中介紹過了(四個步驟),其中用於初始化物件的是第三步,呼叫的初始化函式正是「類函式」本身,而不是constructor。
如果沒有考慮過這個問題,這一點可能不太好理解,那就讓我們舉個例子來說明一下吧:
functionc3(a, b)}//
我們定義乙個函式來覆蓋c3原型中的constructor,試圖改變屬性p的值
function
fake()
c3.prototype.constructor = fake; //
覆蓋c3原型中的constructor
var c3 = new c3(2,3);
c3.alertp();
//結果仍然為5
上述**手動改變了c3原型中的constructor函式,然而卻沒有對c3物件的建立產生實質的影響,可見在new表示式中,起初始化物件作用的只能 是建構函式本身。那麼constructor屬性的作用是什麼呢?一般來說,我們可以使用constructor屬性來測試物件的型別:
var myarray = [1,2,3];(myarray.constructor == array); //
true
這招對於簡單的物件是管用的,涉及到繼承或者跨視窗等複雜情況時,可能就沒那麼靈光了:
function f()function s()
s.prototype = new f(); //
s繼承自f
var son = new s(); //
用建構函式s建立乙個子類物件
(son.constructor == s); //
false
(son.constructor == f); //
true
這樣的結果可能跟你的預期不相一致,所以使用constructor屬性的時候一定要小心,或者乾脆不要用它。
深入理解各種建構函式
include includeusing namespace std class test else test const test t else test operator const test t pdata new char strlen t.pdata 1 strcpy pdata t.pd...
C 建構函式深入理解
01 初始化引數列表.cpp include include include using namespace std struct student 拷貝建構函式定義 拷貝建構函式的有效引數,必須是該類的物件的引用 if 1 student student s,int class no 1 else ...
js箭頭函式深入理解
let f a let f a let f a return alet f a a f 2 2let f 如果沒括號,花括號內會被視作待執行語句let callback callback callback function ok,普通函式 callback callback syntaxerror ...