首页 > 代码库 > 高级程序设计第六章(2)--创建对象

高级程序设计第六章(2)--创建对象

---恢复内容开始---

1:工厂模式

function creatPerson(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 =creatPerson("xiaoming",29,"it");        var person2 =creatPerson("lili",27,"ittoo");        person1.sayName()//xiaoming  没有解决对象识别问题、        person2.sayName()//lili  没有解决对象识别问题,既怎么知道一个对象的类型

2:构造函数模式

function Person(name,age,job){//构造函数始终以大写字母开头            this.name = name;            this.age = age;            this.job = job;            this.sayName =function(){                alert(this.name)            }        }        //作为构造函数使用        var person1 =new Person("xiaoming",29,"it");        var person2 =new Person("lili",27,"ittoo");        //都有一个constructor(构造函数)属性,该属性指向Person        alert(person1.constructor == Person)//true        alert(person1 instanceof Object)//true     alert(person1 instanceof Person)//true        //作为普通函数调用        Person("Gay",28,"itthree");        window.sayName()//gay        var o = Object();        Person.call(o,"blue",28,"itthree");        o.sayName()

      //缺点 创建了两个同样任务的Function实例
      alert(person1.sayName()==person2.sayName())//false

 

不同之处:

   一、没有显示地创建对象;

   二、直接将属性和方法赋给了this对象`

     三、没有return语句

3:原型模式

每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,使用对象的好处就是所有实例共享它所包含的属性和方法。

        function Person(){        }        Person.prototype.name = "Nicholas";        Person.prototype.age = 29;        Person.prototype.job = "it";        Person.prototype.sayName =function(){            alert(this.name)        }        var person1 =new Person();        person1.sayName();//"Nicholas"        var person2 =new Person();        person2.sayName();//"Nicholas"        alert(person1.sayName()==person2.sayName())//true
     Person.prototype.isPrototypeOf(person1) //true
     alert(Object.getPrototypeOf(person1) ==Person.prototype )//true
     alert(Object.getPrototypeOf(person1).name )//"Nicholas"
     person1.name = "Gay";
     alert(person1.name)//"Gay"
     alert(person2.name)//"Nicholas"
    //实例的优先级高于原型 使用delete操作符可以完全删除实例属性,从而让我们能够重新访问原型中的属性
    delete person1.name;
    alert(person1.name)//"Nicholas"
 
 

用hasOwnProperty()用于检测一个属性是否存在于实例中,还是存在于原型中 实例中为true 原型中为false

        person1.name = "Gay";        alert(person1.name)//"Gay"        alert(person1.hasOwnProperty("name"))//true 来自实例        alert(person2.name)//"Nicholas"        alert(person1.hasOwnProperty("name"))//false  来自原型        delete person1.name        alert(person1.name)//"Nicholas        alert(person1.hasOwnProperty("name"))//false  来自原型

2:原型与in操作符

//由于in操作符只要通过对象能够访问到属性就返回true,hasOwnProperty()只在属性存在于实例中才返回true,因此只要in操作符返回true而hasOwnProperty()返回false,就可以确定属性是原型中的属性        function hasPrototypeProperty(Object,name){            return !Object.hasOwnProperty(name)&&(name in object)        }

for in,返回的是所有能够通过对象访问的,可枚举的(enumerated)属性

var o = {            toString : function(){                return "My Object";            }        }        for (var prop in o){            if (prop == "toString") {                alert("Found toString")            }        }

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

function Person(){        }        Person.prototype.name = "Nicholas";        Person.prototype.age = 29;        Person.prototype.job = "it";        Person.prototype.sayName =function(){            alert(this.name)        }        var keys = Object.keys(Person.prototype);        alert(keys)//name age job sayName        var p1 = new Person();        p1.name = "gay";        p1.age = 31;        var p1keys = Object.keys(p1)        alert(p1keys)//name age        //如果你想知道所有实例属性,不管是否有枚举,都可以使用Object.getOwnPropertyNames()        var keys = Object.getOwnPropertyNames(Person.prototype);        alert(keys)//constructor,name,age,job,sayName

3:更简单的原型语法

function Person(){        }        Person.prototype ={            constructor:Person,//少了这一行,,constructor将不再指向Person            name : "Nicholas",            age : 29,            sayName : function(){                alert(this.name)            }        }        //但是会导致他的[[Enumerable]]特性被设置为true,默认情况下,原生的constructor属性是不可枚举的        //重设构造函数,        Object.defineProperty(Person.prototype,‘constructor‘,{            enumerable:false,//设置为不可枚举            value:Person        })
function Person(){        }        //var friend = new Person();//报错        Person.prototype ={            constructor:Person,//少了这一行,,constructor将不再指向Person            name : "Nicholas",            age : 29,            sayName : function(){                alert(this.name)            }        }        var friend = new Person();//不报错

原型的缺点:所有的属性是被很多实例共享的,这种共享对于函数非常适合。然而对于包含引用类型值得属性来说,问题就比较突出

function Person(){        }        //var friend = new Person();//报错        Person.prototype ={            constructor:Person,//少了这一行,,constructor将不再指向Person            name : "Nicholas",            age : 29,            friends:["1","2"]            sayName : function(){                alert(this.name)            }        }        var person1 = new Person();//不报错        var person2 = new Person();        person1.friends.push("3");        alert(person1.friends);        alert(person2.friends);        alert(person1.friends===person2.friends)//true

ES中使用最广泛、认同度最高的一种创建方法,组合使用构造函数模式和原型模式,每个实例都有一份实例属性的副本,但同时又共享着对方的引用,最大限度地节省了内存。

function Person(name,age,job){            this.name = name;            this.age = age;            this.job = job;            this.friends = ["1","2"]        }        Person.prototype ={            constructor:Person,//少了这一行,,constructor将不再指向Person            sayName : function(){                alert(this.name)            }        }        var person1 = new Person("gay",29,"it");//不报错        var person2 = new Person("blue",27,"meinv");        person1.friends.push("3");        alert(person1.friends);//"1","2","3"        alert(person2.friends);//"1","2"        alert(person1.friends===person2.friends)//false        alert(person1.sayName===person2.sayName)//false

动态原型模式

function Person(name, age, job) {    this.name = name;    this.age = age;    this.job = job;    if (typeof this.sayName != "function") {        Person.prototype.sayName = function() {            alert(this.name);        };    }}

Person是一个构造函数,通过new Person(...)来生成实例对象。每当一个Person的对象生成时,Person内部的代码都会被调用一次。

如果去掉if的话,你每new一次(即每当一个实例对象生产时),都会重新定义一个新的函数,然后挂到Person.prototype.sayName属性上。而实际上,你只需要定义一次就够了,因为所有实例都会共享此属性的。所以如果去掉if的话,会造成没必要的时间和空间浪费;而加上if后,只在new第一个实例时才会定义sayName方法,之后就不会了。

至于第二个问题,是这样的:假设除了sayName方法外,你还定义了很多其他方法,比如sayByecrysmile等等。此时你只需要把它们都放到对sayName判断的if块里面就可以了。

寄生构造函数

在JS里面,有一种类似工厂模式的定义对象方法——寄生构造函数模式,如下所示:

技术分享

 

       其实,除了使用new操作符来定义新的对象,以及将其称之为构造函数之外,其他和工厂模式定义一模一样。那么这个寄生构造函数到底是为了什么呢?

我们看一个例子:

      我们定义一个Array的引用类型,并且初始化

      var colors=new Array("red","blue","yellow");

      alert("colors");    //red,blue,yellow

      有时候我们并不想数组输出元素之间用“,”分割,于是我们采用join()方法;

      但是每定义一个引用类型都使用一次join()方法有点麻烦,那么解决这个问题的办法就是直接改变Array构造函数默认定义的输出方式,

      类似Object,Array,Date等等的拥有原生构造函数的引用类型并不能直接修改其原生构造函数,那么此时寄生构造函数就派上用场了。

     于是我们可以定义如下一个特殊的Array引用类型:

技术分享

 

      只要每次利用SpecialArray()来定义新的引用类型即可实现改变Array的输出方式。

       综上所述,其实寄生构造函数就是在原生构造函数上的一个扩展,也就是你可以利用寄生构造函数来自定义一种引用类型,实现自己想要达到的效果。

function Person(name, age, job) {      var o = new Object();            o.sayName = function() {          console.log(name);      };        o.setName = function(value) {          name = value;      };        return o;  };    var friend = Person("Nicholas", 29, "Soft Engineer");  
//除了sayName方法之外,没有其他办法访问name的值

 

高级程序设计第六章(2)--创建对象