JavaScript 高级程序设计

306 阅读5分钟

第6章 面向对象的程序设计

对象:无序属性的集合,其属性值可以包含基本值,对象或者函数

6.1 理解对象

创建单个对象

  • Object构造函数:var obj = new Object();obj.a=...
  • 字面量:var obj = {a:..}

6.1.1

  • 属性类型

    • 数据属性:
      • 一个数据值的位置,用于读写值
      • 四个描述符
        • [[Configurable]]
        • [[Enumerable]]
        • [[Writable]]
        • [[Value]]
    • 访问器属性:
      • 一堆getter/setter:用于读写值
      • 四个描述符
        • [[Configurable]]
        • [[Enumerable]]
        • [[Get]]
        • [[Set]]
  • 访问器属性不可直接定义,必须调用Object.defineProperty()来调用

6.1.2 定义多个属性:

Object.defineProperties()

6.1.3 读取属性特性:

Object.getOwnPropertyDescriptor()

6.2 创建对象

Object构造函数或字面量不适合创建同一接口的多个对象

6.2.1:工厂模式

  • 原理:定义一个用于创建对象的方法,每次创建对象时调用此方法
  • 定义
    function createPerson(name){
        var o = new Object();
        o.name = name;
        ...
        return o;
    }
    var a = createPerson('小明')
    
  • 缺点:无法标识创建对象为某个特定类型

6.2.2:构造函数模式

  • 原理:函数的构造调用
    • 创建一个新对象
    • 将构造函数的this指向新对象
    • 执行构造函数中的代码
    • 返回新对象(如果代码中无返回时)
  • 定义
    function Person(name){
        this.name = name;
        ...
    }
    var a = new Person('小明')
    
  • 优点:实例可以标识为某个特定类型
  • 缺点:每个方法都要在每个实例上重新创建一遍(不同实例的同名函数并不是同一个)

6.2.3:原型模式

  • 原理:原型链
    • 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,此属性为一个对象,称为该函数的原型对象。默认情况下,原型对象会默认添加一个constructor属性,其值为该函数(所在的指针)
    • a.isPropertyOf(b):a是否需为b的原型对象
    • Object.getPropertyOf():获取某个对象的原型对象
    • obj.hasOwnProperty('b'):obj中是否有b属性(只在当前对象上,不在原型链上)
    • 'b' in obj:obj中是否有b属性(在实例上或者原型链上)
    • for .. in:只会出现enumerable为true的属性
    • Object.getOwnPropertyNames():获取某个对象的所有实例属性(和enumerable无关)
    • Object.keys():获取实例所有可枚举属性
  • 定义
    function Person(name){}
    Person.prototype.name = ".."//完全区别于Person.name="..",如果这样写,a实例是访问不到name的,只有当前函数Person可以访问的到
    ...
    var a = new Person()
    
  • 优点:
    • 可以标识为某个特定类型
    • 方法共享
  • 缺点:
    • 实例的所有属性都相同,无法通过传参进行定制化
    • 当其属性值为引用类型时,共享的属性会被实例修改

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

  • 原理:构造函数 + 原型
    • 构造函数:通过传参自定义实例属性
    • 原型:通过原型定义共享属性和方法
  • 定义
    function Person(name){
        this.name = ".."
    }
    Person.prototype = {
        constructor:Person,
        ...
    }
    ...
    var a = new Person()
    
  • 优点:
    • 可以标识为某个特定类型
    • 方法共享
    • 可以通过传参定制实例属性,共享公共属性(仍然可以修改)

6.2.5:动态原型模式

  • 原理:将原型模式封装进构造方法中,并使原型的定义只发生在第一次
  • 定义
    function Person(name){
        this.name = "..";
        if(typeof this.sayName !== 'function'){
            Person.prototype.sayName = function(){...}
        }
    }
    var a = new Person()
    
  • 优点:
    • 可以标识为某个特定类型
    • 方法共享
    • 可以通过传参定制实例属性,共享公共属性(仍然可以修改)

6.2.6:寄生构造函数模式:

  • 原理:工厂模式的构造函数形式
  • 定义
    function Person(name){
        var o = new Object();
        o.name = name;
        ...
        return o;
    }
    var a = new Person('小明')
    
  • 缺点:无法标识创建对象为某个特定类型

6.2.7:稳妥构造函数模式:

  • 原理:稳妥对象
    • 没有公共属性,且其他方法也不引用this对象
    • 不可直接访问对象的属性,而是通过方法调用
  • 定义
    function Person(name){
        var o = new Object();
        //只有私有属性
        ...
        o.sayName = functiuon(){...}
        return o;
    }
    var a = new Person('小明')
    

6.3 继承

继承分为接口继承和实现继承,js中只能实现实现继承

6.3.1 原型链继承

  • 原理:重写子函数(类)的prototype属性为父函数(类)的实例
  • 定义
    function Super(){};
    function Sub(){};
    Sub.prototype = new Super();
    var instance = new Sub()
    
  • 缺点:
    • 父类包含引用类型的值时,会被重写
    • 创建子类实例时,无法向父类构造函数传参

6.3.2 借用构造函数

  • 原理:在子类构造函数中调用父类构造函数
  • 定义
    function Super(){};
    function Sub(){
        Super.call(this)
    };
    var instance = new Sub()
    
  • 优点:可传参
  • 缺点:父中函数无法复用,在每个子类中都需重新定义

6.3.3 组合集成

  • 原理:原型链 + 构造函数
    • 使用原型链实现对原型属性和方法的继承
    • 通过构造函数实现对实例属性的继承
  • 定义
    function Super(name){
        this.name = name;
    };
    Super.prototype.sayName = function(){...}
    function Sub(name,age){
        Super.call(this.name);
        this.age = age;
    };
    Sub.prototype = new Super();
    Sub.prototype.constructor = Sub;
    Sub.prototype.sayAge = function(){...}
    var instance = new Sub()
    
  • 优点:可传参,可复用
  • 缺点:Super构造函数调用两次

6.3.4 原型式继承

Obect.create(o)

    createObject(o){
        function F(){}
        F.prototype = o;
        return F;
    }

6.3.5 寄生继承

  • 原理:创建一个仅用于封装继承过程的函数,在函数内部增强对象再返回
  • 定义
    function createAnother(original){
        var clone = Obect.create(original);//或者createObject(original)
        clone.sayHi = function(){}
        return clone;
    };
    var instance = createAnother();
    

6.3.6 寄生组合式继承

  • 原理:
    • 父级:
      • 构造函数:通过传参定义实例定制属性,定义父级共享属性
      • 原型上:定义父级共享属性和方法
    • 子级:
      • 构造函数:
        • 调用父级构造函数并传参
        • 定义子级共享属性
      • 原型:
        • 指向父级原型
        • 重定向constructor
        • 定义子级共享属性和方法,重写父级方法
  • 定义
    function Super(...){...};
    Super.prototype... = ...;
    function Sub(...){
        Super.call(...);
        ...
    };
    Sub.prototype = Object.create(Super)
    Sub.prototype.constructor = Sub;
    ...
    var instance = new Sub()