zoukankan      html  css  js  c++  java
  • 面向对象--对象的创建

    对象的创建
     
    1. Object() 构造函数
         var foo =  new Object();
     
    2. 对象字面量 
         var foo = {name:"name",age:17};
            
    以上两种方式 的缺点是: 在创建多个对象时, 会产生大量的重复代码.
     
    3. 工厂模式
      将公共的部分提取出来 , 用来创建相似的对象.
      示例 :
        function createPerson(name , job , age){
          var o = new Object();
          o.name = name ;
          o.age = age ;
          o.job = job ;
          o.sayName = function(){
            // do something ..
          };
          return o ;
        }
     
      这种模式 , 解决了创建相似对象 , 代码重复的问题
      但是 , 无法检查 它创建的对象到底是什么类型的. 而且代码耦合性太强 ..
     
    4. 构造函数模式
      示例 :
        function Person(name , job , age){
            this.name = name ;
            this.age = age ;
            this.job = job ;
            this.sayName = function(){
              // do something ..
            };
        }
     
        var person = new Person("name", "worker" , 20) ;
        console.log(person instanceof Person); // display true
     
      这种方式 , 与工厂模式的不同之处 :
        a. 没有显示创建对象. 如 var o = new Object();
        b. 直接将属性和方法 赋值给了 this
        c. 没有return 语句
        d. 函数名 最好要首字母大写
        e. 使用时,必须使用 new 操作符.
     
      构造函数 也是 函数的一种 , 因此也可以不使用 new 操作符.
      Person("name", "worker" , 20); // 添加到window
      window.sayName();
      // 在指定作用域中调用
      var o = new Object();
      Person.call(o,"name", "worker" , 20); //添加到 对象o上.
      o.sayName();
     
      使用构造函数模式的缺点是:
        使用构造函数创建的实例, 它们包含的内部函数虽然同名, 但实际上是不同的两个函数(即使处理逻辑是一样的)
        对于同样的方法和逻辑,没有实现抽象,每个实例中都要创建一份
        如果在全局中定义一个函数 , 让构造函数引用它 , 又毁坏了封闭性.
        如果需要多个对象方法呢 , 总不能定义许多全局函数吧.
        且这个可能只能被指定的对象 使用的函数(比如函数内耦合了this.personName),这个'全局函数'的名称也忒名不副实了.
     
      
    =========   以上需要了解  , 以下模式 开始划重点 =========
      
    5. 原型模式
      每个由构造器模式声明的自定义类型内部都有个prototype属性, 它是个指针, 指向原型对象.
      原型对象用来包含特定类型(即你创建对象的类型)的 被所有该类型的实例所共享的属性和方法.
     
      换句话讲, 可以不用在构造函数中定义 对象实例的信息.将这些信息添加到原型对象上.
     
      示例 :
        function Person(){}
     
        Person.prototype.name = "xx";
        Person.prototype.age = 20 ;
        Person.prototype.sayName = function(){
          console.log(this.name);
        };
     
     
      理解 "原型" :
     
        任何函数(不只是构造函数)在创建的时候, 都会默认生成 prototype 属性 , 它是原型对象的指针.
            -- 因此,为了方便理解 下面再提到 Xxxx.prototype 就是指 原型对象 本身,而不是指 Xxxx 的prototype 指针. Xxxx.prototype 的返回值本来就是原型对象.
     
        当使用构造函数(它就是函数)的时候, 同样有个prototype指针 指向 原型对象.
          例如 : Person的prototype指针 指向了 Person.prototype(这里代指Person的原型对象).
     
        当这个 原型对象 创建后 , 默认只有一个constructor属性(即 Xxxx.prototype.constructor , 也是个指针),
            -- 原型对象创建的时机,就是每次调用构造函数的时候.
        它指向了所属原型对象的所在的函数, 比如 Person.prototype.constructor => Person
        再捋一遍路径 , 是个环形的 :
            Person() --> prototype属性 --> Person.prototype --> Person.prototype.constructor --> Person
        除了默认的constructor属性, 原型对象上 还会包含后添加的 自定义的属性和函数.
     
        当使用构造函数 创建对象实例的时候, 对象实例 默认会有个指针(即__proto__属性) 指向了原型对象(Xxxx.prototype)
        
        现在有了两个东西可以指向原型对象了, 分别是: Object 的prototype 以及 object 的__proto__   -- Object 和 object 分别代指 类 和 对象.
     
        好, 这时候就可以捋一下 访问 对象的属性和函数 的查找链了 , 用Person来举例:
          person          -->       person 的 __proto__  -->  Person.prototype       -->        Person.prototype.constructor --> Person
          从对象本身找  (如果没找到)    通过 __proto__ 属性,找到原型对象,看看它有没有      (如果没找到)    通过原型对象的constructor 看看Person 构造函数里有没有.
     
        注意, 虽然对象实例可以访问到原型对象中保存的值, 但是不可以改变它里面的值. (经测试,这行不对,是可以改变的)
        比如对象实例中定义了一个与原型对象中同名的属性,这会屏蔽原型中的同名属性,而不会覆写它,也就是说不会影响到其他对象.
     
        对象实例会从Object那里继承一个方法 hasOwnProperty(propertyName), 该函数来检查 对象实例 本身中是否存在该属性, 如果存在则返回true.
     
     
      原型 与 in 操作符
        in 操作符无论单独使用, 还是在for-in语句中 , 都可以返回对象实例以及原型对象中的可枚举的属性/函数
        因此 可以利用 hasOwnProperty 与 in 配合使用, 来查看 某个指定的属性 是否在原型对象中存在且没有被屏蔽
        function hasPrototypeProperty(target,propertyName){
          return !target.hasOwnProperty(propertyName) && (propertyName in target);
        }
     
      获取对象上的属性
        Object.keys(target)
            返回指定对象上的 所有可枚举的 属性; 如果该对象不是原型对象 , 该函数也不会通过__proto__向上查找.
        Object.getOwnPropertyNames(target)
            返回对象上所有属性 , 无论可不可枚举 ; 同样不会通过__proto__向上查找.
     
     
      更简单的原型写法
        即使用字面量形式来书写.
        但是有个问题是,这时会默认设置它的constructor指向Object,而不是当前构造函数.(不过不影响正常使用instanceof,会得到正确结果)
        为解决这个问题,可以显式声明.但是显式声明会让contructor的枚举特性设置成true.
        所以如果有需要,最好使用Object.defineProperty()来设置一下constructor.
     
        示例:
          function Person(){}
          // var p = new Person();
          Person.prototype = {
            name:"name",
            age:20,
            sayName : function(){
              console.log(this.sayName);
            }
          };
          Object.defineProperty(Person.prototype,"constructor",{enumerable:false});
          // p.sayName();
     
        如果上面的示例中 的注释去掉. 调用p.sayName()会引发错误.
        因为 对象实例 和 构造函数没有直接关系, 只是刚好分别拥有了指向 原型对象的指针而已,这些指针是相互独立的.
        当创建了对象的时候,两个指针所指向的对象是同一个原型对象.
        当Person的prototype指向了 别的原型对象的时候, 对象实例的__proto__依然指向原来的原型对象,并不会受到影响.
        别的原型对象上添加的属性/方法,自然无法体现在原来的原型对象上,从而不会影响到对象实例.
     
        下面的示例,就不会报错 :
          function Person(){}
          var p = new Person();
          Person.prototype.name="xxx";
          Person.prototype.sayName = function(){console.log(this.name)} ;
          p.sayName(); //在p上调用时,查看自身和原型上有没有该方法 -- 只要调用前,在prototype添加过该函数即可.
     
     
        所有原生的引用类型都是使用这种模式创建对象的.例如 Array,String,Date等
        可以通过原生对象的原型对象的引用 来修改/新增 原生对象上的属性/方法.
        当然了,不到万不得已,这种方式是不推荐的.会影响在其他环境中造成冲突(即你也改原型,我也改原型,造成冲突和覆盖)
     
      使用原型模式的缺点:
        当原型对象上的属性是函数或者基本数据类型时,是没有问题的.对象可以屏蔽这个属性/函数.
        但是,当这个属性是个引用类型时,如果是简单的引用赋值还好(即用=改变指针的指向),
        如果使用了操作函数,会相互影响(比如数组,使用push).
        如果你的初衷是不想让它共享,那这就是个问题了.
     
     
    6. 组合使用构造函数模式和原型模式
        创建自定义类型的最常见方式.
        构造函数里定义实例属性,而原型对象里定义方法和共享属性
     
        示例:
          function Person(name , age){
            this.name = name ;
            this.age = age ;
          }
     
          Person.prototype = {
            constructor : Person,
            legsCnt : 2,
            handsCnt : 2,
            headCnt : 1,
            speak: function(language){
              return `i speak ${language}`;
            }
          };
     
     
    7. 动态原型模式
        示例:
          function Person(name , age){
            this.name = name ;
            this.age = age ;
     
            // 判断条件 只需要找 一个 原型对象 创建完成后必定存在的方法即可 -- 找一个就行
            if(typeof this.speak !== "function"){
              Person.prototype.speak = function(language){
                return `i speak ${language}`;
              };
              
              Person.prototype.play = function(){ ... };
            }
          }
     
        这样可以保证,这些方法只加载一次.这种方法 不能用字面量形式重写原型对象.
     
    8. 寄生构造函数模式
      本身和工厂模式没有不同, 只不过调用的时候使用了new操作符
      示例 :
        function createPerson(name , age){
          var o = new Object();
          o.name = name ;
          o.age = age ;
          o.sayName = function(){
            // do something ..
          };
          return o ;
        }
     
        var p = new createPerson('窦唯’60);
     
      直接看看用途吧 , 比如创建一个具有额外方法的数组,由于不能直接改Array的构造函数,就可以使用这个模式.
      示例:
        function SpecialArray(){
          var array = new Array();
     
          array.push.apply(array,arguments);
     
          array.toPipedString = function(){
            return this.join("|");
          }
          return array ;
        }
     
        var arr = new SpecialArray([1,2,3,4,5,6]);
        arr.toPipedString(); // display 1|2|3|4|5|6
     
      这种模式的问题是,不能依赖instanceof来判断这个实例的真实类型.
     
    9.稳妥构造函数模式
        所谓稳妥,就是在构造函数内部不使用this,调用函数的时候不使用new操作符(像调用普通函数一样调用)
        这种模式适合在一些特殊环境中(这种环境不允许使用this和new:比如ADSafe中,Mashup程序中等)
        示例:
          function Person(name){
            var o = new Object();
     
            var name = name ;
            var func = function(arg){
              // do something
              console.log(`arg is ${arg}`);
            };
     
            o.method = function(age){
              console.log(name);
              func();
            }
     
            return o ;
          }
     
          var p = Person();
          p.method(20);
     
          
  • 相关阅读:
    2019牛客暑期多校训练营(第三场)- LRU management
    2019牛客暑期多校训练营(第三场)- F Planting Trees
    HDU1392 Surround the Trees
    2019 Multi-University Training Contest 2
    3101 阶乘分解 (数学)
    Prime Distance POJ
    反素数ant HYSBZ
    B. Nirvana Codeforces Round #549 (Div. 2) (递归dfs)
    C. Queen Codeforces Round #549 (Div. 2) (搜索)
    Cow Relays POJ
  • 原文地址:https://www.cnblogs.com/lmxxlm-123/p/11131853.html
Copyright © 2011-2022 走看看