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中经常会用到的一个复杂基本类型,对象,先从对象的属性讲起,再讲对象的创建方法,基本涵盖了创建对象的各种方法,大家一起学习呀~ 一.对象 要掌握对象的使用及继承, ...
随机推荐
- Android Studio打包.so文件教程
在eclipse里,.so文件eclipse会帮助我们自动打包进apk文件,通常是放在:libs/armeabi目录,然后把libxxx.so拷贝到这个目录下,这样NDK就会自动把这个libxxx.s ...
- Qt5的插件机制(1)--Qt 框架中的插件载入机制概述
概述 Qt的源代码中通过 Q<pluginType>Factory.Q<pluginType>Plugin 和 Q<pluginType> 这三个类实现了Qt的插件 ...
- ITOO右击菜单实现
ITOO做了持续了这么长时间,client使用MVC+EF+EasyUI框架,服务端在三层基础上增加WCF服务,后来增加容器,AOP(还没怎么接触),封装了在我们刚開始看来神奇的底层方法,克服了非常多 ...
- CodeForces 550B Preparing Olympiad(DFS回溯+暴力枚举)
[题目链接]:click here~~ [题目大意] 一组题目的数目(n<=15),每一个题目有对应的难度,问你选择一定的题目(大于r个且小于l个)且选择后的题目里最小难度与最大难度差不小于x, ...
- less09 判断语句
less //.mixin (@a) when (lightness(@a) >= 50%) { //255/2=127.5 // background-color: black; //} // ...
- django 笔记7 多对多
多对多 方法一 :双外键关联 自定义关系表 自定义 class Host(models.Model): nid = models.AutoField(primary_key=True) hostnam ...
- 集合TreeSet的使用
集合中的TreeSet是集合体系结构中的底层实现,是Collection的孙子,Set的儿子.TreeSet除拥有父接口的特点外,还有其自身的特点.下面就看看TreeSet的排序是怎么实现的.从它的构 ...
- PyCharm 2017 Mac 免注册版破解安装说明
PyCharm 2017 Mac 免注册版破解安装说明 下载完成安装包后,双击打开,将左侧拖拽至右侧应用程序,默认安装. 打开软件,在License server address中填入[http:// ...
- inception - resnet
只有reduction-A是共用的,只是改了其中的几个参数 linear是线性激活. 结构是一样的
- Hadoop集群配置搭建
环境:Centos 6.9,Hadoop 2.7.1,JDK 1.8.0_161,Maven 3.3.9 前言: 1.配置一台master服务器,两台或多台slave服务器. 2.master可 ...