【红宝书】第6章.面向对象的程序设计

面向对象(Object-Oriented,OO

6.1理解对象

//创建Object的实例,然后添加属性和方法
    let person = new Object();
    person.name = "AAA";
    person.sayName = function() {};

//对象字面量模式
    let person = {
      name: "AAA",
      sayName: function() {}
    };

6.1.1属性类型

用特性(attribute)描述属性(property)的各种特征

两种属性:1.数据属性2.访问器属性

1.数据属性

[[configurable]]:能否delete属性后从而重定义属性,能否修改属性的特性,能否把属性修改为访问器属性。默认true。改为false后不可逆。

[[enumerable]]:能否for-in循环返回属性。默认true

[[writable]]:能否修改属性的值。默认true

[[value]]:属性的数据值。默认undefined

let person={name:’AAA’},如此直接定义时特性取默认值和数据值。

1.访问器属性

[[configurable]]:能否delete属性后从而重定义属性,能否修改属性的特性,能否把属性修改为访问器属性。默认true。改为false后不可逆。

[[enumerable]]:能否for-in循环返回属性。默认true

[[get]]:读取属性时调用的函数。默认undefined

[[set]]:写入属性时调用的函数。默认undefined

6.1.2定义属性的特性

定义单个

  Object.defineProperty(obj, property, descriptor); // descriptor 描述符
    descriptor = {
      configurable: true,
      enumerable: true,
      writable: true,
      value: "example"
    };

定义多个

    Object.defineProperties(obj, {
      property: {
        configurable: true,
        enumerable: true,
        get: function() {},
        set: function() {}
      }
    });

6.1.3读取属性的特性

Object.getOwnPropertyDescriptor(obj,property)

Object.getOwnPropertyDescriptors(obj)

6.2创建对象

6.2.1工厂模式

用函数来封装以特定接口创建对象的细节

 function createPerson(name) {
      let obj = {
        name: name,
        sayName: function() {
          alert(this.name);
        }
      };
      return obj;
    }
    let person1 = createPerson("lpr");

虽然解决了多个相似对象的问题,但却没有解决对象识别的问题(即怎么知道一个对象的类型)

6.2.2构造函数模式

   function Person(name) {
      this.name = name;
      this.sayName = function() {
        alert(this.name);
      };
    }
let person1 = new Person("lpr");
let person2 = new Person("lpd");

跟工厂模式的不同:

  1. 没有显示的创建对象
  2. 直接将属性和方法赋给了this对象
  3. 没有return语句
  4. 函数名Person使用的是大写P(构造函数以大写字母开头,非构造函数以小写)

改造函数实际经历4个步骤

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象,即this指向这个新对象
  3. 执行构造函数中的代码,即为新对象添加属性和方法
  4. 返回新对象

这样创建的实例都有一个constructor(构造函数)属性,该属性指向Person

person1.constructor===person2.constructor===Person

person1 instanceof Object // true

person1 instanceof Person // true

因此可以得出实例来自同一种类型,但是其中的方法不是同一个Function实例

解决方案:

    function Person(name) {
      this.name = name;
      this.sayName = sayName();
    }
    function sayName() {
      alert(this.name);
    }

这样做确实解决了两个函数做同一件事的问题,新问题:1.全局定义的方法直给某一个对象调用,全局作用域名不副实2.多个方法多次在全局定义函数,没有封装性

6.2.3原型模式

    function Person() {}
    Person.prototype.name = "AAA";
    Person.prototype.sayName = function() {};
    let person1 = new Person();
    let person2 = new Person();
    person1.sayName===person2.sayName

Person.prototype.isPrototypeOf(person1); // true
Object.getPrototypeOf(person1) === Person.prototype; // true

2.原型与in操作符

    person1.hasOwnProperty("name"); // 是否自身存在属性

    alert("name" in person1); // 属性是否存在于自身或原型中

可组合判断属性存在实例还是原型中

    Object.keys(obj); // 获取对象上自身可枚举的属性(不含Symbol),返回结果是key的数组

    Object.getOwnPropertyNames(); // 获取对象上自身所有(可枚举+不可枚举)的属性,如'constructor'

3.更简单的原型语法

    function Person() {}

    Person.prototype = {
      constructor: Person,
      // 每创建一个函数会同时创建它的prototype对象,这个对象也会自动获取constructor属性。
      // 如果不重新指定,即实例的constructor不指向Person
      // 即person1.constructor !== Person,因此要重新指定建立联系
      // 不过这种方式会使constructor属性的特性[[Enumerable]]为true,变为可枚举
      name: "AAA",
      sayName: function() {
        console.log(this.name);
      }
    };
//但是上述constructor变成可枚举了 Object.defineProperty(Person.prototype, "constructor", { enumerable: false, value: Person });

4. 原型的动态性

 

 

重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系。因此要先设置原型 ,再声明实例

 原型对象的问题

例如原型中有个属性是数组,在实例中push,则添加到了原型中的数组里。即变成共享。

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

构造函数用于实例属性,原型模式用于方法和共享方法。

每个实例都有自己的一份实例属性,又同时共享对方法的应用。是使用最广泛、认同度最高的创建自定义类型的方法。

6.3继承

6.3.1原型链

    function SuperType() {
      this.property = true;
    }
    SuperType.prototype.getSuperValue = function() {
      return this.property;
    };
    function SubType() {
      this.subproperty = false;
    }
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function() {
      return this.subproperty;
    };
    let instance = new SubType();
    alert(instance.getSuperValue()); // true

 

1. 默认原型

最顶层的原型为Object.prototype

 

2. 确定原型和实例的关系
// 都为true
instance instanceof Object
instance instanceof SuperType
instance instanceof SubType

Object.prototype.isPrototypeOf(instance)
SuperType.prototype.isPrototypeOf(instance)
SubType.prototype.isPrototypeOf(instance)
3. 谨慎定义方法

给原型添加方法的代码一定要放在替换原型的语句之后

通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做会重写原型链

4. 原型链的问题
  1. 原型对象的问题
  2. 创建子类型的实例时,不能向超类型的构造函数中传递参数。即没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。

6.3.2借用构造函数(伪造对象或经典继承)

    function SuperType() {
      this.color = ["red", "green", "blue"];
    }
    function SubType() {
      SuperType.call(this); //借调了超类型的构造函数
    }
传递参数
    function SuperType(name) {
      this.name = name;
    }
    function SubType() {
      SuperType.call(this, "lpr");
    }
借用构造函数的问题

构造函数的问题

6.3.3组合继承

    function SuperType(name) {
      this.name = name;
      this.color = ["red", "green", "blue"];
    }
    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);
    };

    let instance1 = new SubType("lpr", 17);
    instance1.color.push("black"); // ["red", "green", "blue","black"]
    instance1.sayName(); // 'lpr'
    instance1.sayAge(); // 17

    let instance2 = new SubType("lpd", 16);
    instance2.color; // ["red", "green", "blue"]
    instance1.sayName(); // 'lpd'
    instance1.sayAge(); // 16

6.3.4原型式继承

6.3.5寄生式继承

6.3.6寄生组合式继承

posted @ 2019-12-22 17:19  把我当做一棵树叭  阅读(183)  评论(0)    收藏  举报