Object构造函数或对象字面量都可以用来创建单个对象,但是使用这些方式用同一个接口创建很多对象,会产生大量的重复代码。为了解决这个问题,工厂模式的一种变体开始被使用。

1、工厂模式

工厂模式抽象了创建具体对象的过程。因为ECMAScript无法创建类,开发人员发明了一种函数,用函数来封装以特定接口创建对象的细节,如:

 1 function createPerson(name, age, job){
2 var o = new Object();
3 o.name = name;
4 o.age = age;
5 o.job = job;
6 o.sayName = function(){
7 alert(this.name);
8 }
9 return o;
10 }
11 var person1 = createPerson("Anna", 26, "Software Engineer");
12 var person2 = createPerson("Lily", 24, "Teacher");

可以无数次调用createPerson()函数,它每次都会返回一个包含三个属性一个方法的对象。

工厂模式虽然可以创建多个对象,但无法识别对象的类型。

2、构造函数模式

构造函数可创建特定类型的对象,包括原生构造函数(如Object、Array)和自定义构造函数。

自定义构造函数,定义自定义对象类型的属性和方法,如:

 1 function Person(name, age, job){    //惯例,构造函数以大写字母开头,非构造函数以小写字母开头
2 this.name = name; //构造函数直接将属性和方法赋给了this对象
3 this.age = age;
4 this.job = job;
5 this.sayName = function(){
6 alert(this.name);
7 };
8 }
9 //使用new操作符创建Person的新实例
10 var person1 = new Person("Anna", 26, "Software Engineer");
11 var person2 = new Person("Lily", 24, "Teacher");

使用new操作符调用构造函数会经理以下4个步骤:

(1)创建一个对象;
(2)将构造函数的作用域赋值给新对象(因此this就指向了这个新对象);
(3)执行构造函数中的代码(为这个新对象添加属性)。
(4)返回新对象。

创建的实例对象有一个constructor(构造函数)属性,该属性指向Person,如:

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

对象的constructor属性最初用来标识对象类型。但是检测对象类型,instanceof更可靠。例子中创建的对象既是Object的实例,也是Person的实例。如:

alert(person1 instanceof Object); //true 所有的对象均继承自Object
alert(person1 instanceof Person); //true

构造函数模式优于工厂模式的地方:自定义的构造函数的实例将来可以标识为一种特定的类型。

构造函数缺点:每个方法都要在每个实例上重新创建一遍。但是,创建多个完成同样任务的Function实例没有必要。

3、原型模式

创建的每一个函数都有一个prototype(原型)属性,是一个指针,指向一个对象,这个 对象包含可以由特定类型的所有实例共享的属性和方法。

使用原型的对象的好处:可以让所有对象实例共享它所包含的属性和方法。

 1 function Person(){
2 }
3 Person.prototype.name = "Anna";
4 Person.prototype.age = 29;
5 Person.prototype.job = "Doctor";
6 Person.prototype.sayName = function(){
7 alert(this.name);
8 };
9 var person1 = new Person();
10 person1.sayName(); // "Anna"
11 var person2 = new Person();
12 person2.sayName(); // "Anna"
13 alert(person1.sayName == person2.sayName); //true

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

使用最广泛、认同度最高的一种建立自定义类型的方法,定义引用类型的默认模式。

构造函数模式用来定义实例属性,原型模式用来定义方法和共享的属性。

每个实例都会有自己的一份实例属性的副本,但同时共享着对方的引用,最大限度地节省了内存。

支持向构造函数传递参数。

 1 function Person(name, age, job){
2 this.name = name;
3 this.age = age;
4 this.job = job;
5 this.friends = ["Shelby", "Court"];
6 }
7 Person.prototype = {
8 constructor: Person,
9 sayName: function(){
10 alert(this.name);
11 }
12 }
13 var person1 = new Person("Anna", 26, "Software Engineer");
14 var person2 = new Person("Greg", 27, "Doctor");
15
16 person1.friends.push("Van");
17 alert(person1.friends); // "Shelby,Court,Van"
18 alert(person2.friends); // "Shelby,Court"
19 alert(person1.friends === person2.friends); // false
20 alert(person1.sayName === person2.sayName); // true

5、动态原型模式

把所有信息都封装在了构造函数中,通过在构造函数中初始化原型(仅在必要的情况下),保持了同时使用构造函数和原型的优点。

 1 function Person(name, age, job){
2 // 属性
3 this.name = name;
4 this.age = age;
5 this.job = job;
6 // 方法
7 if(typeof this.sayName != "function"){ //可以检查任何属性或方法之一即可
8 Person.prototype.syaName = function(){
9 alert(this.name);
10 };
11 }
12 }
13 var friend = new Person("Anna", 27, "Software Engineer");
14 friend.sayName();

这里对原型所做的修改,能够立即在所有实例中得到反映。

注:动态原型模式,不能使用对象字面量重写原型。如果已经创建实例后重写原型,会切断现有实例与新原型之间的联系。

6、寄生构造函数模式

创建一个函数,封装创建对象的代码,返回新创建的对象。

 1 function Person(name, age, job){
2 var o = new Object();
3 o.name = name;
4 o.age = age;
5 o.job = job;
6 o.sayName = function(){
7 alert(this.name);
8 };
9 return o; //构造函数不返回值时,默认返回新对象实例;return返回值时,可以重写调用构造函数时返回的值
10 }
11 var friend = new Person("Anna", 27, "Software Engineer");
12 friend.sayName(); // "Anna"

假设创建一个有额外方法的特殊数组,由于不能直接修改Array构造函数,因此可以使用这个模式:

 1 function SpecialArray(){
2 //创建数组
3 var values = new Array();
4 //添加值
5 values.push.apply(values, arguments);
6 //添加方法
7 values.toPipedString = function(){
8 return this.join("|");
9 };
10 //返回数组
11 return values;
12 }
13 var colors = new SpecialArray("red", "blue", "green");
14 alert(colors.toPipedString()); // "red|blue|green"

返回的对象与构造函数的原型属性之间没有关系,即构造函数返回的对象与在构造函数外部创建的对象没有什么不同。

不能依赖instanceof操作符来确定对象类型。建议在可以使用其他模式的情况下,不要使用这种模式。

7、稳妥构造函数模式

稳妥对象:没有公共属性,其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境禁止使用this和new),或者在防止数据被其他应用程序改动时使用。

 1 function Person(name, age, job){
2 //创建要返回的对象
3 var o = new Object();
4 //可以在这里定义私有变量和属性
5
6 //添加方法
7 o.sayName = function(){
8 alert(name);
9 };
10 //返回对象
11 return o;
12 }
13 var friend =Person("Anna", 27, "Software Engineer");
14 friend.sayName(); // "Anna" 除了调用sayName()方法外,没有别的方式可以访问其数据成员
15 // 非常适合某些安全执行环境

注:
使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此instanceof操作符对这种对象也没有意义。

Javascript面向对象的程序设计 —— 创建自定义类型的7种方法的更多相关文章

  1. AutoCAD.NET二次开发:创建自定义菜单的两种方法比较

    目前我已经掌握的创建CAD菜单方法有两种: COM方式: http://www.cnblogs.com/bomb12138/p/3607929.html CUI方式: http://www.cnblo ...

  2. 重学js之JavaScript 面向对象的程序设计(创建对象)

    注意: 本文章为 <重学js之JavaScript高级程序设计>系列第五章[JavaScript引用类型]. 关于<重学js之JavaScript高级程序设计>是重新回顾js基 ...

  3. JavaScript 面向对象的程序设计

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

  4. JavaScript 面向对象的程序设计(一)之理解对象属性

    首先,JavaScript 面向对象的程序设计,主要分三部分. 理解对象属性: 理解并创建对象: 理解继承. 本文主要从第一方面来阐述: 理解对象属性 首先我们来理解Javascript对象是什么?在 ...

  5. JavaScript面向对象的程序设计

    ECMAScript支持面对对象(oo)编程,但不使用类或接口.对象可以在代码执行过程中创建和增强,因此具有动态性而非严格定义的实体.在没有类的情况下,可以此采用下列模式创建对象. 工厂模式,使用简单 ...

  6. JavaScript面向对象—对象的创建和操作

    JavaScript面向对象-对象的创建和操作 前言 虽然说在JavaScript编程语言中,函数是第一公民,但是JavaScript不仅支持函数式编程,也支持面向对象编程.JavaScript对象设 ...

  7. 面向对象的程序设计之JS创建对象的9种模式及其优缺点

    目录 1.new Object () 2.字面式创建对象 3.工厂模式 4.构造函数模式 4.1.将构造函数当作函数 4.2.构造函数的问题 5.原型模式 5.1.理解原型对象 5.2.原型与in操作 ...

  8. Java 8创建Stream流的5种方法

    不知不觉间,Java已经发展到13了,来不及感慨时间过得真的太快了,来不及学习日新月异的技术更新,目前大多数公司还是使用的JDK8版本,一方面是版本的稳定,另一方面是熟悉,所以很多公司都觉得不升级也挺 ...

  9. .net中创建xml文件的两种方法

    .net中创建xml文件的两种方法 方法1:根据xml结构一步一步构建xml文档,保存文件(动态方式) 方法2:直接加载xml结构,保存文件(固定方式) 方法1:动态创建xml文档 根据传递的值,构建 ...

  10. Python中模拟enum枚举类型的5种方法分享

    这篇文章主要介绍了Python中模拟enum枚举类型的5种方法分享,本文直接给出实现代码,需要的朋友可以参考下   以下几种方法来模拟enum:(感觉方法一简单实用) 复制代码代码如下: # way1 ...

随机推荐

  1. Go 语言内置类型全解析:从布尔到字符串的全维度探究

    关注微信公众号[TechLeadCloud],分享互联网架构.云服务技术的全维度知识.作者拥有10+年互联网服务架构.AI产品研发经验.团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证 ...

  2. 基于 ActionFilters 的限流库DotNetRateLimiter使用

    前言 在构建API项目时,有时出于安全考虑,防止访问用户恶意攻击,希望限制此用户ip地址的请求次数,减轻拒绝服务攻击可能性,也称作限流.接下来,我们就来学习开源库DotNetRateLimiter 如 ...

  3. Spring-Boot-Starter 学习笔记(1)

    Spring-Boot-Starter 1. 准备配置类和 Bean 对象 Spring Boot 提供了两个注解: @Configuration:Spring 提供的配置类注解,作用在类上,代表整个 ...

  4. Python中的可迭代对象和迭代器

    1.可迭代对象 1.1.可迭代对象概念 可迭代对象,最直观的感觉就是可以使用for来循环迭代每一个元素.例如Python内置的类型:str.list.tuple.dict等类型的对象,都是可迭代对象. ...

  5. How to Install Python on Linux

    Summary Hostmonster uses the preinstalled version of Python that ships with CentOS. Because of this ...

  6. Android news Display Owner Info on Your Android Device in Case It Gets Lost

    Display Owner Info on Your Android Device in Case It Gets Lost The latest versions of Android includ ...

  7. 「Eolink Apikit 教程」API 异常监控-创建 API 监控

    API 监控能够确保 API 的稳定性.如果一个 API 出现故障或崩溃,它可能会导致整个应用程序无法正常工作.这对用户和业务来说可能是灾难性的.通过监控 API,开发团队可以及时发现问题并采取措施来 ...

  8. CF48C [The Race]

    Problem 题目简述 现有 \(n\) 个已经加过油的加油站,如果当前剩余油量 \(< 10\) 升,则会加 \(x\) 升的油. 初始状态下,有 \(x\) 升油.每个加油站之间的距离为 ...

  9. NGINX相关介绍其一

    HTTP协议和NGINX 跨网络的主机间通讯 远程的两台主机联系在一起 套接字Socket是进程间通信IPC的一种实现,允许位于不同主机(或同一主机)上不同进程之间进行通信和数据交换,SocketAP ...

  10. PXE批量网络装机

    PXE高效批量网络装机 系统装机的三种引导方式 1.硬盘 2.光驱(u盘) 3.网络启动 pxe 系统安装过程 加载boot loader Boot Loader 是在操作系统内核运行之前运行的一段小 ...