JavaScript_面向對象程序設計

類的建立於實例對象

工廠模型建立對象

function CreatePerson ( name,sex,age ) {
    
    var obj = new Object();
    
    obj.name = name;
    obj.sex = sex;
    obj.age = age;
    
    obj.sayName = function () {
        
        console.log( this.name );
        
    }
    
    return obj;
    
}

var p1 = CreatePerson('zf','女',22);

p1.sayName(); //zf

console.log( p1.name );  //zf

構造函數式

//函數的第一個字母大寫(類的模板)
function Person ( name,age,sex ) {
    
    this.name = name;
    this.age = age;
    this.sex =sex;
    
    this.sayName = function () {
        
        alert(this.name);
        
    }
    
}

//構造一個對象,  使用new關鍵字, 傳遞參數,   執行模板代碼, 返回對象。

var p1 = new Person('zf',20,'女');  //類的概念:根據模板建立出不一樣的實例對象
 
console.log( p1.name );

p1.sayName();

建立類的實例:數組

  • 看成構造函數去使用安全

    var p1 = new Person('a1',20);函數

  • 做爲普通函數去調用性能

    Person('a2',20); //在全局環境中定義屬性並賦值, 直接定義在window上。this

  • 在另個一對象的做用域中調用spa

    var o = new Object();
    Person.call(o,'a3',23);prototype

Object每一個實例都會具備的屬性和方法:指針

Constructor: 保存着用於建立當前對象的函數。(構造函數)
hasOwnProperty(propertyName):用於檢測給定的屬性在當前對象實例中(而不是原型中)是否存在。
isPrototypeOf(Object): 用於檢查傳入的對象是不是另一個對象的原型。
propertyIsEnumerable(propertyName):用於檢查給定的屬性是否可以使用for-in語句來枚舉。
toLocaleString():返回對象的字符串表示。該字符串與執行環境的地區對應.
toString():返回對象的字符串表示。
valueOf():返回對象的字符串、數值或布爾表示。code

判斷一個對象是否是另外一個對象的實例,一般使用的是 instanceof. 比較少使用constructor。對象

原型

建立每個函數的時候,都有一個prototype屬性. 這個是屬性,是一個指針。而這個對象老是指向一個對象。
這個對象 的用途就是將特定的屬性和方法包含在內,是一個實例對象, 起到了一個全部實例所共享的做用。
屏蔽了,構造函數的缺點,new 一個對象,就把構造函數內的方法實例化一次。

function Person () {
                
}

var obj = Person.prototype;

console.log( obj );  //Person.prototype 就是一個對象
//Person.prototype 內部存在指針,指針指向一個對象。 這個對象稱之爲:原型對象。原型對象,被全部的實例對象所共享。

console.log( obj.constructor ); //function Person(){}  //obj這個對象的構造器就是 Person

原型圖例:

clipboard.png

console.log(Person.prototype) 的結果:
clipboard.png

經常使用方法

Object.getPrototypeOf()

根據實例對象得到原型對象

每次代碼讀取一個對象的屬性的時候:首先會進行一次搜索,搜索實例對象裏,看看是否存在,若是沒有,再去實例所對的原型中尋找屬性.若是有則返回,若是兩次都沒有則返回undefined

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
    
    console.log( '我是原型對象方法' );
    
}

var p1 = new Person();

console.log( p1.name ); //z1

console.log( Object.getPrototypeOf(p1) );  
console.log( Object.getPrototypeOf(p1) == Person.prototype ); //true

hasOwnProperty()

判斷是不是 實例對象本身的屬性

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
    
    console.log( '我是原型對象方法' );
    
}


// 判斷一個對象屬性 是屬於 原型屬性 仍是屬性 實例屬性

var p3 = new Person();    
console.log( p3.name ); //zf 是原型上的

//hasOwnProperty() 是不是 實例對象本身的屬性 
console.log( p3.hasOwnProperty('name') ); //false

in 操做符

不管是 原型的屬性, 仍是實例對象的屬性, 都區分不開。 若是存在,返回true

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
    
    console.log( '我是原型對象方法' );
    
}


//判斷屬性是否存在 實例對象 和 原型對象中. 

var p1 = new Person();
console.log('name' in p1); //true  //表示,name的屬性到底在不在p1的屬性中  true
var p2 = new Person();

p1.name = 'zzz';

console.log('name' in p1); //true

判斷一個屬性是否在原型中

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
    
    console.log( '我是原型對象方法' );
    
}


//判斷屬性是否存在 實例對象 和 原型對象中. 

var p1 = new Person();
p1.name = '123';

//在原型對象中,是否存在這個值
//@obj 當前對象
//@判斷的屬性
function hasPrototypeProtoperty ( obj,attrName ) {
    
    return !obj.hasOwnProperty(attrName) && (attrName in obj);
    
}

console.log( hasPrototypeProtoperty(p1,'name') );  //false

Object.keys()

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
    
    console.log( '我是原型對象方法' );
    
}

//ECMA5新特性  Object.keys(); 
//拿到當前對象中的全部keys, 返回一個數組

var p1 = new Person();
p1.name = 'zz';
p1.age = 20;

var attr = Object.keys(p1);
console.log( attr );  //["name", "age"]

var attr2 = Object.keys(p1.__proto__);  

console.log( attr2 );  //["name", "age", "sayName"]

var attr3 = Object.keys(Person.prototype);  

console.log( attr3 );  //["name", "age", "sayName"]

Object.getOwnPropertyNames()

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
    
    console.log( '我是原型對象方法' );
    
}

var p1 = new Person();
p1.name = 'zz';
p1.age = 20;

//ECMA5 
//constructor屬性,是沒法被枚舉的. 正常的for-in循環是沒法枚舉. [eable = false];

//Object.getOwnPropertyNames(); //枚舉對象全部的屬性:無論該內部屬性可以被枚舉.

var attr4 = Object.getOwnPropertyNames(Person.prototype); //["constructor", "name", "age", "sayName"]

console.log( attr3 );

isPrototypeOf()

判斷原型的方法

原型對象.isPrototypeOf(new instance);

實現each方法

原型的另一個做用就是擴展對象中的屬性和方法

//遍歷多維數組
var arr = [1,2,4,5,[455,[456,[345345]]]];


Array.prototype.each = function ( cb ) {

    try {

        //計數器
        this.i || (this.i = 0);

        //核心代碼
        if ( this.length > 0 && cb.constructor === Function ) {

            while ( this.i < this.length ) {  //計數器 大於 數組長度跳出

                //得到每一項值    
                var e = this[this.i];

                //判斷是不是 數組
                if ( e && e.constructor === Array ) {

                    //遞歸
                    e.each(cb);

                } else {

                    cb.call(null,e);

                }

                this.i++;    

            }

            //使用完以後,釋放變量
            this.i = null;

        }


    } catch (e) {
        //do someting
        
    }

    return this;

};


arr.each(function( val ){

    console.log(val);

});

簡單原型

直接經過對象字面量來重寫整個原型對象(這種方法會改變原型對象的構造器[改變爲Object])

//簡單原型
            
function Person () {
    
}

Person.prototype = {
    
    constructor: Person,  //原型的構造器改變
    
    name: 'zz',
    age: 20,
    
    say: function () {
        console.log( this.age );
    }
    
}

var p1 = new Person();

console.log( p1.name );
p1.say();

存在的問題,constructor屬性是沒法被枚舉的。加在原型對象上,能夠被枚舉,被枚舉。不符合要求。

ECMA5中的Object.defineProperty()方法能夠爲原型對象從新加入構造器。constructor問題能夠被避免。

//3個參數,  參數1:從新設置構造的對象 (給什麼對象設置)     參數2:設置什麼屬性        參數3:options配置項 (要怎麼去設置)
Object.defineProperty(Person.prototype,'constructor',{
    enumerable: false,  //是不是 可以 被枚舉
    value: Person  //值   構造器的 引用
});

原型的動態特性

注意原型和建立實例的先後順序

function Person () {
                        
}

var p1 = new Person();  // {}
 
Person.prototype = {
    constructor: Person,
    name: 'zf',
    age: 20,
    say: function () {
        console.log('原型');
    }
}

//先把原型對象寫好,而後再實例化。

//p1.say();    //error 由於      原型對象裏面沒有任何屬性和方法

var p2 = new Person();

p2.say();

//注意  簡單原型使用的順序(實例對象必須在原型對象以後建立)

原型對象的經常使用開發模式

組合構造函數式和原型模式

function Person( name,age,firends ) {
    
    this.name = name;
    this.age = age;
    this.firends = firends;
    
}

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

var p1 = new Person('zz',20,['zf']);
var p2 = new Person('zx',22,['z1']);

console.log( p1.firends ); //['zf']                
console.log( p2.firends );  //['z1']

動態原型模式

就是把信息都封裝到函數中,這樣體現了封裝的概念。

//動態原型模式:(讓你的代碼  都封裝到一塊兒)
function Person( name,age,firends ) {
    
    this.name = name;
    this.age = age;
    this.firends = firends;
    
    //動態原型方法
    if ( typeof this.sayName !== 'function'  ) {
        
        Person.prototype.sayName = function () {
            console.log(this.name);
        }
        
    }
    
}

穩妥構造函數式

穩妥模式就是沒有公共屬性,並且其餘方法也不引用this對象,穩妥模式最適合在安全的環境中使用。若是程序對於安全性要求很高,那麼很是適合這種模式。
也不能使用new關鍵字。

//穩妥構造函數式  durable object (穩妥對象)
//1,沒有公共的屬性
//2,不能使用this對象

function Person ( name,age ) {
    
    //建立一個要返回的對象。  利用工廠模式思惟。
    var obj = new Object();
    
    //能夠定義一下是有的變量和函數 private
    
    var name = name || 'zf';
    
//                        var sex = '女';
//                        var sayName = function () {
//                        }

        
    //添加一個對外的方法
    obj.sayName = function () {
        console.log(name);
    }
    
    return obj;
    
}

var p1 = Person('xixi',20);

p1.sayName();

深刻原型繼承的概念

若是讓原型對象等於另外一個類型的實例,結果會怎麼樣呢?顯然此時的原型對象將包含一個指向另外一個原型的指針,相應的另外一個原型中也包含着一個指向另外一個構造函數的指針。

原型鏈: 利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。

構造函數 原型對象 實例對象

  • 構造函數.prototype = 原型對象

  • 原型對象.constructor = 構造函數

  • 實例對象.__proto__ = 原型對象

  • 原型對象.isPrototypeOf(實例對象)

  • 構造函數 實例對象 (類和實例)

isPrototypeOf(); //判斷是否 一個對象的 原型

clipboard.png

//父類的構造函數  Sup
function Sup ( name ) {
    
    this.name = name;
    
}

//父類的原型對象
Sup.prototype = {
    constructor: Sup,
    sayName: function () {
        console.log(this.name);
    }
}

//子類的構造函數 Sub
function Sub ( age ) {
    
    this.age = age;
    
}

//若是子類的原型對象  等於 父類的 實例

//1, 顯然此時的原型對象將包含一個指向另外一個原型的指針
//2, 相應的另外一個原型中也包含着一個指向另外一個構造函數的指針。

//            實例對象.__proto__ = 原型對象
//                Sup的實例對象  和 Sup的原型對象 有一個關係

Sub.prototype = new Sup('zf');

//            console.log( Sub.prototype.constructor );  //function Sup () {}
//            
//            console.log( Sub.prototype.__proto__ );    //Sup 的 原型對象


var sub1 = new Sub(20);

console.log( sub1.name );  //zf
sub1.sayName();  //zf

原型鏈繼承映射圖

clipboard.png


clipboard.png

繼承的三種方式

原型繼承

//原型繼承的特色:
//即繼承了父類的模板,又繼承了父類的原型對象。  (全方位的繼承)
//父類
function Person ( name,age ) {
    
    this.name = name;
    this.age = age;
    
}

Person.prototype.id = 10;

//子類
function Boy ( sex ) {
    
    this.sex = sex;     
    
}

//原型繼承
Boy.prototype = new Person('zz');

var b = new Boy();

console.log( b.name ); //zz
console.log( b.id ); //10

類繼承

類繼承 (只繼承模板) 不繼承原型對象 (借用構造函數的方式繼承)

//父類
function Person ( name,age ) {
    
    this.name = name;
    this.age = age;
    
}

Person.prototype.id = 10;

//子類
function Boy ( name,age,sex ) {
    
    //類繼承
    Person.call(this,name,age);
    
    this.sex = sex;     
    
}

var b = new Boy('zf',20,'女');

console.log( b.name ); //zf
console.log( b.age ); //20
console.log( b.sex );  //女
console.log( b.id );  //父類的原型對象並無繼承過來.

混合繼承

原型繼承+類繼承

//父類     (關聯父類和子類的關係)
function Person ( name,age ) {
    
    this.name = name;
    this.age = age;
    
}

Person.prototype.id = 10;
Person.prototype.sayName = function () {
    console.log( this.name );
}

//子類
function Boy ( name,age,sex ) {
    
    //1 類繼承
    Person.call(this,name,age);   //繼承父類的模板
    
    this.sex = sex;     
    
}                

//2 原型繼承
//父類的實例  和 父類的 原型對象的關係.
Boy.prototype = new Person();   //繼承父類的原型對象

var b = new Boy('z1',20,'女');

console.log( b.name );//z1
console.log( b.sex ); //女
console.log( b.id ); //10

b.sayName(); //z1

ExtJs底層繼承方式

模擬ExtJs底層繼承一部分代碼

//ExtJs  繼承
//2件事: 繼承了1次父類的模板,繼承了一次父類的原型對象


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

Person.prototype = {
    
    constructor: Person,
    
    sayHello: function () {

        console.log('hello world!');

    }
    
}

function Boy ( name,age,sex ) {
    
    //call 綁定父類的模板函數  實現  借用構造函數繼承  只複製了父類的模板
    
//                Person.call(this,name,age);

        Boy.superClass.constructor.call(this,name,age);
    
    this.sex = sex;        
    
}

//原型繼承的方式: 即繼承了父類的模板,又繼承了父類的原型對象。
//            Boy.prototype = new Person();
//只繼承 父類的原型對象
    extend(Boy,Person);  // 目的 只繼承 父類的原型對象  , 須要那兩個類產生關聯關係.

//給子類加了一個原型對象的方法。
Boy.prototype.sayHello = function () {
    
    console.log('hi,js');
    
}

var b = new Boy('zf',20,'男');

console.log( b.name );
console.log( b.sex );
b.sayHello(); 

Boy.superClass.sayHello.call(b);

//extend方法

//sub子類, sup 父類
function extend ( sub,sup ) {
    
    //目的, 實現只繼承 父類的原型對象。   從原型對象入手
    
    //1,建立一個空函數, 目的:空函數進行中轉
    var F = new Function();  // 用一個空函數進行中轉。
    
//                把父類的模板屏蔽掉, 父類的原型取到。
    
    F.prototype = sup.prototype;  //2實現空函數的原型對象 和 超類的原型對象轉換
    
    sub.prototype = new F();  //3原型繼承
    
    //作善後處理。 還原構造器 ,
    sub.prototype.constructor = sub;  //4 ,還原子類的構造器
    
//                保存一下父類的原型對象  // 由於 ①方便解耦, 減低耦合性      ② 能夠方便得到父類的原型對象
    sub.superClass = sup.prototype;   //5 ,保存父類的原型對象。   //自定義一個子類的靜態屬性   , 接受父類的原型對象。
    
    //判斷父類的原型對象的構造器, (防止簡單原型中給更改成  Object)
    if ( sup.prototype.constructor == Object.prototype.constructor ) {
        
        sup.prototype.constructor = sup;  //還原父類原型對象的構造器
        
    }
    
}
相關文章
相關標籤/搜索