1.工厂模式
        我们可以把JavaScript中的对象看作成散列表,无非就是一组键值对,其中值可以是数据或函数。每个对象都是基于引用类型创建的。Object构造函数或对象字面量都可以用来创建单个对象,但是这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。我们可以使用工厂模式,用函数来封装以特定接口创建对象的细节,如:
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("wuyu", 20, "enginneer");
var person1 = createPerson("zhangsan", 29, "tearcher");
    根据接受的参数通过createPerson()函数构建包含必要信息的Person对象,可以无数次的调用这个函数并且返回包含三个属性和一个方法的对象。
    工厂模式虽然很好的解决的创建多个相似对象的问题,但却没有解决对象识别的问题,即怎么知道一个对象的类型。这里我们可以用构造函数模式来解决。
2.构造函数模式
        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("wuyu", 20, "enginneer");var person2 = new Person("zhangsan", 29, "tearcher");
    这段代码中,Person()方法(当作构造函数使用时,首字母通常大写)替代了createPerson()函数,除此之外,还存在以下不同之处:
    ●没有显式地创建对象
    ●直接将属性和方法赋给了this对象
    ●没有return 语句
要创建Person的新实例,必须使用new操作符。以这种方法调用构造函数实际上会经历以下4个步骤:
    ●创建一个新对象
    ●将构造构造函数的作用域赋给新对象(因此this就是指向了这个新对象)
    ●执行构造函数中的代码(为这个新对象添加属性)
    ●返回新对象
上面的示例,person1和person2分别保存着Person的一个不同的实例,这两个对象都有一个constructor(构造函数)属性,该属性指向Person,如:
alert(person1.constructor == Person);    //true
alert(person2.constructor == Person);    //true

对象的consctructor属性最初是用来标识对象类型的,但是检测对象类型还是使用instanceof检测对象类型比较可靠些。这例子中创建的所有对象既是Object的实例,同时也是Person的实例.如下:

    alert(person1 instanceof Object);    //truealert(person1 instanceof Person);    //truealert(person2 instanceof Object);    //truealert(person2 instanceof Object);    //true
    创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,而这就是构造函数模式胜过工厂模
式的地地方。
    构造函数也是函数,区别构造函数与其他普通函数,主要就在于调用它们的方式不同。任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new操作符来调用,那它跟普通函数也不会有什么两样。
    function Person(name,age,job){this.name=name;this.age=age;this.job=job;this.sayName=function(){alert(this.name);};}//当作构造函数使用var person = new Person("wuyan",20,"enginner");person.sayName(); //wuyan//作为普通函数使用Person("zhangsan",27,"Doctor");window.sayName(); //zhangsan//在另一个对象的作用域中调用(将Person()放入o作用域中调用)var o = new Object();Person.call(o,"Kristen",25,"Nuese");o.sayName(); //Kristen 

构造函数模式虽然好用,但也并非没有缺点。最大的问题就是每个方法都要在每个实例上重新创建一遍。比如上面的person1和person2都有一个sayName()的方法,但那两个方法不是同一个Function的实例。ECMAScript中的函数是对象,因此每定义一个函数,也就是实例化了一个对象。创建两个实现的功能完全相同的Function实例真没必要,大可通过把函数定义转移到构造函数外部来解决这个问题。如下:

   function Person(name,age,job){this.name=name;this.age=age;this.job=job;this.sayName= sayName;}functin sayName = function(){alert(this.name);};var person1 = new Person("wuyu", 20, "enginneer");var person2 = new Person("zhangsan", 29, "tearcher");

将sayName转移到构造函数外部,而在构造函数内部我们将sayName属性设置成了全局的sayName函数。这样一来,sayName包含的是一个指向函数的指针,因此person1 和person2就共享了在全局作用域中的同一个sayName()函数。可是,对象需要定义很多方法,那么就要定义很多全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。这些问题我们可以使用原型模式来解决。

3.原型模式
3.1什么是原型
      我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对        象的用途是包含可以由特定类型的所有实例共享的属性和方法。prototype就是通过调用构造函数创建的那个    对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。如下:
    function Person() {}Person.prototype.name = "wuyan";Person.prototype.age = 20;Person.prototype.job = "engineer";Person.prototype.sayName = function(){alert(this.name);}var person1 = new Person();person.sayName();    //wuyanvar person2 = new Person();person2.sayName();    //wuyanalert(person1.sayName == person2.sayName);    //true
我们将所有属性和sayName()方法直接添加到了Person的prototype属性中,在创建新对象时,这些属性和方法是由所有实例共享的。
 
3.2理解原型对象
    无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型都会自动获得一个constructor(构造函数)属性,这个属性一个指向prototype属性所在函数的指针。
    当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。这个内部属性叫做_proto_,可以在浏览调度中看见,但对于脚本来说则是完全不可见的。我们只要记住,就是这个连接存在于实例与构造函数的原型属性之间,而不是存在于实例与构造函数之间。
   
Person.prototype指向了原型对象,而Person.prototype.constructor又指回了Person。Person.prototype的对象中除了包含constructor属性之外,还包括后来添加的其他属性。Person的每个实例person1和person2都包含一个内部属性,该属性仅仅指向了Person.prototype,它们与构造函数没有直接的关系。
    我们可以使用isPrototypeOf()方法来确定对象之间是否存在在这种关系,当对象的_proto_属性指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就会返回true。如:
    alert(Person.prototype.isPrototypeOf(person1));    //truealert(Person.prototype.isPrototypeOf(person2));    //true

虽然可以通过对象实例访问保存在原型中的值,但去不能通过对象实例重写原型中的值。如下:

    function    Person() {}Person.prototype.name = "wuyan";Person.prototype.age = 20;Person.prototype.job = "engineer";Person.prototype.sayName = function(){alert(this.name);}var person1 = new Person();var person2 = new Person();person1.name = "poxiao";alert(person1.name);    //poxiao--->来自实例,在搜索name属性时,在对象实例本身就可以
//找到,就不 必再搜索原型了alert(person2.name);    //wuyan--->来自原型//当然,我们可以使用delete操作符删除实例属性,这样就可以重新访问到原型中的属性了。如下:function    Person() {}Person.prototype.name = "wuyan";Person.prototype.age = 20;Person.prototype.job = "engineer";Person.prototype.sayName = function(){alert(this.name);}var person1 = new Person();var person2 = new Person();person1.name = "poxiao";alert(person1.name);     //poxiao--->来自实例alert(person2.name);     //wuyan--->来自原型delete person1.name;alert(person1.name);    //wuyan--->来自原型

使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法(它是从Object继承来的)只在给定属性存在于对象实例中时,才会返回true。如下:

    function    Person() {}Person.prototype.name = "wuyan";Person.prototype.age = 20;Person.prototype.job = "engineer";Person.prototype.sayName = function(){alert(this.name);}var person1 = new Person();var person2 = new Person();alert(person1.hasOwnProperty("name"));    //falseperson1.name = "poxiao";alert(person1.name);    //poxiao-->来自实例alert(person1.hasOwnProperty("name"));    //truealert(person2.name);    //wuyan--->来自原型alert(person2.hasOwnProperty("name"));    //falsedelete person1.name;alert(person1.name);     //wuyan--->来自原型alert(person1.hasOwnProperty("name"));    //false
3.3原型与in操作符
    用两种方式使用in操作符:一、单独使用;二、在for-in中使用。
    单独使用时,in操作符会在通过对象能够访问属性时返回true,无论该属性存在于实例中还是原型中。如下:
    function Person(){}Person.prototype.name="wuyan";Person.prototype.age=20;Person.prototype.job="engineer";Person.sayName=function(){alert(this.name);}var person1=new Person();var person2=new Person();alert(person1.hasOwnProperty("name")); //falsealert("name" in person1); //trueperson1.name="poxiao";alert(person1.name); //poxiao来自实例alert(person1.hasOwnProperty("name")); //truealert("name" in person1); //truealert(person2.name); //wuyan来自原型alert(person2.hasOwnProperty("name")); //falsealert("name" in person2); //truedelete person1.name;alert(person1.name); //wuyan来自原型alert(person1.hasOwnProperty("name")); //falsealert("name" in person1); //truealert(person1.hasOwnProperty("qqqq")); //falsealert("qqqq" in person1); //false//同时使用hasOwnProperty()方法和in操作符,就可以确定该属性到底是存在于对象中,
//还是存在于原型中。如下:function hasPrototypeProperty(object, name){return !object.hasOwnProperty(name) && (name in object) ;}

在使用for-in循环时,返回的是所有能够通过对象访问的、可枚举的(enumerated)属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。根据规定,所有开始人员定义的属性都是可枚举的(除ie8及更早版本)。如:

var o={toString:function(){return "My Object";}
}for (var prop in o){if(prop == "toString"){alert("Found toString"); //在IE中不会显示}
}
3.4更简单的原型
    用一个包含所有属性和方法的对象字面量来重写整个原型对象。如下:
    function Person() {}Person.prototype = {name : "wuyan",age : 20,job : "engineer",sayName : function() {alert(this.name);}}
    结果与先前的相同,但有一个不相同的:constructor属性不再指向Person了。我们这样写,本质上完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。
    当然,我们可以特意将它设置回适当的值。如下:
    function Person() {}Person.prototype = {constructor : Person,name : "wuyan",age : 20,job : "engineer",sayName : function() {alert(this.name);}}
3.5原型的动态性
    如果重写了整个原型对象,就相当于把原型修改为另外一个对象了(因为调用构造函数是会为实例添加一个指向最初原型的_proto_指针),就等于切断了构造函数与最初原型之间原联系。如下:
    function Person() {}var friend = new Person();Person.prototype = {constructor : Person,name : "wuyan",age : 20,job : "enginner",sayName : function() {alert(this.name);}};friend.sayName();    //error

3.6原生对象的原型
    原型模式的重要性不公体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建,如Object、Array、String等。
3.7原型对象的问题
    原型模式也是有缺点的。首先,它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。原型模式最大的问题是由其共享的本性所导致的,尤其对引用类型属性。如下:
   function Person() {}Person.prototype = {constructor : Person,name : "wuyan",age : 20,job : "engineer",friends : ["poxiao", "wyl"],sayName : function() {alert(this.name);}};var person1 = new Person();var person2 = new Person();person1.friends.push("wx");alert(person1.friends);    //poxiao,wyl,wxalert(person2.friends);    //poxiao,wyl,wxalert(person1.friends === person2.friends);     //true  问题出来了,person1结
//交了新朋友意味着person2也必须结交这个朋友
4.组合使用构造函数模式和原型模式
    创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内在。这种混合模式还支持向构造函数传递参数。如下:
    function Person(name ,age ,job){this.name = name;this.age = age;this.job = job;this.friends = ["poxiao", "wyl"];}Person.prototype = {constructor : Person,sayName: function() {this.name;}}var person1 = new Person("wuyan", 20, "engineer");var person2 = new Person("shaobo", 26, "teacher");person1.friends.push("wyl");alert(person1.friends);    //poxiao,wyl,wylalert(person2.friends);    //poxiao,wylalert(person1.friends === person2.friends);    //falsealert(person1.sayName=== person2.sayName);    //true

这种构造函数与原型混合的模式,是目前在ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。

5.动态原型模式
    在其他OO语言的开始人员在看到独立的构造函数和原型时,很可能会感到非常困惑。动态原型模式正是致力于解决这个问题的一个方案,把它所有信息都封装在了构造函数中,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。如下:
   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);};}}var friends = new Person("wuyan", 20, "engineer");friends.sayName();
    使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。
6.寄生构造函数模式
    在前述的几种模式都不适用的情况下,可以使用寄生构造函数模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。如下:
   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 friend = new Person("wuyan", 20, "engineer");friend.sayName();     //wuyan
除了使用new操作符并把使用的包装函数叫做构造函数外,这个模式跟工厂模式一模一样。
关于寄生构造函数模式,有一点要需要说明:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖instanceof操作符来确定对象类型。由于存在上述问题,我们建议在可以使用其他模式的情况下,不要使用这种模式。
7.稳妥构造函数模式
    所谓稳妥对象,批的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this和new),或者在防止数据被其他应用程序改动时使用。稳妥构造函数与寄生构造函数有两点不同:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数。如下:
    function Person(name, age, job) {//创建要返回的对象var o = new Object();//可以在这定义私有变量和函数//添加方法o.sayName = function() {alert(name);}return o;}var friend = Person("wuyan", 20, "engineer");friend.sayName(); //wuyan
注意:在以这种模式创建的对象中,除了使用sayName()方法之外,没有其他办法访问name的值。这样,变量friend 中保存的就是一个稳妥对象。安全性高。
    和寄生构造函数模式一样,返回的对象与构造函数或者与构造函数的原型属性之间没一点关系。

javascript之创建对象相关推荐

  1. JavaScript之创建对象的模式

    使用Object的构造函数可以创建对象或者使用对象字面量来创建单个对象,但是这些方法有一个明显的缺点:使用相同的一个接口创建很多对象,会产生大量的重复代码. (一)工厂模式 这种模式抽象了创建具体对象 ...

  2. 【JavaScript】创建对象的三种方式

    JavaScript创建对象的三种方式 1.调用系统的构造函数创建对象2.自定义构造函数创建对象(结合第一种和需求通过工厂模式创建对象)3.字面量的方式创建对象 一.调用系统的构造函数创建对象 举个栗 ...

  3. JavaScript:创建对象(原型模式和构造函数模式)

    JavaScript:对象 一.理解对象 var person = {}Object.defineProperty(person,'name',{writable:false,value : 'Nik ...

  4. JavaScript 中创建对象的方法(读书笔记思维导图)

    面向对象(Object-Oriented, OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.而 ECMAScript 中没有类的概念,所以我们可以使用 ...

  5. javaScript中创建对象和Java创建对象的区别

    1. 前言 作为Java 帝国的未来继承人,Java小王子受到了严格的教育, 不但精通Java语言.Java虚拟机.java类库和框架,还对各种官方的Java规范了如指掌. 近日他听说一个叫做Java ...

  6. JavaScript中创建对象的方法

    1. 工厂模式 用函数来封装以特定接口创建对象的细节.但是这种方法无法解决确定对象类型的问题. function createPerson(name,age,job){var o = new Obje ...

  7. Javascript之创建对象(原型模式)

    我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,它的用途是包含可以有特定类型的所有实例共享的属性和方法. prototype就是通过构造函数而创建的那个对象 ...

  8. JavaScript对象创建对象的四种方式

    1.字面量的方式 2.调用系统的构造函数 3.自定义构造函数的方式 4.工厂模式创建对象 <!DOCTYPE html> <html lang="en"> ...

  9. JavaScript中创建对象方法

    1.直接创建法: 该方法简单直接,却不能直接量产 2.工厂模式---利用函数创建对象: 工厂模式解决了无法量产的问题,但是它自身的弊端就是无法 明确对象类型,比如上例中若用instanceof分别检测 ...

最新文章

  1. Scrum Master是什么?Scrum Master的职责是什么?和PM又有哪些区别?
  2. 挪动以太坊:比特币现金的新功能使其成为智能合约竞争者
  3. SpringBoot集成Mybatis动态多数据源后,MybatisPlus的IPage失效的问题解决方案
  4. interface接口_golang 基础(Four) 接口进阶
  5. 深度剖析Kubernetes API Server三部曲 - part 1
  6. CodeForces - 343D Water Tree(树链剖分+线段树)
  7. RabbitMQ之mandatory和immediate
  8. 一线大厂为什么对免费的开源项目这么热衷?
  9. SQL语句使用总结(一)
  10. 手机APP移动应用开发
  11. 卧槽!华为工程师总结的Java笔记,太优秀了!
  12. java代码split分割数字类
  13. Java项目开发管理工具-Maven基础
  14. UBUNTU完美运行TM,RTX,MSOffice,迅雷
  15. 单片机节日彩灯c语言,单片机节日彩灯控制器的设计Proteus仿真
  16. OSG正二十面体均分球面
  17. linux 达人养成计划 II笔记
  18. Netty下的消息群发
  19. 在TCP端口筛选只允
  20. APK部署手机上出现闪退现象

热门文章

  1. mysql库提示 Table ‘xxx’ is marked as crashed and should be repaired
  2. Dgraph使用总结
  3. 密室NPC的演技吊打流量明星
  4. LeetCode每日一题——380. O(1) 时间插入、删除和获取随机元素
  5. 【计算机网络】【运输层-4】
  6. JAVA导出Word文档工具EasyWord
  7. 【Spring Boot】闲聊Spring Boot(一)
  8. Oracle EBS MRP Forecast预测删除实例脚本
  9. java写一个web服务器_Java实现web服务器功能(简版) | kTWO-个人博客
  10. RME 发布 Fireface UFX III 声卡