深入理解JavaScript系列(26):设计模式之构造函数模式
介绍
构造函数大家都很熟悉了,不过如果你是新手,还是有必要来了解一下什么叫构造函数的。构造函数用于创建特定类型的对象——不仅声明了使用的对象,构造函数还可以接受参数以便第一次创建对象的时候设置对象的成员值。你可以自定义自己的构造函数,然后在里面声明自定义类型对象的属性或方法。
基本用法
在JavaScript里,构造函数通常是认为用来实现实例的,JavaScript没有类的概念,但是有特殊的构造函数。通过new关键字来调用定义的否早函数,你可以告诉JavaScript你要创建一个新对象并且新对象的成员声明都是构造函数里定义的。在构造函数内部,this关键字引用的是新创建的对象。基本用法如下:
function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
this.output= function () {
return this.model + "走了" + this.miles + "公里";
};
}
var tom= new Car("大叔", 2009, 20000);
var dudu= new Car("Dudu", 2010, 5000);
console.log(tom.output());
console.log(dudu.output());
上面的例子是个非常简单的构造函数模式,但是有点小问题。首先是使用继承很麻烦了,其次output()在每次创建对象的时候都重新定义了,最好的方法是让所有Car类型的实例都共享这个output()方法,这样如果有大批量的实例的话,就会节约很多内存。
解决这个问题,我们可以使用如下方式:
function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
this.output= formatCar;
}
function formatCar() {
return this.model + "走了" + this.miles + "公里";
}
这个方式虽然可用,但是我们有如下更好的方式。
构造函数与原型
JavaScript里函数有个原型属性叫prototype,当调用构造函数创建对象的时候,所有该构造函数原型的属性在新创建对象上都可用。按照这样,多个Car对象实例可以共享同一个原型,我们再扩展一下上例的代码:
function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
}
/*
注意:这里我们使用了Object.prototype.方法名,而不是Object.prototype
主要是用来避免重写定义原型prototype对象
*/
Car.prototype.output= function () {
return this.model + "走了" + this.miles + "公里";
};
var tom = new Car("大叔", 2009, 20000);
var dudu = new Car("Dudu", 2010, 5000);
console.log(tom.output());
console.log(dudu.output());
这里,output()单实例可以在所有Car对象实例里共享使用。
另外:我们推荐构造函数以大写字母开头,以便区分普通的函数。
只能用new吗?
上面的例子对函数car都是用new来创建对象的,只有这一种方式么?其实还有别的方式,我们列举两种:
function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
// 自定义一个output输出内容
this.output = function () {
return this.model + "走了" + this.miles + "公里";
}
}
//方法1:作为函数调用
Car("大叔", 2009, 20000); //添加到window对象上
console.log(window.output());
//方法2:在另外一个对象的作用域内调用
var o = new Object();
Car.call(o, "Dudu", 2010, 5000);
console.log(o.output());
该代码的方法1有点特殊,如果不适用new直接调用函数的话,this指向的是全局对象window,我们来验证一下:
//作为函数调用
var tom = Car("大叔", 2009, 20000);
console.log(typeof tom); // "undefined"
console.log(window.output()); // "大叔走了20000公里"
这时候对象tom是undefined,而window.output()会正确输出结果,而如果使用new关键字则没有这个问题,验证如下:
//使用new 关键字
var tom = new Car("大叔", 2009, 20000);
console.log(typeof tom); // "object"
console.log(tom.output()); // "大叔走了20000公里"
强制使用new
上述的例子展示了不使用new的问题,那么我们有没有办法让构造函数强制使用new关键字呢,答案是肯定的,上代码:
function Car(model, year, miles) {
if (!(this instanceof Car)) {
return new Car(model, year, miles);
}
this.model = model;
this.year = year;
this.miles = miles;
this.output = function () {
return this.model + "走了" + this.miles + "公里";
}
}
var tom = new Car("大叔", 2009, 20000);
var dudu = Car("Dudu", 2010, 5000);
console.log(typeof tom); // "object"
console.log(tom.output()); // "大叔走了20000公里"
console.log(typeof dudu); // "object"
console.log(dudu.output()); // "Dudu走了5000公里"
通过判断this的instanceof是不是Car来决定返回new Car还是继续执行代码,如果使用的是new关键字,则(this instanceof Car)为真,会继续执行下面的参数赋值,如果没有用new,(this instanceof Car)就为假,就会重新new一个实例返回。
原始包装函数
JavaScript里有3中原始包装函数:number, string, boolean,有时候两种都用:
// 使用原始包装函数
var s = new String("my string");
var n = new Number(101);
var b = new Boolean(true);
// 推荐这种
var s = "my string";
var n = 101;
var b = true;
推荐,只有在想保留数值状态的时候使用这些包装函数,关于区别可以参考下面的代码:
// 原始string
var greet = "Hello there";
// 使用split()方法分割
greet.split(' ')[0]; // "Hello"
// 给原始类型添加新属性不会报错
greet.smile = true;
// 单没法获取这个值(18章ECMAScript实现里我们讲了为什么)
console.log(typeof greet.smile); // "undefined"
// 原始string
var greet = new String("Hello there");
// 使用split()方法分割
greet.split(' ')[0]; // "Hello"
// 给包装函数类型添加新属性不会报错
greet.smile = true;
// 可以正常访问新属性
console.log(typeof greet.smile); // "boolean"
总结
本章主要讲解了构造函数模式的使用方法、调用方法以及new关键字的区别,希望大家在使用的时候有所注意。
参考:http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript
同步与推荐
本文已同步至目录索引:深入理解JavaScript系列
深入理解JavaScript系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。
深入理解JavaScript系列(26):设计模式之构造函数模式的更多相关文章
- 深入理解JavaScript系列(28):设计模式之工厂模式
介绍 与创建型模式类似,工厂模式创建对象(视为工厂里的产品)时无需指定创建对象的具体类. 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类.该模式使一个类的实例化延迟到了子类.而子 ...
- 深入理解JavaScript系列(25):设计模式之单例模式
介绍 从本章开始,我们会逐步介绍在JavaScript里使用的各种设计模式实现,在这里我不会过多地介绍模式本身的理论,而只会关注实现.OK,正式开始. 在传统开发工程师眼里,单例就是保证一个类只有一个 ...
- 深入理解JavaScript系列(42):设计模式之原型模式
介绍 原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象. 正文 对于原型模式,我们可以利用JavaScript特有的原型继承特性去创建对象的方式,也就是 ...
- 深入理解JavaScript系列(39):设计模式之适配器模式
介绍 适配器模式(Adapter)是将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),适配器模式使得原本由于接口不兼容而不能一起工作的那些类(对象)可以一些工作.速成包装 ...
- 深入理解JavaScript系列(38):设计模式之职责链模式
介绍 职责链模式(Chain of responsibility)是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象 ...
- 深入理解JavaScript系列(36):设计模式之中介者模式
介绍 中介者模式(Mediator),用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 主要内容来自:http://www ...
- 深入理解JavaScript系列(30):设计模式之外观模式
介绍 外观模式(Facade)为子系统中的一组接口提供了一个一致的界面,此模块定义了一个高层接口,这个接口值得这一子系统更加容易使用. 正文 外观模式不仅简化类中的接口,而且对接口与调用者也进行了解耦 ...
- 深入理解JavaScript系列(31):设计模式之代理模式
介绍 代理,顾名思义就是帮助别人做事,GoF对代理模式的定义如下: 代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问. 代理模式使得代理对象控制具体对象的引用.代理几乎可以是任何对 ...
- 深入理解JavaScript系列(29):设计模式之装饰者模式
介绍 装饰者提供比继承更有弹性的替代方案. 装饰者用用于包装同接口的对象,不仅允许你向方法添加行为,而且还可以将方法设置成原始对象调用(例如装饰者的构造函数). 装饰者用于通过重载方法的形式添加新功能 ...
随机推荐
- Tensorflow报错:InvalidArgumentError: You must feed a value for placeholder tensor 'input_y' with dtype
此错误神奇之处是每次第一次运行不会报错,第二次.第三次第四次....就都报错了.关掉重启,又不报错了,运行完再运行一次立马报错!搞笑! 折磨了我半天,终于被我给解决了! 问题解决来源于这边博客:htt ...
- sass的基本语法与使用
一.简介 SASS是一种CSS的开发工具,提供了许多便利的写法,大大节省了设计者的时间,使得CSS的开发,变得简单和可维护.以上都是百度的,哈哈: 其实个人认为sass就是一套提高编写css效率的规则 ...
- 如何删除/mnt/cdrom?|如何删除只读文件系统(Read-only files ystem)? failed !bh ? 挂载光盘?挂载usb?
root权限下 : 首先用umount /mnt/文件夹 卸载文件系统,必要时可以用umount -f(可能丢失数据)然后rm -rf /mnt/cdrom mkdir /mnt/cdrom moun ...
- cancelbubble和stoppraopagation区别
事实上stoppropagation和cancelBubble的作用是一样的,都是用来阻止浏览器默认的事件冒泡行为. 不同之处在于stoppropagation属于W3C标准,试用于Firefox等浏 ...
- 跟我一起读postgresql源码(四)——Planer(查询规划模块)(上)
时间一晃周末就过完了,时间过得太快,不由得让人倍加珍惜.时间真是不够用哈~ 好的不废话,这次我们开始看查询规划模块的源码吧. 查询规划部分的在整个查询处理模块应该是在一个非常重要的地位上,这一步直接决 ...
- 性能检测参考SQL语句
/****** Object: StoredProcedure [dbo].[SP_CPU] Script Date: 12/09/2018 19:01:24 ******/ SET ANSI_NUL ...
- redis主从 && 伪群集
redis主从非常简单,仅需要要从上的配置文件里指定主的IP和端口就可以了.如果主上有密码,则再添加主的密码即可!! slaveof 192.168.15.230 6379 # 添加主上的IP和端口, ...
- charles破解激活方法,注册码
1 最简单的,就是买一个激活码,在网上找到一个,记录一下. // Charles Proxy License // 适用于Charles任意版本的注册码,谁还会想要使用破解版呢. // Charle ...
- pip 使用代理
pip install -i https://mirrors.aliyun.com/pypi/simple/ opencv-python 红色部分 代表使用 阿里云 代理 安装 pip
- Kibana6.x.x源码结构分析笔记