JS构造函数、对象工厂、原型模式
1.对象创建的3中方法
1.1.对象字面量
var obj = {
name: "mingzi",
work: function () { console.log("working...") },
_age: 18, //下划线开头表示该属性不建议在外部被直接访问,但是任然可以被访问,除非使用类或函数定义
//age: 18,使用下划线定义属性后,对象上会默认生成这样一个没有下划线的属性引用,指向同一个值,用来被外部访问
get age(){
console.log("get方法被调用");
return this._age; //这里不能是this.age,否则会陷入死循环
},
set age(val){
console.log("set方法被调用");
if (val > 100 || val < 0) {
throw new Error("invalid value");
} else {
this._age = val;//这里也一样,不能是this.age
}
},
address: {
home: "dingxi",
office: "xian"
}
}
console.log(obj.age); //这里如果写成obj._age,则属性访问不经过get方法
obj.age = 20;//同样,这里写obj._age也不经过set方法,但能设置成功
console.log(obj.age);
使用get/set的好处就是可以对值进行逻辑判断,这种方法定义的对象并不能实现Java中的private私有化效果。
在对多层嵌套对象的属性级联访问时,要对中间属性判空,除了多个if 判断之外,还可以这么写:
var result = obj && obj.address && obj.address.home; //这种方式,只要中途有任何一个地方为null或undefined,则result被赋值为空,否侧赋值为最后一个。
注意:没有这个属性的时候会返回undefined,有属性没值的时候才会返回null。
1.2. var obj = new Object()
与第一种创建的对象是一样的,因为其构造器一样。
构造器结构图:

1.3.Object.defineProperties方式
可以定义属性配置项,不常用
2.对象工厂
加入我们需要创建两个具有相同结构的对象,你可能会这么做:
var a = {
name: "zhangsan",
age: 12
}
var b = a;
console.log(a === b);//true
b.age = 13;
console.log(a);//{ name: 'zhangsan', age: 13 }
但是,显然这么做并不能创建一个具有相同结构的对象b,因为只是复制了一个栈中的引用,堆中的对象是一样的,修改了b.age也就修改了a.age,并不能达到复制对象的效果。
除非复制代码,但这不是程序员应该做的事,所以我们就可以使用对象工厂:
function PersonFactory(name, age) {
return {
name: name,
age: age
}
}
var p1 = PersonFactory("zz",13);
var p2 = PersonFactory("zz",13);
console.log(p1 === p2); //false
通过一个工厂方法,就可以创建具有相同结构的不同对象了,工厂方法每次被调用,就相当于“复制”了一份代码,达到了复制对象的效果。
工厂方法的缺点是:
生产出来的对象虽然结构一样,但他们之间是完全独立的个体,并不是像Java一样的父对象与子对象之间的继承关系,每个对象占用一份独立的内存,无法共用。
3.构造函数
虽然JS是一门面向对象语言,但是并没有像Java一样的类(class)的概念
因为面向对象的三大特征是继承、封装、多态,并没有类、接口等
对象是一种运行时的构造,类是一种开发时的构造、类和接口只是一种实现面向对象的手段。
所以不同语言在面向对象的本质上是一样的,但实现方式可能有所不同,为了满足需求,JS也通过构造函数的形式实现了类:
//构造函数名默认大写,以作区分,可以看作是一个类
function Person(name, age) {
//私有的不同属性直接挂载在this上,指向创建的对象
this.name = name;
this.age = age;
}
//共有的属性挂载在对象原型上,就像父对象
Person.prototype.address = "xian"; var p1 = new Person("xx",13);
var p2 = new Person("yy",14);
console.log(p1);//Person { name: 'xx', age: 13 }
console.log(p1.address); //xian
console.log(p2.address); //xian
构造方法和工厂方法除了可以设置对象原型的属性外效果是一样的,但后者显然更高端一点。
4.原型模式
4.1.原型的定义
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor
(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。它们与构造函数没有直接的关系。

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,
则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。也就是说,在我们调用 person1.sayName() 的时候,会先后执行两次搜
索。
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性设置为 null ,也
只会在实例中设置这个属性,而不会恢复其指向原型的连接。不过,使用 delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性。
4.2更简单的原型语法
function Person(){
}
Person.prototype = {
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
在上面的代码中,我们将 Person.prototype 设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但有一个例外: constructor 属性不再指向 Person 了,而是指向Object,不过可以再设置回来。
4.3原型的动态性
可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,那么情况就不一样了。我们知道,调用构造函数时会为实例添加一个指向最初原型[[Prototype]] 指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型,而不指向构造函数。
4.4原型对象的问题
function Person(){
}
Person.prototype = {
constructor: Person,
name : "Nicholas",
age : 29,
job : "Software Engineer",
friends : ["Shelby", "Court"],
sayName : function () {
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true
即引用类型的原型属性一般来说是私有的,与原型的共享性冲突。
4.5组合使用构造函数模式和原型模式
创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。下面的代码重写了前面的例子。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
这种构造函数与原型混成的模式,是目前在 ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式,这便是JS的伪类模式。
关于原型对象一节,详见《JavaScript高级程序设计(第三版)》148页,写的巨好。
JS构造函数、对象工厂、原型模式的更多相关文章
- js原生设计模式——7原型模式之真正的原型模式——对象复制封装
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- js 构造函数(construction)与原型(prototype)
1.面向对象:js原型 java有class和instance,js仅仅有构造函数(function Cat(name,age){this.name=name;this.age=age}),为了实现数 ...
- JS构造函数原理与原型
1.创建对象有以下几种方式: ①.var obj = {}; ②.var obj = new Object(); ③.自定义构造函数,然后使用构造函数创建对象 [构造函数和普通函数的区别:函数名遵循大 ...
- JS创建对象之动态原型模式
动态原型模式把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了 同时使用构造函数和原型的优点:换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初 ...
- js原生设计模式——7原型模式之new+call(this)组合应用再探讨实例
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- 面向对象的JavaScript --- 原型模式和基于原型继承的JavaScript对象系统
面向对象的JavaScript --- 原型模式和基于原型继承的JavaScript对象系统 原型模式和基于原型继承的JavaScript对象系统 在 Brendan Eich 为 JavaScrip ...
- 设计模式系列之原型模式(Prototype Pattern)——对象的克隆
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- Objective-C设计模式——工厂方法模式virtual constructor(对象创建)
工厂方法模式 工厂方法模式可以控制对象的创建过程,屏蔽对象创建的细节,可以直接创建出我们所需要的已经配置好的对象. 工厂方法模式定义了创建方法的接口,让子类决定实例化哪一个类,工厂方法模式使得一个类的 ...
- JavaScript--面向对象与原型(15)
// ECMAScript有两种开发模式:1.函数式(过程化);2.面向对象(OOP); 一 创建对象 1.普通的创建对象 1 // 创建一个对象,然后给这个对象新的属性和方法; 2 var box ...
- js中对象和对象创建方法
这一次我们来说一说在JavaScript中经常会用到的一个复杂基本类型,对象,先从对象的属性讲起,再讲对象的创建方法,基本涵盖了创建对象的各种方法,大家一起学习呀~ 一.对象 要掌握对象的使用及继承, ...
随机推荐
- Python学习笔记-练习编写ATM+购物车(购物商城)
作业需求: 模拟实现一个ATM + 购物商城程序: 1.额度 15000或自定义 2.实现购物商城,买东西加入 购物车,调用信用卡接口结账 3.可以提现,手续费5% 4.支持多账户登录 5.支持账户间 ...
- bzoj1433: [ZJOI2009]假期的宿舍(最大二分图匹配)
1433: [ZJOI2009]假期的宿舍 题目:传送门 题解: 这题有点水 跑个二分图匹配就完事了(注意在校生不是一定都互相认识) 代码: #include<cstdio> #inclu ...
- Java中泛型的各种使用
Java中的泛型的使用: 1.普通的泛型使用 在使用类的时候后面的<>中的类型就是我们确定的类型. public class MyClass1<T> {//此处定义的泛型是T ...
- 福建省赛-- Common Tangents(数学几何)
Problem B Common Tangents Accept: 191 Submit: 608 Time Limit: 1000 mSec Memory Limit : 32768 K ...
- Windows 10问题
微软修复Windows updat 工具WindowsUpdateDiagnostic.diagcab: http://download.microsoft.com/download/F/2/2/F2 ...
- VS自定义开发向导中的vsdir文件的简单说明
作者:朱金灿 来源:http://blog.csdn.net/clever101 VS自定义开发向导中有一个vsdir文件.这个文件指定了在VS中项目的标题.默认工程名等内容.下面对vsdir文件做一 ...
- C++中inline函数
(一)inline函数(摘自C++ Primer的第三版) 在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联. inline int min(int first, int ...
- c# 结构 struct
结构是使用 struct 关键字定义的,与类相似,都表示可以包含数据成员和函数成员的数据结构. 一般情况下,我们很少使用结构,而且很多人也并不建议使用结构,但作为.NET Framework 一般型別 ...
- 洛谷 P3804 【模板】后缀自动机 统计单词出现次数
后缀自动机模板题. 关键时求解每个节点的 $right$ 大小. 由于后缀自动机在构建时会保证点和点的 $right$ 只可能没有交集,或者一个是另一个的真子集,我们可以不重复的对 $right$ 进 ...
- Redis批量执行(如list批量添加)命令工具 —— pipeline管道应用
前言 Redis使用的是客户端-服务器(CS)模型和请求/响应协议的TCP服务器.这意味着通常情况下一个请求会遵循以下步骤: 使用Redis管道提升性能 (1)客户端向服务端发送一个查询请求,并监听S ...