面向对象的程序设计-----创建对象

1. 创建对象

    1.1 工厂设计模式

/**
 * 
 * 工厂设计模式
       * @param name
       * @param age
       * @param id
       * @returns {Object}
       */
function createObj(name,age,id) {
   var o = new Object();
   o.name = name;
   o.age = age;
   o.id = id;
   o.sayInfo = function(){
      return this.name+'---'+this.age+'---'+this.id;
   }
   return o;
}
var obj_1 = createObj('zhangsan',25,2656);
console.log(obj_1.sayInfo());   //zhangsan---25---2656


1.2 构造函数设计模式

 /**
 * 构造函数设计模式
       * @param name
       * @param age
       * @param id
       * @constructor
       */
function Obj(name,age,id) {
   this.name = name;
   this.age = age;
   this.id = id;
   this.sayInof =function () {
              return this.name+'---'+this.age+'---'+this.id;
          }
      }

      var obj_1 = new Obj('zhangsan',25,2656);
console.log(obj_1.sayInof());   //zhangsan---25---2656

构造函数实例一个对象的四个步骤:

        1. 创建一个新的对象;

        2. 将构造函数的作用域赋给新对象(因此this是指向这个新对象的);

        3. 执行构造函数中的代码;

        4. 返回对象


1.3 原型模式 (省去了构造函数传参初始化,也就是说,原型模式的构造函数是没有参数的空函数)

    每个函数都有一个prototype(原型)属性,该属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的实例共享的属性和方法;

/**
 * 原型模式
 *
 */
function Obj() {
}

Obj.prototype.name = 'zhangsnan';
Obj.prototype.age = 25;
Obj.prototype.getInfo = function () {
    return this.name + '---' + this.age;
}

var obj_1 = new Obj();
var obj_2 = new Obj();
console.log(obj_1.name); 
console.log(obj_1.age);
console.log(obj_1.getInfo());  //zhangsnan---25
console.log(obj_2.name);
console.log(obj_2.age);
console.log(obj_2.getInfo());  //zhangsnan---25

    1.3.1 理解原型对象

          只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,该属性指向原型对象,在默认的情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性是指向prototype属性所在的函数的指针。

          当用构造函数创建一个新实例后,该实例的内部包含一个指针(内部属性,在ECMA第5版中管它叫[[prototype]],但没有标准访问它的方式),指向构造函数的原型对象;在Firefox,Safari,Chrome中可以通过__proto__来访问;

            (1) 原型指针及constructor的指向:

面向对象的程序设计-----创建对象

        (2) 对象的原型属性所指向的原型对象的判断与取得

function Obj_1() {}
Obj_1.prototype.name = 'zhangsan';
function Obj_2() {}
var obj_1 = new Obj_1();
//判断obj_1的原型指针指向Obj_1还是Obj_2的原型对象
console.log(Obj_1.prototype.isPrototypeOf(obj_1));  //true
console.log(Obj_2.prototype.isPrototypeOf(obj_1));  //false
console.log('------------------------');
//取得obj_1所指向的原型对象
console.log(Object.getPrototypeOf(obj_1));  //{name: "zhangsan", constructor: ƒ}

        (3) 实例对象找属性是先找自己有没有那个属性,没有就再向原型对象里找,如果有就有,没有就没有;即使对象添加了一个和原型对象同名的属性,也不会修改原型对象的属性;

function Obj(){}
Obj.prototype.name = 'zhangsan';
Obj.prototype.age = 25;
var obj_1 = new Obj();
var obj_2 = new Obj();
obj_1.name = 'lisi';
console.log(obj_1.name);  //'lisi'   取得obj_1自身的属性
console.log(obj_2.name);  //'zhangsan'   原型对象中的name属性没有被修改
console.log(obj_1.age);  //25   取得原型对象中的
console.log(obj_1.id);  //undefined  自身和原型对象中都没有,则为undefined
delete obj_1.name;  //删除obj_1自身的name属性
console.log(obj_1.name);  //'zhangsan'   取得原型中的属性
        (4) 判断实例中是否屏蔽了原型对象中的属性

// 判断实例中是否屏蔽了原型对象中的属性
function hasPrototypeProperty(object,pro) {
    return !object.hasOwnProperty(pro) && (pro in object);
}

function Obj(){}
Obj.prototype.name = 'zhangsan';
Obj.prototype.age = 25;
var obj_1 = new Obj();
var obj_2 = new Obj();
obj_1.name = 'lisi';
Object.prototype.obj_pro = '我是Objct的属性';
//判断对象是否有某个属性是自身的还是原型的,还是没有;
console.log(hasPrototypeProperty(obj_1,'name'));  //false
console.log(hasPrototypeProperty(obj_1,'age'));  //true
console.log(hasPrototypeProperty(obj_1,'obj_pro'));  //true   所有的对象都要继承Object的属性

       (5) 获取对象上可以枚举的属性  用Object.keys() 方法

//获取对象上可以枚举的属性  用Object.keys() 方法,返回一个数组
function Obj(){}
Obj.prototype.name = 'zhangsan';
Obj.prototype.age = 25;
var obj_1 = new Obj();
obj_1.name = 'lisi';
console.log(Object.keys(Obj.prototype));  //["name", "age"]
console.log(Object.keys(obj_1));  //["name"]


1.3.2 简单的原型语法

/**
     * 简单的原型语法
     * 
     */
    function Obj() {}
    Obj.prototype = {
        name : 'zhangsan',
        age : 25
    };
    console.log(Obj.prototype.constructor);  //ƒ Object() { [native code] }   指向Object构造函数
    console.log('---------------------------');
    
    
//    改进
    function Obj() {}
    Obj.prototype = {
        constructor : Obj,
        name : 'zhangsan',
        age : 25
    };
    console.log(Obj.prototype.constructor);  //ƒ Obj() {}   指向Obj构造函数  但是可以枚举出来
    for(var i in Obj.prototype){
        console.log(Obj.prototype[i]);
    }
    console.log('---------------------------');
    
    
//    改进
    function Obj() {}
    Obj.prototype = {
        name : 'zhangsan',
        age : 25
    };
    Object.defineProperty(Obj.prototype,'constructor',{
        enumerable : false,
        value : Obj
    });
    console.log(Obj.prototype.constructor);  //ƒ Obj() {}   指向Obj构造函数
    for(var i in Obj.prototype){
        console.log(Obj.prototype[i]);
    }


1.3.3 原型的动态性

    对原型做的任何修改都能立即从实例上反应出来,不管什么时候创建实例都如此;除非完全重写原型对象

 /**
     *原型对象的动态性
     *
     */
//    function Obj() {}
//    var obj_1 = new Obj();
//    Obj.prototype.name = 'zhangsan';
//    Obj.prototype.age = 25;
//    console.log(obj_1.name);  //'zhangsan'
//    console.log(obj_1.age);  //25

    //完全重写原型对象后
    function Obj() {}
    var obj_1 = new Obj();
    Obj.prototype.id = 2018;
    Obj.prototype = {
        constructor : Obj,
        name : 'zhangsan',
        age : 25
    };
    console.log(obj_1.name);  //undefined
    console.log(Object.getPrototypeOf(obj_1)); //{id: 2018, constructor: ƒ}
    //由于完全重写原型对象时时从新建立的新对象,新开辟了一个堆内存空间来创建新的原型对象,
    //  obj_1的原型指针是指向Obj构造函数的原来的原型对象


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

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

//构造模式
function Obj(name,age) {
    this.name = name;
    this.age = age;
}
//原型模式
Obj.prototype = {
    constructor : Obj,
    getInfo  :function () {
        return this.name+'----'+this.age
    }
}
var obj_1 = new Obj('zhangsan',25);
var obj_2 = new Obj('lisi',30);
console.log(obj_1.getInfo());  //zhangsan----25
console.log(obj_2.getInfo());  //lisi----30


1.5 动态原型模式

/**
 * 
 *动态原型模式
 */
function Obj(name,age) {
    this.name = name;
    this.age = age;
    console.log(Object.getPrototypeOf(this));
    if(typeof this.getInfo !== 'function'){
        Obj.prototype.getInfo = function () {
            return this.name+'---'+this.age
        }
    }
    console.log(typeof Obj.prototype.getInfo);
}
var obj_1 = new Obj('zhangsan',25);
var obj_2 = new Obj('lisi',30);
console.log(obj_1.getInfo());  //zhangsan---25
console.log(obj_2.getInfo());  //lisi---30


1.6 寄生构造函数模式(慎用)

        思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后返回新创建的对象;在不能直接使用某构造函数时使用;

/**
 * 
 * 寄生构造函数模式
 */
function Obj_1(name,age) {
    this.name = name;
    this.age = age;
}

//在不能直接使用Obj_1创建实例时
function Obj(name,age) {
    var o = new Obj_1(name,age);
    o.getInfo = function () {
        return this.name+'----'+this.age;
    }
    return o;
}
var obj_1 = new Obj('zhangsan',25);
console.log(obj_1);  // Obj_1 {name: "zhangsan", age: 25, getInfo: ƒ}
console.log(obj_1.getInfo());  // zhangsan----25


1.7 稳妥构造函数模式

        不能用this,new,应用于安全执行环境

/**
 * 
 *稳妥构造函数模式
 */
function Obj(name,age) {
    var o = new Object();
    o.getInfo = function () {
        return name+'----'+age;
    }
    return o;
}

var obj_1 = Obj('zhangsan',25);
console.log(obj_1.name);  //undefined
console.log(obj_1.age);  //undefined
console.log(obj_1.getInfo());  //zhangsan----25