JavaScript学习笔记6 面向对象之创建对象

  • 工厂模式
  • 构造函数模式
    • 将构造函数当作函数
    • 构造函数的问题
  • 原型模式
    • 理解原型对象
      • isPrototype()
      • Object.getPrototypeOf()
      • 多个对象实例共享原型对象的属性和方法的基本原理
      • 实例属性屏蔽(创建同名属性)or恢复(delete同名属性)原型对象属性
      • hasOwnProperty()
    • 原型与in操作符
      • 单独使用in
      • 在for-in 循环中使用。
      • Object.keys()
      • Object.getOwnPropertyNames
    • 更简单的原型语法
    • 原型的动态性
    • 原生对象的原型
    • 原型对象的问题
  • 组合使用构造函数模式和原型模式(这种构造函数与原型混成的模式,目前在ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法)
  • 动态原型模式(这种方法非常完美)
  • 寄生构造函数模式
  • 稳妥构造函数模式

工厂模式

function createPerson(name,age,job){
    var o =new Object();
    o.name=name;
    o.age=age;
    o.job=job;
    o.sayName= function(){
        alert(this.name);
    };
    return o;
}

var person1=createPerson("Nicholas",29,"Software Engineer");
var person2=createPerson("Nicholas",27,"Software Engineer");

函数createPerson()能够根据接受的参数来构造一个包含所有必要信息的Person对象。可以无数次的调用这个函数,每次返回一个包含三个属性、一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(怎样知道一个对象的类型)。

构造函数模式

ECMAScript中的构造函数可以用来创建特定类型的对象。
像Object 和Array这样的原生构造函数,在运行时会自动出现在执行环境中。
此外,也可以创建自定义的构造函数,从而定义 自定义对象类型的属性和方法,例如

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=function(){
        alert(this.name);
    };
}

var person1=new Person("Nicholas",29,"Software Engineer");

构造函数模式与工厂模式的不同之处

  • 没有显示地创建对象
  • 直接将属性和方法赋给了this对象
  • 没有return语句
  • 首字母大写,构造函数始终都应该以一个大写字母开头,非构造函数应该以一个小写字母开头(规范问题)

要创建Person的新实例,必须使用new操作符。以这种方式调用构造函数会经历以下4个步骤:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
  3. 执行构造函数中的代码(为这个新对象添加属性);
  4. 返回新对象
    person1和person2分别保存着Person的一个不同实例,都有constructor(构造函数)属性,指向Person,对象的constructor属性最初是用来表示对象类型的 列如:
alert(person1 .constructor ==Person);//true
alert(person2 .constructor ==Person);//true
alert(person1.constructor==Object); //false

但是检测对象类型用instanceof 更靠谱

alert(person1 instanceof Person);//true
alert(person2 instanceof Person);//true
alert(person1 instanceof Object);//true

创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,而这正是构造函数模式胜过工厂模式的地方。和Java一样 所有的对象继承自Object。

将构造函数当作函数

构造函数与其他函数的唯一区别,就是在于调用他们的方式不同。不过,构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new操作符来调用,那它跟普通函数一样。列入Person() 函数 还可以这样调用:

//当作构造函数使用
var person=new Person("Nicholas",29,"Software Engineer");
person.sayName();//Nicholas

//作为普通函数调用 
//在全局作用域中调用一个函数时,this对象总是指向Global对象 (在浏览器中就是window对象)

Person("Nicholas2",28,"Software Engineer");//添加到window
//因此,调用完函数后可以通过window对象来调用sayName();
window.sayName();//Nicholas2


//在另一个对象的作用域中调用
var o =new Object();
//可以使用call 或者apply 在某个特殊对象的作用域中 调用Person()函数 。 
Person.call(o,"Nicholas3",28,"Software Engineer");
//这里是在对象o的作用域中调用的,因此调用后o就拥有了  Person  所有的所有属性 和sayName() 方法
o.sayName();//Nicholas3

构造函数的问题

构造函数的缺点

每个方法都要在每个实例上重新创建一遍。perso1和person2都有一个名为sayName()的方法,但那两个方法不是同一个Function的实例.ECMAScript中的函数是对象。因此每定义一个函数,也就是实例化了一个对象。从逻辑角度讲。此时的构造函数也可以这样定义。

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=new Function("alert(this.name)");// 与声明函数在逻辑上是等价的
}

从这个角度上来看构造函数,更容易明白每个Person实例都包含一个不同的Function实例(以显示name属性)的本质。说明白些,以这种方式创建函数,会导致不同的作用域链 和标识符解析,但创建Function新实例的机制仍然是相同。因此,不同实例上的同名函数是不相等的,例如:

alert(person1.sayName== person2.sayName);//false

创建两个完成同样任务的Function实例的确没有必要,况且有this对象在,根本不在执行代码钱就把函数绑定到特定对象上面。因此,大可像下面这样,通过把函数定义转移到构造函数外部来解决这个问题。

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=sayName;
}

function sayName(){
    alert(this.name);
}

var person1=new Person("Nicholas",29,"Software Engineer");
var person2=new Person("Nicholas2",29,"Software Engineer");
person1.sayName();//Nicholas
person2.sayName();//Nicholas2

这个例子中,把sayName()函数的定义转移到了构造函数外部。而在构造函数内部,我们将sayName属性设置成等于全局的sayName函数。这样一来,由于sayName包含的是一个指向函数的指针,因此person1和person2对象就共享了在全局作用域中定义的同一个sayName()函数。这样确实解决了两个函数做同一件事的问题,但是:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让人无法接受的是:如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。好在,这些问题可以通过使用原型模式来解决

原型模式

我们创建的每个函数都有一个prototype(原型),这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性好方法。如果按字面意思来理解,那么prototype就是通过调用构造函数而创建对象实例的原型对象
使用原型对象的好处:
可以让所有对象实例共享它所包含的属性和方法,不必在构造函数中定义对象实例的信息,而是直接将形象添加到原型对象中,例如:

function Person(sex){
    this.sex=sex;
}
//直接将属性和sayName()方法添加到了Person的prototype属性中
//这些属性和方法 Person的实例都可以共享
Person.prototype.name="Nicholas";
Person.prototype.age=25;
Person.prototype.job="Software Engineer";

Person.prototype.sayName=function(){
    alert(this.name+"_"+this.sex);
};

var person1=new Person("man");
var person2=new Person("woman");

//person1 和 person2访问的都是同一属性(name)  和 同一个Function函数 sayName(); 
person1.sayName();//Nicholas _ man
person2.sayName();//Nicholas  _woman
alert(person1.sayName==person2.sayName);//true

理解原型对象

要理解原型模式的工作原理,必须先理解ECMScript中原型对象的性质。A这里写图片描述

  1. 创建一个新函数,会根据一组特定规则创建prototype属性,指向原型对象(Person Prototype) ,
  2. 原型对象(Person Prototype)会自动获得一个constructor(构造函数)属性,指向Person(prototype属性所在的函数)
  3. 通过这个构造函数可以继续添加属性和方法(创建构造函数后,其原型对象只有constructor属性,其他方法从Object下继承而来)
  4. 创建实例后(person1,person2),实例将包含一个指针([[Prototype]]内部属性),指向构造函数的原型对象(Person Prototype)

isPrototype()

可以判断构造函数(Person)的prototype属性指向的原型对象(Person Prototype) 是否 和实例(person1)的[[Prototype]]属性指向的原型对象(Person Prototype)相同, 相同为true ,不同为false

function Book(){

}
alert(Person.prototype.isPrototypeOf(person1));//true
alert(Book.prototype.isPrototypeOf(person2));//false

Object.getPrototypeOf()

ECMAScript新增方法 在所有支持的实现中,这个方法返回[[Prototype]]的值

//确定getPrototypeOf()方法返回的对象是否是Person的原型对象
alert(Object.getPrototypeOf(person1)==Person.prototype);//ture
//获取原型对象中的值
alert(Object.getPrototypeOf(person1).name);//Nicholas

Object.getPrototypeOf()可以方便地取得一个对象的原型,这在利用原型实现继承,是非常重要的。

多个对象实例共享原型对象的属性和方法的基本原理

多个对象实例(person1,person2)共享原型对象(Person Prototype)的属性和方法的原理

这里写图片描述

注意:虽然可以通过对象实例访问对象原型中的值,但是却不能通过实例重写原型中的值。如果实例中创建一个与原型同名的属性,该属性将会屏蔽原型中的属性。

function Person(){

}
Person.prototype.name="Nicholas";
Person.prototype.age=10;
var person1=new Person();
var person2=new Person();
//在实例中创建一个与原型同名的属性
person1.name="Greg";
//屏蔽了原型中的属性
alert(person1.name);//Greg   来自对象
alert(person2.name);//Nicholas  来自原型

//通过delete操作符删除了person1.name属性  
delete person1.name; 
//  屏蔽消失   重新指向原型中的属性
alert(person1.name); //Nicholas 

hasOwnProperty

(用于判断访问的是实例属性还是原型属性)
hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法(是从Object继承而来)只在给定属性存在于对象实例中,才会返回true

function Person(){

}
Person.prototype.name="Nicholas";
Person.prototype.age=29;
Person.prototype.job=29;
Person.prototype.sayName=function(){
     alert(this.name);
};
var person1=new Person();
var person2=new Person();

//来自原型的属性Nicholas
alert(person1.hasOwnProperty("name"));//false
person1.name="Greg";

//来自实例的属性  Greg
alert(person1.hasOwnProperty("name"))//true

//删除实例中的name属性
delete person1.name;
// 来自 原型的属性 Nicholas
alert(person1.hasOwnProperty("name"));//false
//附图一张说明 不同情况下的实例与原型的关系

这里写图片描述

原型与in操作符

有两种方式使用in操作符:
1.单独使用
2.在for-in 循环中使用。

1.单独使用

function Person (){

        }
        Person.prototype.name="wangshaoli";
        Person.prototype.age=29;
        Person.prototype.sayName=function(){
            alert(this.name);
        }
        var person1= new Person();
        var person2= new Person();
        //person1  name属性来自原型  wangshaoli
        console.log(person1.hasOwnProperty("name"));//false 
        console.log("name" in person1);//true

        person1.name="wang";
        //person1 name 来自实例     wang
        console.log(person1.hasOwnProperty("name"));//true
        console.log("name" in person1);//true

        //person2 name属性来自原型  wangshaoli
        console.log(person2.hasOwnProperty("name"));//false
        console.log("name" in person2);//true

        delete person1.name;//恢复person1原型对象属性 name
        //person1.name 来自原型 wangshaoli
        console.log(person1.hasOwnProperty("name"));//false
        console.log("name" in person1);//true




        //高级用法
        /**
         * 
         * @description判断属性是存在于原型中  还是存在于对象中  
         *  if return true  属性在原型中
         *  if return false 属性在实例中    或者 属性undefined
         * @param {Object} object
         * @param {Object} name
         * 
         *  
         * 
         */

        function hasPrototypeProperty(object ,name){
            return  ! object.hasOwnProperty(name) && (name in object);
        }
        console.log("hasOwnProperty="+person1.hasOwnProperty("qwe"));
        console.log("qwe"  in person1 );

        /**
         * 解析
         * hasOwnProperty 去判断name  存在原型中返回false   存在实例中返回true  .
         * PS:hasOwnProperty可以理解为 实例是否拥有某个属性 
         * 拥有 return true   
         * 不拥有 return false(包括 来自原型 或未定义)
         *
         * in   判断实例能否访问到属性  能  return true 不能 return false     
         * 因为 hasOwnProperty 判断未定义的属性时也返回false  所以需要in
         * 来判断 实例能否访问属性
         */
        //qwe 属性 undefined  false
        console.log("qwe in person1 ? =" + hasPrototypeProperty(person1,"qwe"));
        //name 属性 在原型中  return true
        console.log("name in person2 ? =" + hasPrototypeProperty(person2,"name"));
        //name 属性  person2在实例中 return false
        person2.name ="wang";
        console.log("name in person2 ? =" + hasPrototypeProperty(person2,"name"));

2.在for-in 循环中使用。

/*在使用for in 循环时,返回的是所有能够通过对象访问的、
         * 可枚举属性    即包括实例中属性,也包括原型中的属性。
         *屏蔽了原型中不可枚举属性(即将[[Enumerable]]标记的属性)
         * 的实例属性也会在for-in 循环中返回,应为根据规定
         * 所有开发人员定义的属性都是可枚举的(IE8 及更早版本除外 , 应为有bug所以不会出现在for-in循环中)
         * 
         *PS:[[Enumerable]]:表示能否通过 for-in循环返回属  默认true 可返回 
         */


        var o ={
            toString :function(){
                return "My Object";
            }
        };
        for (var prop in o) {
            if(prop == "toString"){
                console.log("found toString"); //IE中不 显示
            }

        }

        /*
         *对象o 定义了toString()方法  屏蔽了原型中(不可枚举)的toString()方法
         * but  IE 认为原型的toString方法[[Enumerable]]false  所以跳过 不弹alert
         * 该bug会影响默认不可枚举的所有属性和方法,包括: hasOwnProperty()
         * propertyIsEnumerable(),toLocaleString(),toString()和 valueOf().
         * 
         * ECMAScript5也将constructor和prototype属性的[[Enumerable]] 特效设置为false,
         * 但不是所有浏览器都照此实现
         * 
         *   
         */

Object.keys()

要获取对象上所有可枚举 的实例属性,可以使用ECMAScript的Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组 。

function Person(){

        }
        Person.prototype.name="wangshaoli";
        Person.prototype.age="25";
        Person.prototype.job="Software Engineer";
        Person.prototype.sayName=function(){
            console.log(this.name)
        };
        var keys= Object.keys(Person.prototype);
        console.log("keys="+keys);//"keys=name,age,job,sayName"
        var p1=new Person();
        p1.name="dabai";
        p1.age=500;
        var p1keys=Object.keys(p1);
        console.log("p1keys="+p1keys);// "p1keys=name,age"
        console.log("keys is Array? "+ Array.isArray(keys));//keys is Array? true
        /**
         * Object.keys 返回Array  数组中name,age,job,sayName
         * 顺序是 它们在 for-in循环中出现的顺序。
         * 如果通过Person实例调用Object.keys() 仅返回name,age 两个实例属性
         */

Object.getOwnPropertyNames()

通过Object.getOwnPropertyNames() 可以得到所有实例属性,无论是否可枚举

var OwnPropertyNamesKeys  = Object.getOwnPropertyNames(Person.prototype);
        console.log("OwnPropertyNamesKeys="+OwnPropertyNamesKeys);
        //OwnPropertyNamesKeys=constructor,name,age,job,sayName
        /*
         * 结果中包含了 不可枚举的 constructor 属性
         * Object.getOwnPropertyNames() 和Object.keys()
         * 都可以用来替代for-in 循环
         */

更简单的原型语法

function Person(){

    }
    Person.prototype={
        name :"wangshaoli",
        age :25,
        job : "Software Engineer",
        sayName:function(){
            alert(this.name);
        }
    }
    /*
     * 将 Person.prototype 设置为等于一个以 对象字面量形式创建的新对象。
     * 最终结果相同  但是   constructor 属性不在指向Person了
     * 因为 每创建一个函数,就会同事创建它的prototype对象,这个对象也会自动获得constructor属性。
     * 这里本质上是重写了 默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Objcet构造函数)
     * 不再指向Person函数  如下所示
     * 
     * 
     */
    var man =new Person();
    console.log(man instanceof Object)//true
    console.log(man instanceof Person)//true
    console.log(man.constructor === Person)//false
    console.log(man.constructor === Object)//true


    /**
     * 如果constructor 属性很重要  可以设置适当的值
     */
    function Person1(){

    }
    var manPerson1=new Person1();
    Person1.prototype={
        constructor :Person1,
        name :"wangshaoli",
        age:25,
        job : "Software Engineer",
        sayName: function(){
            alert(this.name);
        }
    }
    var woman=new Person1();
    console.log(woman.constructor === Person1)//true
    console.log(woman.constructor === Object)//false






    /**
     * 注意,以这种方式重设constructor 属性会导致它的[[Enumerable]]特性被设置为true。
     * 默认情况下,原生的constructor属性时不可枚举的,因此如果你使用兼容ECMAScript5的JavaScript
     * 引擎,可以试一试Objcet.defineProperty(); 修改[[Enumerable]]特性 
     * 详情参考http://blog.csdn.net/csdn1125550225/article/details/71131520  ->搜索 defineProperty()
     */

    var defineBefoe = Object.keys(Person1.prototype);
    console.log("defineBefoe="+defineBefoe);//"defineBefoe=constructor,name,age,job,sayName"
    Object.defineProperty(Person1.prototype,"constructor",{
        enumerable:false,
        value:Person1
    })

    var defineAfter = Object.keys(Person1.prototype);//
    console.log("defineAfter="+defineAfter);//"defineAfter=name,age,job,sayName

原型的动态性


    /**
     * 由于在原型中查找值得过长是一次搜索,因此我们对原型对象所做的任何修改都能够立即从
     * 实例上反应出来————即使是先创建了实例后修改原型也是如此
     */
    function Person(){}
    var friend=new Person();
    Person.prototype.sayHi=function(){
        console.log("Hi");
    }
    friend.sayHi();//Hi 没问题

    /**
     *     即使是先创建了实例后修改原型 也可以访问新方法。
     * 原因是 实例与原型之间的松散连接关系。
     * 当调用friend.sayHi()时,首先会在实例中搜索名为sayHi的属性,没有找到的话 会继续搜索原型。
     * 因为实例与原型之间的连接只不过是一个指针,而非一个副本,因此就可以在原型中找到新的sayHi
     * 属性并返回保存在那里的函数。
     * 
     * 
     */

    /*
     *尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反应出来,但如果是重写整个
     * 原型对象,那么情况就不一样了。 
     * 
     */

    // 1. 创建Person函数  会   函数 Person.prototype 指向    Person Prototype 原型
    function Person(){
    }

    //2  调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,
    //friend.[[Prototype]] 指向  Person Prototype
    var friend = new Person();

    //3重写原型对象   Person.prototype 指向 New Person Prototype 
    Perosn.prototype={
        constructor:Person,
        name:"Nicholas",
        age:29,
        job :"Software Engineer",
        sayName:function(){
            console.log(this.name);
        }

    }
    //4  因为friend 实例的[[Prototype]] 指向 Person Prototype 原型对象  而不是 New Person Prototype
    //原型对象  所以报错  如图所示
    friend.sayName();//ReferenceError: Perosn is not defined   

这里写图片描述

原生对象的原型


    /**
     * 原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,
     * 都是采用这种模式创建的。所有原生引用类型(Objcet、Array、String,等等)都
     * 在器构造函数的原型上定义了方法。例如: Array.prototype中可以找到sort()
     * 方法,而在String.prototype中 可以找到substring()方法,如下所示
     */
    console.log(typeof Array.prototype.sort);//function
    console.log(typeof String.prototype.substring);//function

    /*
     *      通过元素对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法。
     * 可以像修改自定义对象的原型一样修改原生对象的原型,因此可以随时添加方法。如下
     */

    String.prototype.startsWith=function (text){
        return this.indexOf(text)==0;
    }
    var msg ="Hello world";
    console.log(msg.startsWith("Hello"));//true

原型对象的问题


    /**
     * 原型模式的缺点
     * 1.它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况
     * 下都将取得相同的属性值。(虽然这会在某种程度上带来一些不方面,但不是原型的最大问题)
     * 2.原型中所有属性被很多实例共享,这种共享对于函数非常合适。对于包含基本值得属性也说的过去
     * ,毕竟,通过在实例上添加一个同名属性,会隐藏原型中的对应属性。但是,对于包含引用类型值的
     * 属性来说,问题就比较突出
     */

    function Person(){

    }
    Person.prototype={
            constructor :Person,
            name:"Nicholas",
            age:30,
            job:"SoftWare Engineer",
            friends:["shelby","Court"],
            sayName:function(){
                console.log(this.name);
            }
    };
    var person1=new Person();
    var person2=new Person();
    person1.friends.push("Van");
    console.log(person1.friends);//"shelby,Court,Van"   
    console.log(person1.friends);//"shelby,Court,Van"   
    console.log(person1.friends===person2.friends);//true

    /**
     * 如果想在实例中有一个共享数组,这个结果是适用的,
     * 但是实例一般都是要有属于自己的属性。这个问题就是很少有人单独使用原型模式的原因
     */

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


    /**
     * 创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。
     * 构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。
     * 这样,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,
     * 最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数:可谓
     * 是集两种模式之长。下面的代码重新了前面的例子。
     */



    function Person (name,age,job){
        this.name=name;
        this.age=age;
        this.job=job;
        this.friends=["Shelby","Court"];
    }
    Person.prototype={
        constructor:Person,
        sayName : function(){
            console.log(this.name);
        }
    }


    var person1=new Person("Nicholas",29,"Software Engineer");
    var person2=new Person("Greg",27,"Doctor");
    person1.friends.push("Van");
    console.log(person1.friends);//Shelby,Court,Van
    console.log(person2.friends);//Shelby,Court
    console.log(person1.friends===person2.friends );//false
    console.log(person1.sayName===person2.sayName);//true


    /**
     * 实例属性都是在构造函数中定义的,而由所有实例共享的属性constructor 和方法sayName()则是在
     * 原型中定义。而修改person1.friends,并不影响person2.friends 因为他们分别引用了不同的数组
     * 
     * 
     * 这种构造函数与原型混成的模式,目前在ECMAScript中使用最广泛、认同度最高的一种创建自定义类
     * 型的方法。可以说,这是用来定义引用类型的一种默认模式。
     */

动态原型模式(这种方法非常完美)

/**
     *     有其他OO语言经验的开发人员在看到独立的构造函数和原型是,很可能会
     * 感到非常困惑。动态原型模式正是致力于解决这个问题的一个方案,它把所有
     * 信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),
     * 又保持了同时使用构造函数和原型的有点。
     * 换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需初始化原型。
     */
    function Person(name,age,job){
        //属性 
        this.name =name;
        this.age =age;
        this.job = job;
        //方法
        if(typeof this.sayName!= "function"){
            this.aaa="1";
            Person.prototype.sayName=function(){
                console.log(this.name);
            };
            Person.prototype.sayAge=function(){
                console.log(this.age);
            }
        }

    }

    var friend =new Person("Nicholas",29,"SoftWare Engineer");
    friend.sayName();//Nicholas
    friend.sayAge()//29
    console.log(friend instanceof Person);  //true


    /**
     * 这里只在sayName()方法不存在的情况下,才会将它添加到原型中。
     * 这段代码只会在初次调用构造函数时才会执行。此后,原型已经完成初始化,
     * 不需要再做什么修改了。不过要记住,这里对原型所做的修改,能够立即在
     * 所有实例中得到反映。  (完美的方法)
     * if语句只用检查其中一个, 不用分别判断
     * 还可以使用 instanceof 确定类型
     */

寄生构造函数模式

/**
     * 通常,前几种模式都不适用的情况下,可以使用寄生构造函数模式。
     * 这种模式的基本思想是:
     * 创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后返回新创建的对象;
     * 从表面上看,这个函数很像是典型的构造函数。
     */
    function Person(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;
    }

    /**
     * 除了使用new 操作符并把使用的包装函数叫做构造函数之外,
     * 这个模式跟工厂模式其实是一模一样的。构造函数在不返回值的
     * 情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个
     * return语句,可以重写调用构造函数时返回的值
     */

    /**
     * 这个模式可以在特殊的情况下来为对象创建构造函数。假设我们想创建一个具有额外方法
     * 的特殊数组。由于不能直接修改Array构造函数,因此可以使用这个模式。
     * 
     */

    function SpecialArray(){
        //创建数组
        var values =new Array();

        //添加值
        values.push.apply(values,arguments);

        //添加方法
        values.toPipedString =function(){
            return this.join("|");
        }
        return values;
    }

    var colors=new SpecialArray("red","blue","green");
    console.log(colors.toPipedString());//red|blue|green

    /**
     * 关于寄生构造函数模式:
     * 1.返回的对象与构造函数或者与构造函数的原型属性之间没有关系;
     * 也就是说,构造函数返回的对象与在构造函数外部创建的对象是一样的
     * 所以,不能依赖instanceof操作符来确定对象类型。
     * 如果需要确定对象类型,不要使用这种模式
     */
    console.log(colors instanceof Array);   //true
    console.log(colors instanceof SpecialArray);    //false

稳妥构造函数模式

/* 稳妥对象:即没有公共属性,而且其方法也不引用this的对象。
     * 稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this和new),
     * 或者防止数据被其他应用程序(如Mashup程序)改动时使用。
     * 
     * 稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:
     * 1.新创建对象的实例方法不引用this
     * 2.不适用new 操作符调用构造函数。
     */


    function Person(name,age,job){
        var o =new Object();//创建一个新对象
        o.sayName= function(){
            console.log(name);
        };
        //返回对象
        return o;
    }
    /*
     *以这种模式创建的对象中,除了使用sayName()方法之外,没有其他办法访问name的值。
     */
    var friend =Person("Nicholas",29,"Software Engineer");

    console.log(friend.name);//undefined 
    friend.sayName();//Nicholas

    /**
     * 即使给friend对象添加方法或数据成员,也不可能有别的方法访问传入到构造
     * 函数中的原始数据。稳妥构造函数模式提供的这种安全性,非常适合在某些安全
     * 执行环境 如 ADsafe (www.adsafe.org)  和  Caja(http://code.google.com/p/google-caja/)
     */

    /**
     *与寄生构造函数模式类似 ,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,
     * 因此instanceof 操作符对这种对象也没有意义
     */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值