前面我们介绍了可以通过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. Delphi在创建和使用DLL的时候如果使用到string,请引入ShareMem单元

    当使用了长字符串类型的参数.变量时,如string,要引用ShareMem. 虽然Delphi中的string功能很强大,但若是您编写的Dll文件要供其它编程语言调用时,最好使用PChar类型.如果您 ...

  2. Duilib源码分析(四)绘制管理器—CPaintManagerUI—(前期准备一)

    上节中提到在遍历创建控件树后,执行了以下操作:      1. CDialogBuilder构建各控件对象并形成控件树,并返回第一个控件对象pRoot:     2. m_pm.AttachDialo ...

  3. 【Java并发系列01】Thread及ThreadGroup杂谈

    img { border: solid black 1px } 一.前言 最近开始学习Java并发编程,把学习过程记录下.估计不是那么系统,主要应该是Java API的介绍(不涉及最基础的概念介绍), ...

  4. Launching web on MyEclipse Tomcat 问题

    错误提示: Launching web on MyEclipse Tomcat has encountered a problemAn internal error occurred during: ...

  5. 前端试题本(Javascript篇)

    JS1. 下面这个JS程序的输出是什么:JS2.下面的JS程序输出是什么:JS3.页面有一个按钮button id为 button1,通过原生的js如何禁用?JS4.页面有一个按钮button id为 ...

  6. Mac/IOS/linux获取当前时间包含微秒毫秒的代码

    #include <sys/time.h> 1 struct UnityLocalTimeStat { int Year; int Month; int DayOfWeek; int Da ...

  7. java接口

    一.定义 Java接口(Interface),是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为( ...

  8. 丢手帕问题即约瑟夫问题的PHP解法

    问题描述:n个人排成一圈.从某个人开始,依次报数,数到m的人被杀死.下一个人重新从1开始报数,数到m的人被杀死.直到剩下最后一个人. 解决思路:从数学角度去看,每一次报数决定谁去死是一个n.m的求余数 ...

  9. 单片机与控制实验(5)——重量测量并在LCD12864显示

    一.实验目的和要求 掌握点阵式液晶显示屏的原理和控制方法,掌握点阵字符的显示方法.掌握模拟/数字(A/D)转换方式,进一步掌握使用C51语言编写程序的方法,使用C51语言编写实现重量测量的功能. 二. ...

  10. $(document).ready,$(window).load,window.onload区别和联系

    $(document).ready是在dom结构加载完毕就执行. $(window).load 等价于window.onload,必须等到页面内包括图片的所有元素加载完毕后才能执行. $(docume ...