前面我们介绍了可以通过Object构造函数或对象字面量都可以用来创建单个对象,但是如果需要创建多个对象的话,显然很多冗余代码。

接下来介绍几种模式来创建对象。不过在此之前,我们还是先来了解下 typeof和instanceof 。

typeof和instanceof

//typeof主要用了检查值类型数据,如:
alert(typeof (1) + " " + typeof ("1") + " " + typeof (false) + " " + typeof (undefined));
//instanceof主要用了检查对象,如:
var arr = new Array();
alert((arr instanceof Object) + " " + (arr instanceof Array) + " " + (arr instanceof Number));//既是Object也是array,但不是Number

例:

//typeof主要用了检查值类型数据,如:
alert(typeof (1) + " " + typeof ("1") + " " + typeof (false) + " " + typeof (undefined));
//instanceof主要用了检查对象,如:
var arr = new Array();
alert((arr instanceof Object) + " " + (arr instanceof Array) + " " + (arr instanceof Number));//既是Object也是array,但不是Number

下面我们接着介绍数种创建对象的方式。

一、 工厂模式:

function createPerson(name) {
var o = new Object();
o.name = name;
o.sayName = function () {
alert(this.name);
};
return o;
}
var obj = createPerson("张三");
var obj2 = createPerson("李四");
alert(obj instanceof Object);
alert(obj instanceof createPerson)

由上可知,工厂模式简单、思路清晰、容易理解,也可以创建对象,不过有个缺点不能确定对象类型。因为它总是一个object类型,而不能判定是createPerson类型。

例1:

function createPerson(name) {
var o = new Object();
o.name = name;
o.sayName = function () {
alert(this.name);
};
return o;
}
var obj = createPerson("张三");
var obj2 = createPerson("李四");
alert(obj instanceof Object);
alert(obj instanceof createPerson)

二、构造函数模式

var obj = { name: "李四" };
function Person(name) {
this.name = name;
this.sayName = function () {
alert(this.name);
};
}
var per = new Person("张三");
per.sayName();//张三
var per2 = new Person("李四");
alert(per.sayName==per2.sayName);//false

其实,构造函数模式我们在上篇博文就简单介绍过了。同样,构造函数模式也不完美,因为每个实例化出来的对象所拥有的方法都是独立的,而一个对象类型的方法完全是可以同享引用来节省内存空间。

例2:

var obj = { name: "李四" };
function Person(name) {
this.name = name;
this.sayName = function () {
alert(this.name);
};
}
var per = new Person("张三");
per.sayName();//张三
var per2 = new Person("李四");
alert(per.sayName==per2.sayName);//false
alert(per instanceof Person)//与上面的工厂模式不同,这里通过构造函数创建的方法,可以直接判断对象类型了。

由例2可以看出,第二次弹出消息为false,证明了每个实例对象中的方法都是独立的。 第三次弹出消息为true,与上面的工厂模式不同,这里通过构造函数创建的方法,可以直接判断对象类型了。

我们说实例对象的方法应该是共享的,那么我们可以用接下来的模式实现。

三、原型模式

1.0在使用原型模式之前,我们首先需要了解什么是原型。我的理解就是,原始对象类型的模型。每个对象都有一个属性(prototype)指向对象的原型。

function Person() {
this.sayHi1 = function () { }
}
Person.prototype.sayHi2 = function () { }; var per1 = new Person();
var per2 = new Person();
alert(per1.sayHi1 === per2.sayHi1);//每个实例化出来的对象所独有的,所以为false
alert(per1.sayHi2 === per2.sayHi2);//因为是同一个引用,所以为true
//我们再次证明了构造函数中的属性(或是方法、对象)是实例化对象独有的,原型中的属性(或是方法、对象)是共享的。

第一个比较是对象的属性,所以每个实例对象拥有独立的方法,而第二个比较是原型方法,就算是实例对象,它们直接也是引用共享的。

例3:

function Person() {
this.sayHi1 = function () { }
}
Person.prototype.sayHi2 = function () { };

var per1 = new Person();
var per2 = new Person();
alert(per1.sayHi1 === per2.sayHi1);//每个实例化出来的对象所独有的,所以为false
alert(per1.sayHi2 === per2.sayHi2);//因为是同一个引用,所以为true

我们看到了 per1.sayHi2 === per2.sayHi2  比较是true。(===是全等的意思,不仅比较值,还比较类型。)

我们看到了 __proto__ 指向的就是我们所谓的原型(只有Firefox、 Safari 和 Chrome浏览器有此属性)。还有一个 constructor 指向我们的构造函数。

1.1 __proto__ 和原型 prototype 的关系(其实__proto__并不是一个js语言中规定的对象属性,只是某些浏览器实现了

function Person() {
this.name1 = "张三",
this.sayHi1 = function () { }
}
Person.prototype.sayHi2 = function () { }; var per1 = new Person();
var per2 = new Person();
alert(per1.constructor);//constructor指向了构造函数
alert(per1.constructor.prototype);//constructor.prototype 指向了构造函数的原型
alert(per1.constructor.prototype === per1.__proto__);//true 由此看出__proto__和原型的关系。(指向了构造函数的原型)

例:

function Person() {
this.name1 = "张三",
this.sayHi1 = function () { }
}
Person.prototype.sayHi2 = function () { };

var per1 = new Person();
var per2 = new Person();
alert(per1.constructor);//constructor指向了构造函数
alert(per1.constructor.prototype);//constructor.prototype 指向了构造函数的原型
alert(per1.constructor.prototype === per1.__proto__);//true 由此看出__proto__和原型的关系。(指向了构造函数的原型)

1.2如果原型中的属性和构造函数中的属性重名,会优先访问构造函数中的属性

function Person(name) {
this.name1 = name;
};
Person.prototype.name1 = "test1";
Person.prototype.name2 = "test2"; var per1 = new Person("name1");
alert(per1.name1);//访问到的是实例对象中的name1属性“name1”
delete per1.name1;//删除实例对象中的name1属性
alert(per1.name1);//访问类型原型中的name1属性“test1” alert(per1.name2);//访问原型属性“name2”
per1.name2 = "name2";//这里并不是修改了原型属性“name2”的值,而是为实例对象动态添加了一个“name2”的属性,并赋值。
alert(per1.name2);//访问实例属性“name2”
delete per1.name2;
alert(per1.name2);//访问原型属性“name2”

例:

function Person(name) {
this.name1 = name;
};
Person.prototype.name1 = "test1";
Person.prototype.name2 = "test2";

var per1 = new Person("name1");
alert(per1.name1);//访问到的是实例对象中的name1属性“name1”
delete per1.name1;//删除实例对象中的name1属性
alert(per1.name1);//访问类型原型中的name1属性“test1”

alert(per1.name2);//访问原型属性“name2”
per1.name2 = "name2";//这里并不是修改了原型属性“name2”的值,而是为实例对象动态添加了一个“name2”的属性,并赋值。
alert(per1.name2);//访问实例属性“name2”
delete per1.name2;
alert(per1.name2);//访问原型属性“name2”

如图:

1.3使用字面量表示法为原型统一添加方法和属性

我们上面看到定义原型的属性和方法都是一个个定义的,看上去明显感觉杂乱。其实我们也可以通过字面量的方式为原型添加属性或方法,如:

function Person() { }
Person.prototype = {
name1: "张三",
age: 23,
sayHi: function () {
alert(this.name1);
}
}
var per1 = new Person();
per1.name1 = "李四"
per1.sayHi()

缺点:这样的定义,相当与重写了对象类型的prototype属性,也就是我们再也访问不到 constructor 属性了。

当然,我也可以手动设置,如:

function Person() { }
Person.prototype = {
constructor: Person,//手动设置constructor赋值Person
name1: "张三",
age: 23,
sayHi: function () {
alert(this.name1);
}
}
var per1 = new Person();
per1.name1 = "李四"
per1.sayHi()

我们通过在构造函数中定义属性,在原型中通过字面量表示法定义方法已经可以很好的创建对象了。唯一的缺点就是分为两个步骤,那么我们下面试着全部封装到构造函数中。如:

function Person(str1, str2, str3) {
this.name1 = str1;
this.name2 = str2;
this.age = str3;
//方法
if (typeof this.sayName != "function") {//只用判断一个就可以了,第一次构造的时候是不能有sayName方法的。
//在构造函数里面貌似不能通过字面量来为prototype统一赋值
Person.prototype.sayName = function () {
alert(this.name1);
};
Person.prototype.sayHi = function () {
alert(this.name2);
};
}
} var per1 = new Person("张三","李四","12");
per1.sayName();

我们平时常用的一些创建对象的方式就介绍到这里了。

下一篇继续分析对象的继承,欢迎大家继续关注。

这是学习记录,不是教程。文中错误难免,您可以指出错误,但请不要言辞刻薄。

原文链接:http://haojima.net/zhaopei/516.html

本文已同步至目录索引:一步步学习javascript

欢迎上海“程序猿/媛”、"攻城狮"入群:【沪猿】229082941 入群须知

欢迎对个人博客感兴趣的道友加入群:【嗨-博客】469075305 入群须知

如果您觉得文章对您有那么一点点帮助,那么麻烦您轻轻的点个赞,以资鼓励。

一步步学习javascript基础篇(4):面向对象设计之创建对象(工厂、原型和构造函数等模式)的更多相关文章

  1. 一步步学习javascript基础篇(0):开篇索引

    索引: 一步步学习javascript基础篇(1):基本概念 一步步学习javascript基础篇(2):作用域和作用域链 一步步学习javascript基础篇(3):Object.Function等 ...

  2. 一步步学习javascript基础篇(3):Object、Function等引用类型

    我们在<一步步学习javascript基础篇(1):基本概念>中简单的介绍了五种基本数据类型Undefined.Null.Boolean.Number和String.今天我们主要介绍下复杂 ...

  3. 一步步学习javascript基础篇(5):面向对象设计之对象继承(原型链继承)

    上一篇介绍了对象创建的几种基本方式,今天我们看分析下对象的继承. 一.原型链继承 1.通过设置prototype指向“父类”的实例来实现继承. function Obj1() { this.name1 ...

  4. 一步步学习javascript基础篇(8):细说事件

    终于学到事件了,不知道为何听到“事件”就有一种莫名的兴奋.可能是之前的那些知识点过于枯燥无味吧,说起事件感觉顿时高大上了.今天我们就来好好分析下这个高大上的东西. 可以说,如果没有事件我们的页面就只能 ...

  5. 一步步学习javascript基础篇(1):基本概念

    一.数据类型 数据类型 基本数据类型(五种) Undefined Null Boolean Number String 复杂数据类型(一种) Object Undefined:只有一个值undefin ...

  6. 一步步学习javascript基础篇(7):BOM和DOM

    一.什么是BOM.什么是DOM BOM即浏览器对象模型,主要用了访问一些和网页无关的浏览器功能.如:window.location.navigator.screen.history等对象. DOM即文 ...

  7. 一步步学习javascript基础篇(6):函数表达式之【闭包】

    回顾前面介绍过的三种定义函数方式 1. function sum (num1, num2) { return num1 + num2; }  //函数声明语法定义 2. var sum = funct ...

  8. 一步步学习javascript基础篇(2):作用域和作用域链

    作用域和作用域链 js的语法用法非常的灵活,且稍不注意就踩坑.这集来分析下作用域和作用域链.我们且从几道题目入手,您可以试着在心里猜想着答案. 问题一. if (true) { var str = & ...

  9. 一步步学习javascript基础篇(9):ajax请求的回退

    需求1: ajax异步请求 url标识请求参数(也就是说复制url在新页面打开也会是ajax后的效果) ajax异步请求没问题,问题一般出在刷新url后请求的数据没了,这就是因为url没有记录参数.如 ...

随机推荐

  1. Lua源码编译之CL编译器编译

    通过使用VC下的CL编译器,可方便地编译Lua源码,而无需构造工程并设置各种选项: 以下以源码Lua5.3.1版本为例,将通过CL编译选项直接编译源码,为方便编译将采用批处理脚本,脚本放置在Lua解压 ...

  2. 使用dd命令备份Linux分区

    为了备份分区,开始使用的是Remastersys,但最终生成的iso文件仅有几十K,应该是软件bug,且此软件不再更新,后尝试使用Linux Respin,但github一直连接不上. 其实可以尝试使 ...

  3. [Git] 还原Git上commit,但是没有push代码

    直接在Idea上操作2步解决: 1. 找到: 2. 在To Commit里面填写:HEAD^,表示将commit的信息还原为上一次的,需要多次直接reset多次即可: 使用命令行:原理一样 以下内容转 ...

  4. Cocos2d-x 生成真正的随机数

    关于随机数 cocos2d-x 定义了一个宏 CCRANDOM_0_1 生成的是 [0, 1] 之间的值 因此,要生成  [0-100] 之间的数    CCRANDOM_0_1 * 100 生成 [ ...

  5. gcc -Wall -pedantic -ansi(转载)

    转载自R-G-Y-CQ的新浪博客 -Wall显示所有的警告信息 -Wall选项可以打开所有类型的语法警告,以便于确定程序源代码是否是正确的,并且尽可能实现可移植性. 对Linux开发人员来讲,GCC给 ...

  6. Youth -Samuel Ullman

    Samuel Ullman(塞缪尔.厄尔曼) Youth is not a time of life,it is a state of mind;青春不是年华,而是心境: it is not a ma ...

  7. 进击的Python【第十七章】:jQuery的基本应用

    进击的Python[第十七章]:jQuery的基本应用

  8. linux 负载均衡

    [博文推荐]关于负载均衡技术使用的一些误区 如今,负载均衡已经不是一个新鲜的词,也不是什么新技术,主要用于解决单机负载能力的局限性,但问题是你的应用真的到了单 机的负载上限了吗,未必,很多不知道如何推 ...

  9. iOS drewRect方法

    You do not need to override this method if your view sets its content in other ways. By the time thi ...

  10. 深入理解Java的接口和抽象类(转)

    深入理解Java的接口和抽象类 对于面向对象编程来说,抽象是它的一大特征之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类.这两者有太多相似的地方,又有太多不同的地方.很多人在初学的 ...