JavaScript物件導向精要(二)

dwqs發表於2016-08-13

JavaScript 物件導向精要(一)

建構函式和原型物件

建構函式也是函式,用new建立物件時呼叫的函式,與普通函式的一個區別是,其首字母應該大寫。但如果將建構函式當作普通函式呼叫(缺少new關鍵字),則應該注意this指向的問題。

var name = "Pomy";
function Per(){
    console.log("Hello "+this.name);
}
var per1 = new Per();  //"Hello undefined"
var per2 = Per();   //"Hello Pomy"

使用new時,會自動建立this物件,其型別為建構函式型別,指向物件例項;缺少new關鍵字,this指向全域性物件。

可以用instanceof來檢測物件型別,同時每個物件在建立時都自動擁有一個constructor屬性,指向其建構函式(字面量形式或Object建構函式建立的物件,指向Object,自定義建構函式建立的物件則指向它的建構函式)。

console.log(per1 instanceof Per);  //true
console.log(per1.constructor === Per); //true

每個物件例項都有一個內部屬性:[[Prototype]],其指向該物件的原型物件。建構函式本身也具有prototype 屬性指向原型物件。所有建立的物件都共享該原型物件的屬性和方法。

function Person(){}
Person.prototype.name="dwqs";
Person.prototype.age=20;
Person.prototype.sayName=function()
{
    alert(this.name);
};
var per1 = new Person();
per1.sayName();  //dwqs
var per2 = new Person();
per2.sayName();  //dwqs
alert(per1.sayName == per2.sayName);  //true

所以,例項中的指標僅指向原型,而不指向建構函式。 ES5提供了hasOwnProperty()和isPropertyOf()方法來反應原型物件和例項之間的關係

alert(Person.prototype.isPrototypeOf(per2));  //true
per1.blog = "www.ido321.com";
alert(per1.hasOwnProperty("blog"));  //true
alert(Person.prototype.hasOwnProperty("blog"));  //false
alert(per1.hasOwnProperty("name"));  //false
alert(Person.prototype.hasOwnProperty("name"));  //true

因為原型物件的constructor屬性是指向建構函式本身,所以在重寫原型時,需要注意constructor屬性的指向問題。

function Hello(name){
    this.name = name;
}
//重寫原型
Hello.prototype = {
    sayHi:function(){
        console.log(this.name);
    }
};
var hi = new Hello("Pomy");
console.log(hi instanceof Hello);  //true
console.log(hi.constructor === Hello); //false
console.log(hi.constructor === Object); //true

使用物件字面量形式改寫原型物件改變了建構函式的屬性,因此constructor指向Object,而不是Hello。如果constructor指向很重要,則需要在改寫原型物件時手動重置其constructor屬性

Hello.prototype = {
    constructor:Hello,
    sayHi:function(){
        console.log(this.name);
    }
};
console.log(hi.constructor === Hello); //true
console.log(hi.constructor === Object); //false

利用原型物件的特性,我們可以很方便的在JavaScript的內建原型物件上新增自定義方法:

Array.prototype.sum=function(){
    return this.reduce(function(prev,cur){
        return prev+cur;
    });
};
var num = [1,2,3,4,5,6];
var res = num.sum();
console.log(res);  //21
String.prototype.capit = function(){
    return this.charAt(0).toUpperCase()+this.substring(1);
};
var msg = "hello world";
console.log(msg.capit()); //"Hello World"

繼承

利用[[Prototype]]特性,可以實現原型繼承;對於字面量形式的物件,會隱式指定Object.prototype為其[[Prototype]],也可以通過Object.create()顯示指定,其接受兩個引數:第一個是[[Prototype]]指向的物件(原型物件),第二個是可選的屬性描述符物件。

var book = {
    title:"這是書名";
};
//和下面的方式一樣
var book = Object.create(Object.prototype,{
    title:{
        configurable:true,
        enumerable:true,
        value:"這是書名",
        wratable:true
    }
});

字面量物件會預設繼承自Object,更有趣的用法是,在自定義物件之間實現繼承。

var book1 = {
    title:"JS高階程式設計",
    getTitle:function(){
        console.log(this.title);
    }
};
var book2 = Object.create(book1,{
    title:{
        configurable:true,
        enumerable:true,
        value:"JS權威指南",
        wratable:true
    }
});
book1.getTitle();  //"JS高階程式設計"
book2.getTitle();  //"JS權威指南"
console.log(book1.hasOwnProperty("getTitle"));  //true
console.log(book1.isPrototypeOf("book2"));  //false
console.log(book2.hasOwnProperty("getTitle"));  //false

當訪問book2的getTitle屬性時,JavaScript引擎會執行一個搜尋過程:現在book2的自有屬性中尋找,找到則使用,若沒有找到,則搜尋[[Prototype]],若沒有找到,則繼續搜尋原型物件的[[Prototype]],直到繼承鏈末端。末端通常是Object.prototype,其[[Prototype]]被設定為null。

實現繼承的另外一種方式是利用建構函式。每個函式都具有可寫的prototype屬性,預設被自懂設定為繼承自Object.prototype,可以通過改寫它來改變原型鏈。

function Rect(length,width){
    this.length = length;
    this.width = width;
}
Rect.prototype.getArea = function(){
    return this.width * this.length;
};
Rect.prototype.toString = function(){
    return "[Rect"+this.length+"*"+this.width+"]";
};
function Square(size){
    this.length = size;
    this.width = size;
}
//修改prototype屬性
Square.prototype = new Rect();
Square.prototype.constructor = Square;
Square.prototype.toString = function(){
    return "[Square"+this.length+"*"+this.width+"]";
};
var rect = new Rect(5,10);
var square = new Square(6);
console.log(rect.getArea());  //50
console.log(square.getArea());  //36

如果要訪問父類的toString(),可以這樣做:

Square.prototype.toString = function(){
    var text = Rect.prototype.toString.call(this);
    return text.replace("Rect","Square");
}

相關文章