• Javascript学习笔记(二)--创建对象(七种模式)


    一、工厂模式

       考虑到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("小码农",18,"码农");
    var person1=createPerson("老码农",38,"码农");

      工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题,即怎么样知道一个对象的类型。(这句话怎么理解????)

     二、构造函数模式

    function Person(name,age,job){
        this.name=name;
        this.age=age;
        this.job=job;
        this.sayName=function(){
            alert(this.name);
        };
    }
    
    var person1=new Person("小码农",18,"码农");
    var person1=new Person("老码农",38,"码农");

    与工程模式的createPerson()不同的地方:

    1、没有显式地创建对象;

    2、直接将属性和方法赋给了this对象;

    3、没有return语句;

     

      创建的对象既是Object的实例,同时也是Person的实例

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

    三、原型模式

     1、概念

    prototype就是调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处就是所有对象实例共享他所包含的属性和方法。

    function Person(){}
    
    Person.prototype.name="leo";
    Person.prototype.age=29;
    Person.prototype.job="软件工程师";
    Person.prototype.sayName=function(){
         alert(this.name);
    };
    
    var person1=new Person();
    person1.sayName();                   //leo
    
    var person2=new Person();
    person2.sayName();               //leo
    
    alert(person1.sayName==person2.sayName);   //true

       与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。person1和person2访问的都是同一组属性和同一个sayName()方法。

      Person的每个实例-----person1和person2都包含一个内部属性,该属性仅仅指向了Person.prototype;换句话说,他们与构造函数没有直接的关系。

      可以通过isPrototypeOf()方法来确定对象之间存在这种关系。如:

    alert(Person.prototype.isPrototypeOf(Person1));  //true
    alert(Person.prototype.isPrototypeOf(Person2));  //true

       Object.getPrototypeOf()方法,返回[[Prototype]]的值。如:

    alert(Object.getPrototypeOf(person1)==Person.prototype); //true
    alert(Object.getPrototypeOf(person1).name);  //leo

       每当代码读取某个对象的某个属性时,搜索首先从对象实例本身开始,如果找到给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。有则返回该属性的值。也就是说,如果我们在调用Person.sayName()时,会先后执行两次搜索。首先,解析器会问:“实例person1有sayName属性吗? 答”没有“,然后他继续问:”person1的原型中有sayName属性么?“答:有,就读取那个保存在原型对象中的函数。

      如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。

      使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法旨在给定属性存在于对象实例中时,才会返回true。

    2、原型与in操作符

      in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。

    function Person(){
            
    }
    Person.prototype.name="leo";
    Person.age=20;
    
    var person1=new Person(); 
    
    alert(person1.hasOwnProperty("name"));  //false
    alert("name" in person1);  //true    

      确定是否是原型中的属性的方法:

    function hasPrototypeProperty(object,name)
    {
        return !object.hasOwnProperty(name) && (name in object)
    }
    如果属性存在于实例中object.hasOwnProperty(name)返回true,要想整个返回true,就必须为name属性存在于object原型中。
    要取得对象上所有可枚举的实例属性,可以使用Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
    function Person(){}
    
    Person.prototype.name="leo";
    Person.prototype.age=29;
    Person.prototype.job="软件工程师";
    Person.prototype.sayName=function(){
         alert(this.name);
    };
    var keys=Object.keys(Person.prototype);
    alert(keys);                             //"name,age,job,sayName"

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

         原型模式的缺点:所有实例在默认情况下都将取得相同的属性值。修改一个实例的属性会影响到其他实例。

      所以创建自定义类型的最常见方式,就是组合使用构造函数模式和原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

    function Person(name,age,job){
        this.name=name;
        this.age=age;
        this.job=job;
        this.friends=["shirley","leo"];
    }
    
    Person.prototype={
        constructor:Person,
        sayName:function(){
            alert(this.name);
        }
    }
    
    var person1=new Person("Nicholas",29,"Software Engineer");
    var person2=new Person("greg",28,"Teacher");
    
    person1.friends.push("ven");
    alert(person1.friends);   //"shirley,leo,ven"
    alert(person2.friends);  //"shirley,leo"
    alert(person1.friends===person2.friends);  //false
    alert(person1.sayName===person2.sayName)  //true

     五、动态原型模式

      把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

    function Person(name,age,job){
        this.name=name;
        this.age=age;
        this.job=job;
        if   (type of this.sayName!="function"){
            Person.prototype.sayName=function(){
                alert(this.name);
            };
        }
    }
    
    var friend=new Person("Nicholas",29,"teacher");
    friend.sayName();

      只有在sayName方法不存在的情况下,才会将他添加到原型中。

      对于采用这种模式创造的对象,还可以采用instanceof操作符确定他的类型。

    六、寄生构造函数模型

      这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后返回新创建的对象。从表面上看,这个函数很像是典型的构造函数。例如:

    function Person(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=new Person("leo",18,"码农");
    person1.sayName();     //leo

      在这个例子中,Person函数创建了一个新对象,并以响应的属性和方法初始化该对象,然后返回了这个对象。除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式是一模一样的。

      另一个例子:

    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");
    alert(colors.toPipedString());        //"red|blue|green"

      关于寄生构造函数模式,有一点需要说明:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与构造函数外部创建的对象没有什么不同。对此,不能依赖instanceof操作符来确定对象类型。(怎么理解??)

     七、稳妥构造函数模式

      稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不通:一是新创建对象的实例方法不引用this,二是不使用new操作符调用构造函数。

    function Person(name,age,job){
        var o=new Object();
        o.name=name;
        o.age=age;
        o.job=job;
        o.sayName=function(){
            alert(name);            //这里不使用this.name
        };
        return o;
    }
    
    var person1=Person("leo",18,"码农");  //这里不使用new
    person1.sayName();     //leo
  • 相关阅读:
    每天学一点MATLAB函数——文件编程函数
    每天学一点MATLAB函数——软件操作函数(1)
    C# 杂记
    ActiveX控件注册与反注册
    First Java Graphic Program
    判断式
    两个仿函数示例
    STL文件的读取与显示
    SQLite数据库(一)
    机器学习--如何理解Accuracy, Precision, Recall, F1 score
  • 原文地址:https://www.cnblogs.com/shanoon/p/5308847.html
Copyright © 2020-2023  润新知