JavaScript高程學習筆記之面向對象的程序設計(6)


[toc]javascript


內容概要java

  • 理解對象屬性
  • 理解並建立對象
  • 理解繼承

1. 理解對象

用對象字面量建立自定義對象數組

1.1 屬性類型
  1. 數據屬性
  • Configurable:可否經過delete刪除屬性而從新定義屬性
  • Enumerable:可否經過for-in循環返回屬性
  • Writable:可否修改屬性的值
  • Value:包含這個屬性的數據 用Object.defineProperty()方法修改默認屬性的特性
var person = {};
        Object.defineProperty(person, "name", {
            writable: false,
            value: "Nicholas"
        });
        
        alert(person.name);
        person.name = "Michael";
        alert(person.name);
  1. 訪問器屬性
  • Configurable:可否經過delete刪除屬性而從新定義屬性
  • Enumerable:可否經過for-in循環返回屬性
  • Get:在讀取屬性時調用的函數
  • Set:在寫入屬性時調用的函數 用Object.defineProperty()方法來定義
var book = {
            _year: 2004,
            edition: 1
        };
          
        Object.defineProperty(book, "year", {
            get: function(){
                return this._year;
            },
            set: function(newValue){
            
                if (newValue > 2004) {
                    this._year = newValue;
                    this.edition += newValue - 2004;
                
                }
            }
        });
        
        book.year = 2005;
        alert(book.edition);   //2
1.2 定義多個屬性

Object.defineProperties()方法 經過描述符一次定義多個屬性 兩個對象參數:要添加和修改屬性的對象;對象屬性與要添加和修改的屬性app

var book = {};
        
        Object.defineProperties(book, {
            _year: {
                value: 2004
            },
            
            edition: {
                value: 1
            },
            
            year: {            
                get: function(){
                    return this._year;
                },
                
                set: function(newValue){
                    if (newValue > 2004) {
                        this._year = newValue;
                        this.edition += newValue - 2004;
                    }                  
                }            
            }        
        });
           
        book.year = 2005;
        alert(book.edition);   //2
1.3 讀取屬性特性

Object.getOwnPropertyDescriptor()方法:取得給定屬性的描述符 兩個參數:屬性所在對象和要讀取其描述符的屬性名稱函數

var book = {};
        
        Object.defineProperties(book, {
            _year: {
                value: 2004
            },
            
            edition: {
                value: 1
            },
            
            year: {            
                get: function(){
                    return this._year;
                },
                
                set: function(newValue){
                    if (newValue > 2004) {
                        this._year = newValue;
                        this.edition += newValue - 2004;
                    }                  
                }            
            }        
        });
           
        var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
        alert(descriptor.value);          //2004
        alert(descriptor.configurable);   //false
        alert(typeof descriptor.get);     //"undefined"
        
        var descriptor = Object.getOwnPropertyDescriptor(book, "year");
        alert(descriptor.value);          //undefined
        alert(descriptor.enumerable);     //false
        alert(typeof descriptor.get);     //"function"

2. 建立對象

使用構造函數和對象字面量建立單個對象,有個缺點:使用同一個接口建立對象,產生大量代碼重複,使用工廠模式的變體解決這個問題this

2.1 工廠模式

抽象建立具體對象的過程,用函數來封裝以特定接口建立對象的細節prototype

function createPerson(name, age, job){
            var o = new Object();
            o.name = name;
            o.age = age;
            o.job = job;
            o.sayName = function(){
                alert(this.name);
            };    
            return o;
        }
        
        var person1 = createPerson("Nicholas", 29, "Software Engineer");
        var person2 = createPerson("Greg", 27, "Doctor");
        
        person1.sayName();   //"Nicholas"
        person2.sayName();   //"Greg"

沒有解決對象識別問題code

2.2 構造函數模式

建立自定義的構造函數,從而定義自定義對象類型的屬性和方法對象

function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = function(){
                alert(this.name);
            };    
        }
        
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");

與工廠模式creatPerson()的不一樣之處繼承

  • 沒有顯式建立對象
  • 直接將屬性和方法賦給this對象
  • 沒有return語句
  1. 將構造函數看成函數
var person = new Person("Nicholas", 29, "Software Engineer");
        person.sayName();   //"Nicholas"
        
        Person("Greg", 27, "Doctor");  //adds to window
        window.sayName();   //"Greg"
        
        var o = new Object();
        Person.call(o, "Kristen", 25, "Nurse");
        o.sayName();    //"Kristen"

屬性和方法添加給window對象 2. 構造函數的問題 每一個方法都要在每一個實例上建立一遍 不一樣實例名是不相等的 咱們能夠把函數轉移到構造函數外部

function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = sayName;
        }
        
        function sayName(){
            alert(this.name);
        }
        
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");

在全局做用域中定義的函數實際上只能被某個對象調用,這讓全局做用域有點名存實亡。更難以接受的是:若是對象須要定義不少方法,那麼就定義多個全局函數。這個自定義的引用類型就無封裝性可言了。咱們能夠經過原型函數解決這個問題

2.3 原型模式

prototype就是經過調用構造函數而建立的那個對象實例的原型對象 讓全部對象實例共享它包含的屬性和方法

function Person(){
}
Person.prototype.name = "yohann";
Person.prototype.age = "22";
Person.prototype.job = "Software Engineer";
Person.sayname = function(){
	alert(this.name);
};

var person1 = new Person();
var person2 = new Person();
alert(person1.sayName = person2.sayName);     //true

1. 理解原型對象 Person構造函數、Person原型屬性和實例的關係 isPrototypeOf()方法:肯定對象是否具備原型屬性 Object.getPrototypeOf()方法:返回prototype的值 實例屬性屏蔽原型屬性

function Person(){
        }
        
        Person.prototype.name = "Nicholas";
        Person.prototype.age = 29;
        Person.prototype.job = "Software Engineer";
        Person.prototype.sayName = function(){
            alert(this.name);
        };
        
        var person1 = new Person();
        var person2 = new Person();
        
        person1.name = "Greg";
        alert(person1.name);   //"Greg" – from instance
        alert(person2.name);   //"Nicholas" – from prototype

delete操做符刪除實例屬性解除屏蔽 hasOwnProperty()方法:檢測屬性是存在實例中仍是原型中,對象實例返回true 2. 原型與in操做符 in操做符會在經過對象能訪問給定屬性時返回true hasOwnProperty()方法和in操做符組合使用:肯定該屬性是存在對象中仍是存在原型中

function hasPrototypeProperty(object, name){
	return !object.hasOwnProperty() && (name in object);
}

使用for-in循環時,返回的是可以經過對象訪問的、可枚舉的屬性,既包括實例中的屬性也包括原型中的屬性 Object.key()方法:接收一個對象做爲參數,返回一個包含全部可枚舉屬性的字符串數組

function Person(){
        }
        
        Person.prototype.name = "Nicholas";
        Person.prototype.age = 29;
        Person.prototype.job = "Software Engineer";
        Person.prototype.sayName = function(){
            alert(this.name);
        };
        
        var keys = Object.keys(Person.prototype);
        alert(keys);   //"name,age,job,sayName"

Object.getOwnPropertyNames()方法:返回全部實例屬性,不管是否枚舉 3. 更簡單的原型語法 前面例子中每添加一個原型屬性就要敲一遍Person.protptype。更常見是用一個包含全部屬性和方法的對象字面量來重寫整個原型對象

function Person(){
        }
        
        Person.prototype = {
            name : "Nicholas",
            age : 29,
            job: "Software Engineer",
            sayName : function () {
                alert(this.name);
            }
        };

        var friend = new Person();
        
        alert(friend instanceof Object);  //true
        alert(friend instanceof Person);  //true
        alert(friend.constructor == Person);  //false
        alert(friend.constructor == Object);  //true

可是constructor屬性值會改變,要設置其爲適當的值,會致使Enumerable特性被設置爲true 4. 原型的動態性 先建立實例,再修改原型。仍然能夠訪問該方法 重寫原型對象就不同了 下面例子是先建立一個對象實例,而後重寫原型對象

function Person(){
        }
        
        var friend = new Person();
                
        Person.prototype = {
            constructor: Person,
            name : "Nicholas",
            age : 29,
            job : "Software Engineer",
            sayName : function () {
                alert(this.name);
            }
        };
        
        friend.sayName();   //error

重寫原型對象切斷了現有原型與任何以前已經存在的實例的聯繫 4. 原生對象的原型 修改原生對象的原型,隨時添加方法 例如:給基本包裝類String添加一個startWith()方法

String.prototype.startsWith = function (text) {
            return this.indexOf(text) == 0;
        };
        
        var msg = "Hello world!";
        alert(msg.startsWith("Hello"));   //true

不建議在產品化的程序中修改原生對象的原型 5. 原型對象的問題 在原型中有一個數組,在實例中push,會改變原型中數組的值。另外一個實例訪問的是改變後的值

function Person(){
        }
        
        Person.prototype = {
            constructor: Person,
            name : "Nicholas",
            age : 29,
            job : "Software Engineer",
            friends : ["Shelby", "Court"],
            sayName : function () {
                alert(this.name);
            }
        };
        
        var person1 = new Person();
        var person2 = new Person();
        
        person1.friends.push("Van");
        
        alert(person1.friends);    //"Shelby,Court,Van"
        alert(person2.friends);    //"Shelby,Court,Van"
        alert(person1.friends === person2.friends);  //true
2.4 組合使用構造函數模式和原型模式

構造模式:定義實例屬性 原型模式:定義方法和共享的屬性

function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.friends = ["Shelby", "Court"];
        }
        
        Person.prototype = {
            constructor: Person,
            sayName : function () {
                alert(this.name);
            }
        };
        
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");
        
        person1.friends.push("Van");
        
        alert(person1.friends);    //"Shelby,Court,Van"
        alert(person2.friends);    //"Shelby,Court"
        alert(person1.friends === person2.friends);  //false
        alert(person1.sayName === person2.sayName);  //true
2.5 動態原型模式

檢查某個應該存在的方法是否有效,來決定是否初始化原型

function Person(name, age, job){
        
            //properties
            this.name = name;
            this.age = age;
            this.job = job;
            
            //methods
            if (typeof this.sayName != "function"){
            
                Person.prototype.sayName = function(){
                    alert(this.name);
                };
                
            }
        }

        var friend = new Person("Nicholas", 29, "Software Engineer");
        friend.sayName();
2.6 寄生構造函數模式

建立一個構造函數,做用僅僅是來封裝建立對象的代碼,而後返回新建立的對象

function SpecialArray(){       
 
            //create the array
            var values = new Array();
            
            //add the values
            values.push.apply(values, arguments);
            
            //assign the method
            values.toPipedString = function(){
                return this.join("|");
            };
            
            //return it
            return values;        
        }
        
        var colors = new SpecialArray("red", "blue", "green");
        alert(colors.toPipedString()); //"red|blue|green"

        alert(colors instanceof SpecialArray);

返回的對象與構造函數或者構造函數的原型屬性之間沒有關係。缺點:不能依賴instanceof操做符肯定對象類型

2.7 穩妥構造函數模式

**穩妥對象:**沒有公共屬性,其餘方法也不引用this的對象 與寄生構造模式的不一樣:新建立對象的實例方法不引用this;不使用new操做符調用構造函數

function Person(name, age,job){
	var o = new Object();
	//能夠在這裏定義私有變量和函數
    //添加方法
    o.sayName = function(){
		alert(name);
	};
return o;
}

除本身添加方法,無其餘方式訪問內部數據

3. 繼承

OO語言支持兩種繼承模式:接口繼承和實現繼承 ECMAScript值支持實現繼承

3.1 原型鏈

利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法 SubType繼承SuperType。繼承經過建立SuperType實例,並將該實例賦給SubType.prototype實現的

function SuperType(){
            this.property = true;
        }
        
        SuperType.prototype.getSuperValue = function(){
            return this.property;
        };
        
        function SubType(){
            this.subproperty = false;
        }
        
        //inherit from SuperType
        SubType.prototype = new SuperType();
        
        SubType.prototype.getSubValue = function (){
            return this.subproperty;
        };
        
        var instance = new SubType();
        alert(instance.getSuperValue());   //true
       
        alert(instance instanceof Object);      //true
        alert(instance instanceof SuperType);   //true
        alert(instance instanceof SubType);     //true

        alert(Object.prototype.isPrototypeOf(instance));    //true
        alert(SuperType.prototype.isPrototypeOf(instance)); //true
        alert(SubType.prototype.isPrototypeOf(instance));   //true

1. 別忘記默認的原型 SubType繼承了SuperType,調用instance.toSteing(),實際是調用保存在Object.prototype中的方法 2. 肯定原型與實例的關係 instance操做符和isPrototype()方法 3. 謹慎地定義方法 給原型添加方法放在替換原型語句後 原型鏈繼承不能使用對象字面量建立原型方法 4. 原型鏈的問題 包含引用類型值的原型,修改原型中的數據 建立子類型實例時,不能向超類型的構造函數中傳遞參數

3.2 借用構造函數

在子類型構造函數內部調用超類型構造函數 使用apply()和call()方法在新建立的對象上執行構造函數

function SubType(){  
            //inherit from SuperType
            SuperType.call(this);
        }

        var instance1 = new SubType();
        instance1.colors.push("black");
        alert(instance1.colors);    //"red,blue,green,black"
        
        var instance2 = new SubType();
        alert(instance2.colors);    //"red,blue,green"

借用構造函數的問題 方法都是在構造函數中定義的,函數複用無從談起 超類型原型中定義的方法對子類型不可見

3.3 組合繼承

將原型鏈和借用構造函數技術組合一塊兒 使用原型鏈實現對原型屬性的方法的繼承,經過借用構造函數實現對實例屬性的繼承

function SuperType(name){
            this.name = name;
            this.colors = ["red", "blue", "green"];
        }
        
        SuperType.prototype.sayName = function(){
            alert(this.name);
        };

        function SubType(name, age){  
            SuperType.call(this, name);
            
            this.age = age;
        }

        SubType.prototype = new SuperType();
        
        SubType.prototype.sayAge = function(){
            alert(this.age);
        };
        
        var instance1 = new SubType("Nicholas", 29);
        instance1.colors.push("black");
        alert(instance1.colors);  //"red,blue,green,black"
        instance1.sayName();      //"Nicholas";
        instance1.sayAge();       //29
        
       
        var instance2 = new SubType("Greg", 27);
        alert(instance2.colors);  //"red,blue,green"
        instance2.sayName();      //"Greg";
        instance2.sayAge();       //27
3.4 原型式繼承

藉助原型能夠基於已有對象建立新對象,沒必要新詞建立自定義類型

function object (o){
	functin F() {}
	F.prototype = o;
	return new F();
}

Object.creat()方法

var person = {
            name: "Nicholas",
            friends: ["Shelby", "Court", "Van"]
        };
        
        var anotherPerson = Object.create(person);
        anotherPerson.name = "Greg";
        anotherPerson.friends.push("Rob");
        
        var yetAnotherPerson = Object.create(person);
        yetAnotherPerson.name = "Linda";
        yetAnotherPerson.friends.push("Barbie");
        
        alert(person.friends);   //"Shelby,Court,Van,Rob,Barbie"

第二個參數

var person = {
            name: "Nicholas",
            friends: ["Shelby", "Court", "Van"]
        };
                           
        var anotherPerson = Object.create(person, {
            name: {
                value: "Greg"
            }
        });
        
        alert(anotherPerson.name);  //"Greg"
3.5 寄生式繼承

建立一個僅用於封裝繼承的函數,在函數在內部以某種方式加強對象

functin creatAnother(original){
	var clone = object(original);
	clone.sayHi = function(){
		alert("hi");
	};
	return clone;
}

var person = {
	name: "yohann";
	friends: ["hehe", "haha", "heng"]
};

var anotherPerson = creatAnother(person);
anotherPerson.sayHi();    //hi
3.6 寄生組合式繼承

經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法

function object(o){
            function F(){}
            F.prototype = o;
            return new F();
        }
    
        function inheritPrototype(subType, superType){
            var prototype = object(superType.prototype);   //create object
            prototype.constructor = subType;               //augment object
            subType.prototype = prototype;                 //assign object
        }
                                
        function SuperType(name){
            this.name = name;
            this.colors = ["red", "blue", "green"];
        }
        
        SuperType.prototype.sayName = function(){
            alert(this.name);
        };

        function SubType(name, age){  
            SuperType.call(this, name);
            
            this.age = age;
        }

        inheritPrototype(SubType, SuperType);
        
        SubType.prototype.sayAge = function(){
            alert(this.age);
        };
        
        var instance1 = new SubType("Nicholas", 29);
        instance1.colors.push("black");
        alert(instance1.colors);  //"red,blue,green,black"
        instance1.sayName();      //"Nicholas";
        instance1.sayAge();       //29
        
       
        var instance2 = new SubType("Greg", 27);
        alert(instance2.colors);  //"red,blue,green"
        instance2.sayName();      //"Greg";
        instance2.sayAge();       //27
相關文章
相關標籤/搜索