js 面向对象的程序设计之构造

283 阅读5分钟
引言

面向对象的程序设计

理解对象

对象主要包括数据属性访问器

  • 简单创建一个对象
    var person=new Object();
    person.name="zhangsan";
    person.age=24;
    person.job="IT";
    person.sayName=function(){
        console.log("人员名:"+person.name);
    }
    person.sayName();
    //打印结果
    人员名:zhangsan
  • 对象里的数据属性:Configurable,Enumerable,Writable,Value

    1. Configurable: 是否可以删除对象的属性,默认为true.

    2.Enumberable: 是否可以通过for-in循环返回属性,默认为true.

    3.Writable: 是否可以修改属性的值,默认为true.

    4.Value:保存属性值,读取和修改属性值都从这个位置,默认为undefined.

    var person={};
    Object.defineProperty(person,'name',{writable:false,value:"zhangsan"});
    console.log("修改前:"+person.name);
    person.name="lisi";
    console.log("修改后:"+person.name);
    var person={};
    Object.defineProperty(person,'name',{configurable:false,value:"zhangsan"});
    delete person.name;
    //删除操作会报错
    //重新设置configurable为true也会报错,也就说只能设置一次
  • 访问器的属性:Configurable,Enumberable,Get,Set,主要介绍Get,Set

    1. Get: 调用该函数获取属性值,函数也是一种变量,默认为undefined.
    2. Set: 调用该函数设置属性值,默认为undefined.
     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+=1;
         //     }
         // }
     })
     book.year=2005;
     console.log("没修改成功:"+book.year);
     console.log(book.edition);
     //打印结果
     没修改成功:2004
     1

私以为和WritableValue,区别在于不仅能修改自身的值,还能关联修改其他属性的值。

  • 同时定义多个属性值,用Object.defineProperties设置,可以通过
    var book={};
    Object.defineProperties(book,{
        year:{value:25},
        name:{
            get:function(){
                return "张三";
            }
        }
    })
  • 读取属性的特性值: Object.getOwnPropertyDescriptor,传入两个参数:对象属性名,根据属性名不同获取的是数据属性或者访问器属性.
    var book={};
    Object.defineProperties(book,{
        year:{value:25},
        name:{
            get:function(){
                return "张三";
            }
        }
    });
    var data=Object.getOwnPropertyDescriptor(book,"name");
    console.log("获取姓名:"+data.get());
    var param=Object.getOwnPropertyDescriptor(book,"year");
    console.log("获取年龄:"+param.value);
    //打印结果
    获取姓名:张三
    获取年龄:25
创建对象
  • 工厂模式: 无法用instanceOf判断类型
    function createPerson(name,age){
        var person=new Object();
        person.name=name;
        person.age=age;
        person.sayName=function(){
            console.log(this.name);
        }
        return person;
    }
     var person1=createPerson("zhangsan",25);
     console.log("姓名:"+person1.name);
     //打印结果
     姓名:zhangsan
  • 构造函数的模式: 函数命令格式为大驼峰方式
    function Person(name,age){
        this.name=name;
        this.age=age;
        this.sayName=function(){
            console.log(this.name);
        }
    }
    var person1=new Person("zhangsan",58);
    console.log(person1.name);
    //打印结果
    zhangsan

创建的对象都是不同的,并且可以用instanceOf判断类型

    function Person(name,age){
        this.name=name;
        this.age=age;
        this.sayName=function(){
            console.log(this.name);
        }
    }
    var person1=new Person("zhangsan",58);
    var person2=new Person("zhangsan",58);
    console.log(person1==person2);
    console.log(person1 instanceof Person)
    //打印结果
    false
    true

这样每次创建的对象里的函数都是不同的,而执行功能的函数没必要不相同,可以把函数抽取出来

    function Person(name,age){
        this.name=name;
        this.age=age;
        this.sayName=sayName;
    }
    function sayName(){
        console.log(this.name);
    }
    var person1=new Person("zhangsan",58);
    var person2=new Person("zhangsan",58);
    console.log(person1.sayName==person2.sayName);
    //打印结果
    true
  • 原型模式
  1. 只要创建了一个函数,就会根据特定规则为函数生成一个prototype属性,该属性指向原型对象
     function Person(){
     }
     Person.prototype.name="zhang san";
     Person.prototype.age=15;
     Person.prototype.job="IT";
     Person.prototype.sayName=function(){
         console.log(this.name);
     }
     var person1=new Person();
     var person2=new Person();
     console.log(person1.sayName=person2.sayName)
     //打印结果
     true
  1. 而在默认情况下,每个原型对象都会自动获得一个constructor属性,该属性指向 prototype 属性所在函数解如下:
   function Person(){
   }
   Person.prototype.name=zhangsan;
   Person.prototype.age=15;
   Person.prototype.job="IT";
   Person.prototype.sayName=function(){
       console.log(this.name);
   }
   var person1=new Person();
   var person2=new Person();
   console.log("person2的原型是否是Person的原型:"+(Object.getPrototypeOf(person2)==Person.prototype));
   console.log("person2的name的值:"+Object.getPrototypeOf(person2).name);
   //打印结果
   true
   person2的name的值:zhangsan 
  1. 每当代码读取对象的某个属性时,会在对象实例本身查找,如果找不到再顺着原型链去查找.
    function Person(){
    }
    Person.prototype.name="zhangsan";
    var person1=new Person();
    var person2=new Person();
    person1.name="lisi";
    console.log("person1 name重新赋值:"+person1.name);
    console.log("person2 name未操作:"+person2.name);
    //打印结果
    person1 name重新赋值:lisi
    person2 name未操作:zhangsan
  1. 判断实例中是否包含某个属性
    function Person(){
    }
    Person.prototype.name="zhangsan";
    var person1=new Person();
    console.log("person1是否包含name属性:"+(person1.hasOwnProperty("name")));
    person1.name="lisi";
    console.log("person1是否包含name属性:"+(person1.hasOwnProperty("name")));
   //打印结果
   person1是否包含name属性:false
   person1是否包含name属性:true
  1. 判断实例或者原型中是否包含某个属性
    function Person(){
    }
    Person.prototype.name="zhangsan";
    function hasOwnPrototypeProperty(object,prop){
        return !object.hasOwnProperty(prop)&& (prop in object);
    }
    var person1=new Person();
    console.log("原型中含有实例中不含有:"+hasOwnPrototypeProperty(person1,"name"));
    person1.name="lisi";
    console.log("实例中含有:"+ person1.hasOwnProperty("name"));
  1. 获取对象上可枚举的所有实例属性:Object.keys(),传入原型对象时获取的是原型对象的属性,传入实例对象获取的是实例对象独有的属性。
    function Person(){
    }
    Person.prototype.name="zhangsan";
    Person.prototype.age=25;
    Person.prototype.job="IT";
    var person=new Person();
    person.score=58;
    var prop=Object.keys(Person.prototype);
    console.log("原型对象属性:"+prop);
    var props=Object.keys(person);
    console.log("实例对象属性:"+props);
    //打印结果
    原型对象属性:name,age,job
    实例对象属性:score
  1. 获取不可枚举属性,感觉有点像java的反射暴力反射:Object.getOwnPropertyNames() 传统对象定义不可枚举
    var person={
    };
    Object.defineProperty(person,"name",{
        enumerable:false,   //不可枚举
        value:"zhangsan",
    });
    Object.defineProperties(person,{
        age:{
            enumerable:false,   //不可枚举
            get:function(){
                return 24;
            }
        },
        job:{
            get:function(){
                return "IT";
            }
        }
    });
    var prop=Object.getOwnPropertyNames(person);
    console.log(prop);
    //打印结果
    [ 'name', 'age', 'job' ]

原型模式定义不可枚举

    function Person(){
    }
    Person.prototype.name="zhangsan";
    Person.prototype.age=16;
    var person=new Person();
    Object.defineProperty(person,"job",{
        enumerable:false,
        value:"IT"
    })
    var props=Object.getOwnPropertyNames(person);
    console.log("实例属性:"+props);
    var prop=Object.getOwnPropertyNames(Person.prototype);
    console.log("原型属性:"+prop);
    //打印结果
    实例属性:job
    原型属性:constructor,name,age
  1. 字面量的形式定义原型对象,此时必须特别定义原型对象constructor属性的指向,并不会自动生成
    function Person(){
    }
    Person.prototype={
        constructor:Person,
        name:"zhangsan",
        age:24,
        sayName:function() {
            console.log(this.name);
        }
    }
    var person=new Person();
    console.log(person.sayName())
    //打印对象
    zhangsan
    undefined
    true
组合使用构造函数模式和原型模式
    function Person(name,age){
        this.name=name;
        this.age=age;
        this.sayName=function(){
            console.log(this.name);
        }
    }
    Person.prototype={
        job:"IT",
        friend:"Many",
    }
    var person=new Person("zhangsan",25);
    person.sayName();
    console.log(person.job);
    //打印结果
    zhangsan
    IT
动态原型模式
  function Person(name,age){
      this.name=name;
      this.age=age;
      if(typeof this.sayName!="function"){
          Person.prototype.sayName=function(){
              console.log(this.name);
          }
      }
  }
  var person=new Person("LISI",26);
  person.sayName()
  //打印结果
  LISI

这里是对原型进行修改,会反馈到所有实例上

寄生构造函数模式
  function SpecialArray(){
       var arr=new Array();
       arr.push.apply(arr,arguments);
       arr.toPipeString=function(){
           return this.join("|");
       }
       return arr;
  }
  var specialArray=new SpecialArray("lisi","wanger","hehe");
  console.log(specialArray.toPipeString());
  //打印结果
  lisi|wanger|hehe

写法类似上面的工厂模式,基本上使用的都是另外一个对象,只对一些实现细节进行修改

稳妥构造函数模式
    function Person(name,age,job){
        var object=new Object();
        object.sayName=function(){
            console.log(name);
        }
        return object;
    };
    var person=new Person("张三",25,26);
    person.sayName();
    person.getAge=function(){
        console.log(age);
    };
    person.getAge();
    //打印结果
    张三
    ReferenceError: age is not defined

定义的是稳妥对象:指的是没有公共属性,而且其方法也不引用 this 的对象,对象进行添加属性和方法时也无法获取构造函数传入的原始数据值。