1. 程式人生 > >JavaScript 建立物件和繼承

JavaScript 建立物件和繼承

首先宣告下,本人不擅長文字表達,文采不行,所以文章中文字較少,請看程式碼

本人 GitHub連結個人部落格網站部落格園 郵箱地址:[email protected]

建立物件

JS中可以有許多設計模式,在這些中推薦使用組合建構函式和原型模式;
因為不太善於寫文章,所以下面簡化文字敘述,直接看程式碼

工廠模式

function person(name,age){
    var obj={}
    obj.name=name;
    obj.age=age;
    obj.sayName=function(){
        console.
log(this.name); } return obj; }

建構函式模式

正常情況下我們將函式名起為大寫字母開頭

function Person(name,age){
    this.name=name;
    this.age=age;
    this.sayName=function(){
        console.log(this.name);
    }
}

var a=new Person("張三",29);
var b=new Person("李四",22);

a.sayName();
b.sayName();

建構函式可以通過 new

關鍵字來進行處理,使其例項化,每個都有獨有資料,也就是私有變數

原型模式

function Person(){
    
}
Person.prototype.name = "Li Si";
Person.prototype.age = 21;
Person.prototype.sayName=function(){
    console.log(this.name);
}

var person1=new Person();
person1.sayName(); //"Li Si"
var person2=new Person();
person2.sayName(); //"Li Si"

原型模式所有的都共用同一個資料,相當於公有變數

組合模式:建構函式和原型模式結合

通過上面兩個例子,兩個都有獨有的特性,所以我們可以組合兩者了來進行處理,一般推薦使用組合模式

function Person(name,age){
    this.name=name;
    this.age=age;
}
Person.prototype.sayName=function(){
    console.log(this.name);
}

var person1=new Person("張三");
person1.sayName(); //"張三"
var person2=new Person("李四");
person2.sayName(); //"李四"

這樣各自經過例項化後,都有自己的獨有資料,但是卻有著公共方法;

繼承

ES5 繼承有許多中方式,我們這裡只說普通常用的繼承方式

原型鏈賦值繼承

function father(name){
    this.name=name;
}

father.prototype.sayName=function(){
    console.log(this.name);
}

function sub(age){
	this.age=age;
}
sub.prototype=new father();
sub.prototype.sayAge=function(){
	console.log(this.age);
}

上面就是典型的原型鏈賦值繼承,但是這個繼承是有缺點的,在繼承時需要 new 關鍵字進行處理。

建構函式繼承

function father(name){
    this.name=name;
    this.age=22;
}

father.prototype.sayName=function(){
    console.log(this.name);
}

function sub(){
	father.apply(this,arguments)
}

var s=new sub("a")
> s
> sub {name: "a", age: 22}
    age: 22
    name: "a"
    __proto__:
    constructor: ƒ sub()
    __proto__: Object

建構函式繼承最後在輸出中會發現並沒有 父級的方法 ,但是可以將資料傳到父級,與原型鏈繼承互補,所以衍生出組合繼承的方式

組合繼承

function Father(name) {
    this.name = name;
    this.className = "Father"
}
Father.prototype.sayName = function () {
    console.log(this.name)
}
function Sub(name) {
    Father.apply(this, arguments)
}
//繼承原型
Sub.prototype = new Father();

var s=new Sub("張三",12);


不僅會繼承建構函式中的屬性,也會複製父類原型鏈中的屬性

但是在 Sub.prototype = new Father(); 之後

Sub 的原型變成這樣的了

> Sub.prototype
> {name: undefined, className: "person"}

也就是說Sub的原型中已經有了一個name屬性,而之後建立 s 時傳給構造的函式的name則是通過this重新定義了一個name屬性,相當於只是覆蓋掉了原型的name屬性(原型中的name依然還在),這樣很不優雅。

寄生組合繼承

function Father(name) {
    this.name = name;
    this.className = "Father"
}
Father.prototype.sayName = function () {
    console.log(this.name)
}
function Sub(name,age) {
    this.age=age;
    Father.call(this, name)
}
// 注意此處
Sub.prototype=Object.create(Father.prototype)

Sub.prototype.sayAge=function(){
    console.log(this.age)
}

這裡用到了 Object.creat(obj) 方法,該方法會對傳入的obj物件進行淺拷貝。和上面組合繼承的主要區別就是:將父類的原型複製給了子類原型。這種做法很清晰:

  1. 建構函式中繼承父類屬性/方法,並初始化父類。
  2. 子類原型和父類原型建立聯絡。

還有一個問題,就是constructor屬性,我們來看一下:

> Father.prototype.constructor
< Father(name){
   this.name=name;
   this.className="Father" 
 }
> Sub.prototype.constructor
< Father(name){
   this.name=name; 
   this.className="Father" 
  }

constructor是類的建構函式,我們發現,Father和Sub例項的constructor指向都是Father,當然,這並不會改變instanceof的結果,但是對於需要用到construcor的場景,就會有問題。所以一般我們會加上這麼一句:

Sub.prototype.constructor = Sub