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中经常会用到的一个复杂基本类型,对象,先从对象的属性讲起,再讲对象的创建方法,基本涵盖了创建对象的各种方法,大家一起学习呀~ 一.对象 要掌握对象的使用及继承, ...
随机推荐
- 如何查询mysql中是否表被锁
可直接在mysql命令行执行:show engine innodb status\G;(只能通过cmd或者shell登录mysql) 查看造成死锁的sql语句,分析索引情况,然后优化sql然后show ...
- HDU 3108 Ant Trip
Ant Trip Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- jeesite 简介
jeesite 简介 https://github.com/thinkgem/jeesite http://jeesite.com/
- 移动端页面弹出对话框效果Demo
核心思路:设置一个隐藏的(display:none;).背景偏暗的div及其子div作为对话框.当点击某处时,将此div设置为显示. 核心代码例如以下(部分js代码用于动态调整div内容的行高.这部分 ...
- 从fork面试题開始的思考
一.文章来由 还是按照惯例来说一下文章为什么来的.晚上好基友在网上刷面试题,看到一个有趣的题目,于是開始了研究,就有了这篇文章. 二.进入正题 题目例如以下: #include <stdio.h ...
- 关于DPM(Deformable Part Model)算法中模型可视化的解释
搭建了自己的博客平台,本文地址:http://masikkk.com/blog/DPM-model-visualization/ DPM源代码(voc-release)中的模型可视化做的还算相当炫酷的 ...
- Redis 存储图片 [base64/url/path]vs[object]
一.base64图片编解码 基本流程:从网络获取下载一张图片.然后base64编码,再base64解码,存到本地E盘根文件夹下. import java.awt.image.BufferedImage ...
- 7.配置文件mocha.opts
转自:http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html Mocha允许在test目录下面,放置配置文件m ...
- 基于python3-sklearn,Flask 的回归预测系统
看到一副图片挺有意思,放在片头 序 "傍晚小街路面上沁出微雨后的湿润,和煦的西风吹来,抬头看看天边的晚霞,嗯明天又是一个好天气.走到水果摊旁,挑了个根蒂蜷缩.敲起来声音浊响的青绿西瓜,一边满 ...
- ASP.NET 部分视图
ASP.NET MVC 里的部分视图,相当于 Web Form 里的 User Control.我们的页面往往会有许多重用的地方,可以进行封装重用. 使用部分视图有以下优点: 1. 可以简写代码. ...