【JavaScript】(四)创建对象的7种模式

2023-10-30

        面向对象的语言有一个标志,那就是他们都有类的概念,而通过类可以创建多个具有相同属性和方法的对象。ECMAScript的对象没有类的概念,因此它的对象也与基于类的语言对象有所不同。

早期JavaScript开发人员创建类的方法是创建一个Object的实例,然后为它添加属性和方法,如下:

var person = new Object();
    person.name = "Amy";
    person.age = 25;
    person.job = "Student";
    person.sayName = function(){
         alert(this.name);
    };
    person.sayName();      //"Amy"

          这种方法的明显缺点:使用同一个接口创建很多对象,会产生大量的重复代码,为解决这个问题,工厂模式诞生。

1. 工厂模式

用函数来封装以特定接口创建对象的细节,如下:

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("Amy","25","Student");
var person2 = createPerson("Joy","29","Engineer"); 

person1.sayName();    //"Amy"
person2.sayName();    //"Joy"

       函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的person对象,可以无数次的调用这个函数。工厂模式虽然解决了创建多个相似对象的问题,但是却没有解决对象识别问题,于是有一个新模式出现了。

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("Amy","25","Student");
var person2 = new Person("Joy","29","Engineer");
person1.sayName();       //"Amy"
person2.sayName();       //"Joy"   

Person()和createPerson()的区别在于:

  • 没有显示地创建对象;
  • 直接将属性和方法赋给了this对象;
  • 没有return语句。

        要创建Person的新实例,就要用关键字new,通过new操作符来调用的任何函数,都可以作为构造函数。person1和person2都有一个constructor(构造函数)属性,该属性指向Person,检测对象类型,还可以用instanceof操作符,如下:

alert(person1.constructor == Person);  //true
alert(person2.constructor == Person);  //true

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

person1和person2之所以同时是Object的实例,是因为所有对象均继承与Object。

缺点:每个方法都要在每个实例上重新创建一遍。所以,有一种方法是说把函数定义转移到构造函数外部,代码如下:

 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("Amy",25,"Software Engineer");
    var person2=new Person("Joy",29,"Doctor");
    person1.sayName();     //"Amy"
    person2.sayName();     //"Joy"
        把sayName()函数的定义转移到构造函数外部,成为全局的函数,构造函数内部把sayName赋为为全局的sayName。这样sayName是一个指向外部函数的指针,因此person1和person2就共享了 全局的sayName函数。但是这会有个问题:全局作用域的函数只能被某个对象调用,这名不副实,会造成对全局环境的污染;更糟糕的是如果对象要定义很多方法,就要定义多少个全局函数, 那这个自定义引用类型就丝毫 没有封装性可言了。这个问题可以通过原型模式解决。

3.原型模式

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

function Person(){}
    Person.prototype.name = "Amy";
    Person.prototype.age = 25;
    Person.prototype.job = "student";
    Person.prototype.sayName = function(){
         alert(this.name);
    };        
var person1 = new Person();
var person2 = new Person();
person1.sayName();       //"Amy"
person2.sayName();       //"Amy"  
        我们将sayName()方法和所有属性直接添加到了Person的prototype属性中,构造函数变成了空函数,即便如此也可以通过调用构造函数来创建一个新对象,而且还会具有相同的属性和方法,与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。 原型模式最大的问题是由其共享的本性所导致的,代码如下:
function Person(){
}
Person.prototype={
    name:"Amy",
    age:22,
    job:"Software Engineer",
    friends:["firend1","friend2"],
    sayName:function(){
        alert(this.name);
    }
}; 
    var person1=new Person();
    var person2=new Person();
    alert(person1.friends);                           //friend1,friend2
    alert(person2.friends);                           //friend1,friend2
    alert(person1.friends==person2.friends);          //true
    person1.friends.push("friend3");
    alert(person1.friends);                           //friend1,friend2,friend3
    alert(person2.friends);                           //friend1,friend2,friend3
    alert(person1.friends==person2.friends);          //true

实例一般都要有属于自己的全部属性,所以很少有人单独使用原型模式。

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

        构造函数模式用于自定义实例属性,原型模式用于定义方法和共享属性。这样可以最大限度的节省内存,这种混合模式还支持向构造函数传递参数,可谓集两种模式之长,目前使用最广,代码如下:

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.friends=["firend1","friend2"];
}

Person.prototype={
    constructor:Person,
    sayName:function(){
        alert(this.name);
    }
}
var person1=new Person("Amy",22,"Software Engineer");
var person2=new Person("Joy",25,"Doctor");
alert(person1.friends);                           //friend1,friend2
alert(person2.friends);                           //friend1,friend2

alert(person1.friends==person2.friends);          //false
person1.friends.push("friend3");
alert(person1.friends);                           //friend1,friend2,friend3
alert(person2.friends);                           //friend1,friend2

5.动态原型模式

        动态原型模式是把所有信息都封装到构造函数中,即通过构造函数必要时初始化原型,在构造函数中同时使用了构造函数和原型,这就成了动态原型模式。真正用的时候要通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    if(typeof this.sayName!="function"){
        alert("初始化原型");//只执行一次
        Person.prototype.sayName=function(){
            alert(this.name);
        };
    }
}
var person1=new Person("Amy",22,"Software Engineer");
person1.sayName();
var person2=new Person("Joy",25,"Doctor");
person2.sayName();

        if部分只有当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 person = new Person("Amy",22,"Software Engineer");
person.sayName();       //"Amy"

除了使用new操作符使得该函数成为构造函数外,这个模式和工厂模式一模一样。构造函数在不返回值的情况下,默认返回新对象实例。返回的对象与构造函数或者与构造函数的原型属性之间没有任何关系,也就是说,构造函数返回的对象与在构造函数外创建的对象没有什么不同,所以不能用instanceof来确定对象类型。一般不使用这种模式。

7.稳妥构造函数模式

        道格拉斯·克罗克福德发明了JavaScript中的稳妥对象这个概念,稳妥对象指的是没有公共属性,而且方法也不引用this的对象。稳妥对象最适合在一些安全的环境中,或者在防止数据被其他应用程序改动时使用。

稳妥构造函数遵循与构造函数类似的模式,但有两点不同:

  • 新创建对象的实例方法不引用this
  • 不使用new操作符调用构造函数
function Person(name,age,job){
    //创建要返回的对象
    var o=new Object();
    //可以在这里定义私有变量和函数
    
    //添加方法
    o.sayName=function(){
        alert(name);
    };
    //返回对象
    return o;
}
var person=new Person("Amy",22,"Software Engineer");
person.sayName();    //"Amy"
alert(person.name);//undefined
alert(person.age);//undefined
        以这种模式创建的对象中,person是一个稳妥对象,除了使用sayName()方法外,没有其他方法访问name的值,age和job类似。即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的办法访问传入到构造函数中的原始数据。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【JavaScript】(四)创建对象的7种模式 的相关文章

随机推荐

  • GitHub博客地址

    本人博客主要发布在GitHub Pages CSDN上的博客不再更新 我的GitHub博客地址 Hong s Bloghttps yalandhong github io 备用链接 https yalandhong vercel app h
  • IDEA解决运行项目虚拟内存溢出问题

    在我们运行一个比较大的项目的时候总是出现Caused by java lang OutOfMemoryError PermGen space的问题简单在网上查询了一下意思是内存溢出了 什么 我的电脑是8G运行内存 256的固态 你告诉这样都
  • bigquant量化平台笔记

    https bigquant com tutorial 宽客学院 https bigquant com community t topic 131694 AI量化策略快速理解 AI量化策略的目标 Label 默认使用股票收益率作为目标 第一
  • 关于antd的treeselect使用

    在表单中 我们可能会碰到这样的需求 比如我想将某一个物品或者资产 分配到某一个部门的员工下 那么我们需要在表单中做这样的配置 这样就会用到antd的treeselect组件 组件很好用 自带搜索框 可以大大的提高用户的可操作性 我们来看一下
  • 【CentOS安装配置Jupyter 】

    CentOS安装配置Jupyter 功能快捷键 合理的创建标题 有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 创建一个表格 设定内容居中 居左 居右 SmartyPants 创建一个自
  • 攻防世界之misc1

    攻防世界之misc1 首先这题非常的考研脚本实力 他题目没有下载文件 只有一个值 d4e8e1f4a0f7e1f3a0e6e1f3f4a1a0d4e8e5a0e6ece1e7a0e9f3baa0c4c4c3d4c6fbb9e1e6b3e3b
  • Nginx Upstream负责均衡的实现方案

    nginx中upstream实现负责均衡的几种方式 1 轮询 weight 1 默认选项 当weight不指定时 各服务器weight相同 每个请求按时间顺序逐一分配到不同的后端服务器 如果后端服务器down掉 能自动剔除 upstream
  • commons math用户手册翻译(一)

    翻译源址 http commons apache org proper commons math userguide index html 1 math包版本3 6 2 commons math包解决哪些问题 math包由一组数据和统计的包
  • jmavsim使用说明

    一 启动 在px4源码目录下输入 make px4 sitl jmavsim 二 基础操作 在操作界面上按F1键 会弹出操作说明 操作说明如下 按照说明可以控制飞机移动 视角控制 F 第一人称视角 S 地面固定视角 G 稳定云台视角 Z 切
  • jsp 页面选项卡

  • 使用阿里巴巴的EasyExcel操作Excel

    引入依赖
  • 使用TortoiseGit执行merge/rebase操作

    如果使用git 相信大家比较多的使用merge 对于rebase则少有陌生 相信刚使用git不久的同学会有一个困惑 经常使用merge会造成破窗效应 查阅log时会掺杂了大量无用的小提交 起初也对此习以为常 毕竟svn就是这样 使用reba
  • js中background-position 无效

    css中正确书写background position x y 坐标之间有空格 然而在写js的时候如果粗心会忘记第一个 px 后面加个空格 正确js书写应该是backgroundposi x px y px
  • Docker基本概念、linux

    Docker的基本概念 一 认识容器 1 1 容器是什么 1 2 对比容器和虚拟机 1 2 1 虚拟化的差异 1 2 2 资源利用总结 二 Docker基本概念 2 1 Docker是什么 2 2 Docker 的思想与核心 2 3 Doc
  • Qt-常用数据类型

    常用基本数据类型 类型名称 注释 备注 qint8 signed char 有符号8位数据类型 qint16 signed short 有符号16位数据类型 qint32 signed int 有符号32位数据类型 qint64 long
  • php7 发送邮件,使用PHP发送邮件的两种方法

    使用PHP发送邮件的两种方法 如何使用使用PHP发送邮件呢 想要知道的朋友 以下是为大家分享的使用PHP发送邮件的两种方法 供大家参考借鉴 欢迎浏览 一 使用PHP内置的mail 函数 看了一下手册 就直接开始写代码了 如下 to test
  • 超人气思维导图XMind新年新版本,这6个新功能你一定要看

    2018 年初 XMind公司推出了这款花了三年时间重写了每一行代码的新旗舰产品XMind ZEN 经过不断的优化和完善 终于成为了一款全平台 功能丰富的优秀思维导图软件 如今已然成为国内人气思维导图软件 噔噔噔 今年XMind ZEN正式
  • Java面向对象编程

    关于抽象类与最终类 下列说法错误的是 A 抽象类能被继承 最终类只能被实例化 B 抽象类和最终类都可以被声明使用 C 抽象类中可以没有抽象方法 最终类中可以没有最终方法 D 抽象类和最终类被继承时 方法可以被子类覆盖 答案 D 已知表达式i
  • 【wpf,C#】wpf访问连接Oracle数据库的详细过程及代码(第2种方法)

    注 之前写了一篇很详细的wpf连接Oracle的过程及代码 这一篇是另外一种方法 文件是我们自己写的 小伙伴们 亦可以完成此功能 1 添加引用文件 System Data OracleClient 如图 2 新建一个类 我这里命名为 MyO
  • 【JavaScript】(四)创建对象的7种模式

    面向对象的语言有一个标志 那就是他们都有类的概念 而通过类可以创建多个具有相同属性和方法的对象 ECMAScript的对象没有类的概念 因此它的对象也与基于类的语言对象有所不同 早期JavaScript开发人员创建类的方法是创建一个Obje