3

面向对象的程序设计

理解对象

属性类型

  • 创建自定义对象的最简单方式就是创建一个Object的实例,然后再为它添加属性和方法

    var person=new Object();
    person.name="Nicholas";
    person.age=20;
    person.job="Software Engineer";
    person.sayName=function(){
      alert(this.name)
    }
  • 对象字面量称为创建这种对象的首选方式

    var person={
      name:"Nicholas",
      age:20,
      job:"Software Engineer",
      sayName:function(){
        alert(this.name);
      }
    };
  • 两种属性

    • 数据属性,可以读取和写入值,有四个描述其行为的特性

      • [[Configurable]]表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,特性默认值为true
      • [[Enumerable]]表示能否通过for-in循环返回属性,特性默认值为true
      • [[Writable]]表示能否修改属性的值,特性默认值为true
      • [[Value]]包含这个属性的数据值,读取属性值的时候,从这个位置读,写入属性值的时候,把新值保存在这个位置,特性默认值是undefined
      • 要修改属性默认的特性,使用Object.definePropery()方法,这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。

        var person={};
        Object.definePropery(person,"name",{
          writable:false,
          value:"Nicholas"
        });
        alert(person.name);//"Nicholas"
        person.name="Greg";
        alert(person.name);//"Nicholas"
    • 访问器属性,包含一对getter和setter函数。在读取访问器属性的时候,会调用getter函数,这个函数负责返回有效的值。在写入访问器属性的时候,会调用setter函数并传入新值,这个函数负责决定如何处理数据。

      • [[Configurable]]表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性,特性默认值为true
      • [[Enumerable]]表示能否通过for-in循环返回属性,特性默认值为true
      • [[Get]]在读取属性时调用的函数,默认值为undefined
      • [[Set]]在写入属性时调用的函数,默认值为undefined
      • 访问器属性不能直接定义,必须使用Object.defineProperty()来定义

定义多个属性

  • Object.defineProperties()方法可以通过描述符一次定义多个属性,这个方法接收两个对象参数,一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应

    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;
          }
        }
      }
    });

读取属性的特性

  • Object.getOwnPropertyDescription()方法,可以取得给定属性的描述符,这个方法接收两个参数,属性所在的对象和要读取其描述符的属性名称

    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"

创建对象

工厂模式

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");

构造函数模式

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");
  • 只要通过new操作符来调用,那它就可以作为构造函数

    // 当作构造函数使用
    var person = new Person("Nicholas", 29, "Software Engineer");
    person.sayName(); //"Nicholas"
    // 作为普通函数调用
    Person("Greg", 27, "Doctor"); // 添加到 window
    window.sayName(); //"Greg"
    // 在另一个对象的作用域中调用
    var o = new Object();
    Person.call(o, "Kristen", 25, "Nurse");
    o.sayName(); //"Kristen"

原型模式

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();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
  • 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象,默认情况下,所有的原型对象都会自动获得一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针。

图片描述
图片描述
图片描述

组合使用构造函数模式和原型模式

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,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

动态原型模式

function Person(name, age, job){
//属性
this.name = name;
this.age = age;
this.job = job;
// 方法
if (typeof this.sayName != "function"){
  Person.prototype.sayName = function(){
    alert(this.name);
  };
}
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

寄生构造函数模式

function Person(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 friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"

稳妥构造函数模式

function Person(name, age, job){
//创建要返回的对象
var o = new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName = function(){
  alert(name);
};
//返回对象
return o;
}

继承

原型链

图片描述
图片描述

  • 可以通过两种方式来确定原型和实例之间的关系,第一种方式是使用instanceof操作符,只要这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回true

    alert(instance instanceof Object); //true
    alert(instance instanceof SuperType); //true
    alert(instance instanceof SubType); //true
  • 第二种方式是使用isPrototypeOf()方法,只要是原型链中出现过的原型,都可以说是原型链所派生的实例的原型,因此isPrototypeOf()方法也会返回true

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

借用构造函数

function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
// 继承了 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"
  • 对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传递参数

    function SuperType(name){
      this.name = name;
    }
    function SubType(){
      //继承了 SuperType,同时还传递了参数
      SuperType.call(this, "Nicholas");
      //实例属性
      this.age = 29;
    }
    var instance = new SubType();
    alert(instance.name); //"Nicholas";
    alert(instance.age); //29

组合继承

  • 指的是将原型链和借用构造函数的技术组合到一块

    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.constructor = SubType;
    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

原型式继承

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"

寄生式继承

  • 思路和构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,函数在内部以某种方式来增强对象

    function createAnother(original){
      var clone = object(original); //通过调用函数创建一个新对象
      clone.sayHi = function(){ //以某种方式来增强这个对象
        alert("hi");
      };
      return clone; //返回这个对象
    }
    var person = {
      name: "Nicholas",
      friends: ["Shelby", "Court", "Van"]
    };
    var anotherPerson = createAnother(person);
    anotherPerson.sayHi(); //"hi"

寄生组合式继承

  • 无论什么情况下都会调用两次超类型构造函数,一次是在创建子类型原型的时候,另一次实在子类型构造函数内部

    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); // 第二次调用 SuperType()
      this.age = age;
    }
    SubType.prototype = new SuperType(); // 第一次调用 SuperType()
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function(){
      alert(this.age);
    };

图片描述


神膘护体小月半
406 声望6 粉丝