zoukankan      html  css  js  c++  java
  • 第六章 面向对象的程序设计

    属性类型

    1.数据属性特性

    [[Configurable]](可配置):能否通过delete删除属性重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。

    [[Enumerable]](可枚举):能否通过for-in循环返回属性。

    [[Writable]](可写):能否修改属性的值。

    [[Value]](值):包含这个属性的数据值。读写属性值都再这,默认为undefined。

    修改属性默认的特性,必须使用Object.defineProperty()方法。

    Object.defineProperty(): 接受三个参数:属性所在的对象,属性的名字,描述符对象。属性描述符对象必须是 configurable、enumberable、writable、value。其中的一或多个。

    var person = {};
    Object.defineProperty(person, "name", {
        writable: false,               //设置为不可写的。(不能修改name的值)
        configurable: false,           //设置为不可配置的,
        value: "Nicholas"
    });
    console.log(person.name);
    // Nicholas
    delete person.name; 
    console.log(person.name);
    // Nicholas
    person.name = "Gray"; 
    //修改了name的值
    console.log(person.name); //修改的值无效


    //
    Nicholas

    在调用Object.defineProperty(),如果不指定,configurable,wirtable,enumerable默认值为false.

    2、访问器属性

    访问器属性不包含数据值。包含一对getter函数和setter函数。

    [[Configurable]](可配置):能否通过delete删除属性重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。

    [[Enumerable]](可枚举):能否通过for-in循环返回属性。

    [[Get]]:读取属性时调用的函数。

    [[Set]]: 写入属性时调用的函数。

    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;
    console.log(book.edition);
    // 2

    IE9之前创建访问器属性的方法:

    var book = {
        _year: 2004,
        edition: 1
    }
    book.__defineGetter__("year", function(){
         return this._year;
    });
    
    book.__defineSetter__("year", function(newValue) {
        if(newValue > 2004) {
          this._year = newValue;
          this.edition +=newValue - 2004;
        }
    });
    
    book.year = 2005;
    console.log(book.edition);
    // 2

    Object.defineProperties(): 通过描述符一次定义多个属性。接受2个对象参数:第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性对应。

    var book = {};
    
    Object.defineProperties(book, {
         _year: {
            writbale: true,
            value: 2004
        },
        edition: {
           writable: true,
           value: 1
       },
       
       year: {
          get: function() {
             return this._year;
          },
    
         set: function(newValue) {
           if(newValue > 2004) {
             this._year = newValue;
             this.edition += newValue - 2004;
           }
        }
      }
          
    });
    
    book.year = 2007;
    console.log(book.edition);
    var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
    console.log(descriptor.value);
    //  4
    //  2004
    //  false

    创建对象

    1.工厂模式

    工厂模式解决了创建多个相似对象的问题。没有解决对象识别的问题(怎样指定一个对象的类型)。

    function createPerson(name, age, job) {
      var o = new Object();
      o.name = name;
      o.age = age;
      o.job = job;
      o.sayName = function() {
        console.log(this.name);
       };
      return o;
    }
    
    var person1 = createPerson("zhangsan", 28, "Software Engineer");
    var person2 = createPerson("李四", 25, "Docter");
    console.log(person1.name);
    console.log(person2.name);
    //  zhangsan
    //  李四

    2.构造函数模式


    构造函数始终都应该以一个大写字母开头,非构造函数以小写字母开头。以这种方式定义的构造函数是定义再Global对象,再浏览器即window对象下。

    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = function() {
            console.log(this.name);
        }
    }
    var person3 = new Person("Nicholas", 27, "Software Enginner");   var person4 = new Person("Gray", 25, "Docter");
    console.log(person3.name);
    console.log(person4.name);

    console.log(person3 instanceof Object);
    console.log(person3 instanceof Person);
    console.log(person4 instanceof Object);
    console.log(person4 instanceof Person);

    // Nicholas
    // Gray
    // true
    // true
    // true
    // true

    调用 Person构造函数经历了以下4个步奏:

    1.创建一个新对象。

    2、将构造函数的作用域赋给新对象(这里的新对象是person3,this指向person3)

    3、执行构造函数中的代码。

    4.返回新对象。

    构造函数的优点:将来可以将它的实例标识为一直特定的类型。

    构造函数的缺点:每个方法都要再每个实例上重新创建一遍。函数也是对象,每定义一个函数,即实例化了一个对象。

    原型模式

    创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象。即原型对象。作用是让所有对象实例共享它包含的属性和方法。

    function Person() { }
    Person.prototype.name = "Nicholas";                              
    Person.prototype.age = 27;
    Person.prototype.job = "Software Enginner";
    Person.prototype.sayName = function() {
       console.log(this.name);
    };
    //将所有属性和方法直接添加到prototype原型中。这里的Person.prototype.constructor指向他的构造函数,即Person
    
    var person1 =new Person();
    person1.sayName();
    // Nicholas 
    
    var person2 = new Person();
    person2.sayName();
    // Nicholas
    
    console.log(person1.sayName === person2.sayName);
    // true  person1实例和person2实例访问的都是同一组属性和同一个sayName()函数

    原型对象自动获得一个constructor属(构造函数)性,constructor包含一个指向prototype属性所在的函数的指针。

    EC5 把这个指针叫[[prototype]]。这个链接存在于实例与构造函数的原型对象之间。

    可以用isPrototyOf():确认实例和构造函数之间是否存在这种关系。

    console.log(Person.prototype.isPrototypeOf(person1));
    console.log(Person.prototype.isPrototypeOf(person2));
    // true 用原型对象测试person1和person2.它们内部都有一个指向Person.prototype的指针。
    // true

    Object.getPrototypeOf(): 返回[[prototype]]值。

    console.log(Object.getPrototypeOf(person1) == Person.prototype);
    console.log(Object.getPrototypeOf(person1).name);
    // true
    // Nicholas

    通过对象的实例可以访问原型中的值,不能通过对象实例重写原型中的值,再实例中定义的同名属性的值,会屏蔽原型中的值。但是不会修改原型中的值。

    function Person() { }
    Person.prototype.name = "Nicholas";
    Person.prototype.age = 27;
    Person.prototype.job = "Software Enginner";
    Person.prototype.sayName = function() {
       console.log(this.name);
    };
    
    var person1 =new Person();
    person1.sayName();
    
    var person2 = new Person();
    person2.name = "张三";
    console.log(person1.name);
    console.log(person2.name);
    //  Nicholas
    //  Nicholas        来着原型
    //  张三            来自实例

    hasOwnProperty():用于检测一个属性是存在于实例还是原型中。从Object继承而来。在实例中返回true,在原型中返回false;

    console.log(person1.hasOwnProperty("name"));
    console.log(person2.hasOwnProperty("name"));
    // false 来自原型
    // true 来自实例

    Object.keys(): 取的对象中所有可枚举的实例属性,接受一个参数:一个对象。返回一个包含所有可枚举属性的字符串数组。

    function Person() { }
    Person.prototype.name = "张三";
    Person.prototype.age = 25;
    Person.prototype.job = "Software Enginner";
    Person.prototype.sayName = function() {
       console.log(this.name);
         };
    var keys = Object.keys(Person.prototype);
    console.log(keys);
    
    var p1 = new Person();
    p1.name = "Rob";
    p1.age = 27;
    var p1keys = Object.keys(p1);
    console.log(p1keys);
    
    //  ["name", "age", "job", "sayName"]
    //  ["name", "age"]
    //如果要得到所有的实例属性不论是否可枚举,可使用Object.getOwnPropertyNames()
    var keys1 = Object.getOwnPropertyNames(Person.prototype);
    console.log(keys1);
    //[”constructor“, "name", "age", "job", "sayName"]

    更简介的原型语法

    function Person() {}
    
    Person.prototype = {
      name: "张三",
      age: 27,
      job: "Software Enginner",
      sayName: function() {
          console.log(this.name);
      }
    };

    这种形式的原型语法和之前的原型语法结果一样,但是constructor不在指向原型对象的函数。可以通过显式的设置,让它指向正确。

    但是Enumerable特性被设置为:true;

    function Person() {}
    
    Person.prototype = {
     constructor: Person,  //显式的设置constructor值
      name: "张三",
      age: 27,
      job: "Software Enginner",
      sayName: function() {
          console.log(this.name);
      }
    };
    Object.defineProperty(Person.prototype,"constructor", { enumerable: false, value: Person });

    原型模式的问题

    1、所有实例默认情况下都将取的相同的值。

    2、在其中一个实例中的修改,会影响其他的实例的属性。像一个实例中添加属性,不是同名的字符串,都是再原型中添加。

    function Person() {}
    
    Person.prototype = {
      name: "张三",
      age: 27,
      job: "Software Enginner",
      friends: ["Shelby", "Court"],
      sayName: function() {
          console.log(this.name);
      }
    };
    
    Object.defineProperty(Person.prototype,"constructor", {
           enumerable: false,
           value: Person
    });
    
    var person3 = new Person();
    var person4 = new Person();
    
    person3.friends.push("Van");
    console.log(person3.friends);
    console.log(person4.friends);
    // ["Shelby", "Court", "Van"]
    // ["Shelby", "Court", "Van"]

    构造函数与原型混成的模式,是最广泛,认同度最高的一直创建自定义类型的方法。

    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ["Shely", "Court"];
    }
    
    Person.prototype = {
          constructor: Person,
          sayName: function() {
               console.log(this.name);
          }
    };
    
    var person1 = new Person("张三", 24, "Docter");
    var person2 = new Person("李四", 25, "后台");
    
    person1.friends.push("刘五");
    console.log(person1.friends);
    console.log(person2.friends);
    console.log(person1.friends === person2.friends);
    // ["Shely", "Court", "刘五"]
    // ["Shely", "Court"]
    // false

     继承

    EC只支持实现继承,实现继承依靠原型链来实现。利用原型让一个引用类型继承另一个引用类型的属性和方法。

    实现原型链的基本模式

    function SuperType() {
       this.property = true;
       }
    
    SuperType.prototype.getSuperValue = function() {
        return this.property;   
    };           //在SuperType的原型中定义一个方法
      
    function SubType() {
        this.subproperty = false;                      
      }
    
    SubType.prototype = new SuperType();              //把superType当作实例,赋给SubType的原型,(实例对象包含一个指针[[prototype]],指向构造函数的原型对象),SubType继承了 SuperType
    SubType.prototype.getSubValue  = function() {     //为SubType添加了一个新方法
           return this.subproperty;
        };
    //重新父原型中的方法 代码需放在替换原型的语句之后 SuperType.prototype.getSuperValue = function() {
    return false;

      }

       var instance = new SubType();
    console.log(instance.getSuperValue());
    //   false

     原型链继承的缺点:

    1.包含引用类型值的原型属性会被所有实例共享;

    2.没有办法在不影响所有对象实例的情况下,给父原型对象的构造函数传递参数。

    function SuperType() {
      this.color = ["yellow", "blue", "green", "red"];
    }
    
    function SubType() {}
    SubType.prototype = new SuperType();
    var instance1 = new SubType();
    instance1.color.push("black");
    
    console.log(instance1.color);
    
    var instance2 = new SubType();
    console.log(instance2.color);
    
    
    //  ["yellow", "blue", "green", "red", "black"]
    //  ["yellow", "blue", "green", "red", "black"]

    借用构造函数

    即在子类型构造函数的内部调用超类型构造函数。函数只不过是在特定环境中执行代码的对象,通过使用call()或apply()方法在新创建的对象上执行构造函数。

    function colorsItem() {
       this.color = ["red", "blue", "green"];
    }
    
    function colorsItem2 () {
       colorsItem.call(this);                 //借调“colorsItem”构造函数,实际上是在colorItem2的实例下调用“colorsItem”构造函数
    }
    
    var instance3 = new colorsItem2();       // 实例化一个对象
    instance3.color.push("black");           //  在 colorsItem2的其中一个实例中执行colorsItem2的初始化代码,顺便把一个值推进副本中去。每个实例化对象都拥有各自的color属性的副本
    console.log(instance3.color);
    
    var instance4 = new colorsItem2();
    console.log(instance4.color);
    // ["red", "blue", "green", "black"]
    // ["red", "blue", "green"]

    组合继承

    使用原型链实现原型属性和方法的继承,通过借用构造函数实现对实例属性的基础。

    在原型上定义方法实现函数复用,又能够保证每个实例有自己的属性。

    function SuperType1(name) {
      this.name = name;
     this.colors = ["red", "blue", "yellow"];
    }
    SuperType1.prototype.sayName = function () {
          console.log(this.name);
    }
    function SubType1(name, age) {
       SuperType1.call(this, name);
       this.age = age;
     }
    
    SubType1.prototype = new SuperType1();
    SubType1.prototype.consctructor = SubType1;
    SubType1.prototype.sayAge = function() {
       console.log(this.age);
    }
    
    var instances = new SubType1("张三", 27);
    instances.colors.push("black");
    console.log(instances.colors);
    instances.sayName();
    instances.sayAge();
    
    // ["red", "blue", "yellow", "black"]
    // 张三
    // 27
    var instances1 = new SubType1("李斯", 25);
    console.log(instances.colors);
    instances1.sayName();
    instances1.sayAge();
    
    
    // ["red", "blue", "yellow", "black"]
    // 李斯
    // 25

    优点: 可以让不同的实例分别拥有自己的属性又可以使用不同的方法。instanceof和isPrototypeOf()也能识别基于组合继承创建的对象。也是最常用的继承模式。

    缺点: 会调用俩次超类型函数,一次是创建子类型函数,另一次在子类型函数内部。

    原型式继承

     EC5的Object.create()方法:接受2个参数:用作新对象原型的对象和(可选)一个为心对定义额外属性的对象;

    var person = {
        name: "Nicholas",
        friends: ["Shelby", "Court", "Van"]
     };
    
    var anotherPerson = Object.create(person, {
         name: {
            value: "Greg"
         }
    });
    
    console.log(anotherPerson.name);
    // Greg

    寄生式继承

    创建一个用于封装继承过程的函数, 该函数在内部以某种方式来增强对象,最后再像针对是它做了所有工作一样返回对象。

    function createAnother(original) {
       var clone = object(original);
       clone.sayHi = function() {
            console.log("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() {
         console.log(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 () {
        console.log(this.age);
    };

    第一次调用,SubType得到俩个属性,SuperType构造函数的color 和name属性。调用SubType又会再一次调用SuperType构造函数。这一次在新对象上创建实例属性name和color,这俩个同名属性屏蔽了原型中的同名属性。

    寄生组合式继承: 通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

    思路:使用寄生式继承来继承超类型的原型,将结果指定给子类型的原型。

    寄生组合式继承的模式 是引用类型最理想的继承范式

    function inheritPrototype(SubType, SuperType) {
            var prototype = Object(SuperType.prototype);       //创建对象,创建超类型原型的一个副本
            prototype.consctructor = SubType;                   //增强对象  为创建的副本添加constructor属性
            SubType.prototype = prototype;                      //指定对象  将新创建的对象赋值给子类型的原型。
       }
     function SuperType(name) { 
    this.name = name; 
    this.colors = ["red", "blue", "yellow"]; 
    } 
    SuperType.prototype.sayName = function() {
     console.log(this.name); 
    }; 
    function SubType(name, age) { 
    SuperType.call(this, name); 
    this.age = age; 
    } 
    inheritPrototype(SubType, SuperType); 
    Subtype.prototype.sayAge = function () { 
    console.log(this.age); 
    };

     

  • 相关阅读:
    【英语天天读】Feed Your Mind
    【IBM Tivoli Identity Manager 学习文档】4 TIM基本概念
    【英语天天读】Dreams
    【嵌入式开发技术之串口】Linux下串口主机程序
    【IBM Tivoli Identity Manager 学习文档】3 系统部署
    【IBM Tivoli Identity Manager 学习文档】2 部署准备知识
    【Linux开发技术之常见问题】一个建立线程时常见的问题:invalid conversion from `void*' to `void*(*)(void*)
    C# winform combobox 在绑定数据之后插入一项选择项
    Response.Redirect在新窗口打开 && 3.0扩展方法
    sql字符转换函数大全
  • 原文地址:https://www.cnblogs.com/zhangbaihua/p/5569221.html
Copyright © 2011-2022 走看看