深入理解JavaScript系列(26):设计模式之构造函数模式详解


Posted in Javascript onMarch 03, 2015

介绍

构造函数大家都很熟悉了,不过如果你是新手,还是有必要来了解一下什么叫构造函数的。构造函数用于创建特定类型的对象——不仅声明了使用的对象,构造函数还可以接受参数以便第一次创建对象的时候设置对象的成员值。你可以自定义自己的构造函数,然后在里面声明自定义类型对象的属性或方法。

基本用法

在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 相关文章推荐
关于setInterval、setTimeout在jQuery中的使用注意事项
Sep 28 Javascript
Javascript 中的 call 和 apply使用介绍
Feb 22 Javascript
jquery中文乱码的多种解决方法
Jun 21 Javascript
JS防止用户多次提交的简单代码
Aug 01 Javascript
js实现遮罩层划出效果是生成div而不是显示
Jul 29 Javascript
JavaScript使用Replace进行字符串替换的方法
Apr 14 Javascript
jQuery控制控件文本的长度的操作方法
Dec 05 Javascript
webpack实现一个行内样式px转vw的loader示例
Sep 13 Javascript
浅谈React碰到v-if
Nov 04 Javascript
jQuery的ztree仿windows文件新建和拖拽功能的实现代码
Dec 05 jQuery
微信小程序实现文件预览
Oct 22 Javascript
在HTML5 localStorage中存储对象的示例代码
Apr 21 Javascript
深入理解JavaScript系列(25):设计模式之单例模式详解
Mar 03 #Javascript
js+jquery常用知识点汇总
Mar 03 #Javascript
js实现宇宙星空背景效果的方法
Mar 03 #Javascript
Angular中的Promise对象($q介绍)
Mar 03 #Javascript
Javascript设计模式之观察者模式的多个实现版本实例
Mar 03 #Javascript
Node.js 学习笔记之简介、安装及配置
Mar 03 #Javascript
JS+CSS模拟可以无刷新显示内容的留言板实例
Mar 03 #Javascript
You might like
使用PHP访问RabbitMQ消息队列的方法示例
2018/06/06 PHP
php微信公众号开发之答题连闯三关
2018/10/20 PHP
学习ExtJS 访问容器对象
2009/10/07 Javascript
Prototype的Class.create函数解析
2011/09/22 Javascript
javascript图片预加载实例分析
2015/07/16 Javascript
js基础知识(公有方法、私有方法、特权方法)
2015/11/06 Javascript
AngularJS 作用域详解及示例代码
2016/08/17 Javascript
IE8利用自带的setCapture和releaseCapture解决iframe的拖拽事件方法
2016/10/25 Javascript
BootStrap 图标icon符号图标glyphicons不正常显示的快速解决办法
2016/12/08 Javascript
js通过keyCode值判断单击键盘上某个键,然后触发指定的事件方法
2017/02/19 Javascript
javascript实现的图片预览功能
2017/03/25 Javascript
vue-resource调用promise取数据方式详解
2017/07/21 Javascript
工作中常用到的ES6语法
2018/09/04 Javascript
vue 右键菜单插件 简单、可扩展、样式自定义的右键菜单
2018/11/29 Javascript
JavaScript生成一个不重复的ID的方法示例
2019/09/16 Javascript
vue项目中微信登录的实现操作
2020/09/08 Javascript
微信小程序中target和currentTarget的区别小结
2020/11/06 Javascript
Handtrack.js库实现实时监测手部运动(推荐)
2021/02/08 Javascript
Python实现在matplotlib中两个坐标轴之间画一条直线光标的方法
2015/05/20 Python
Python 稀疏矩阵-sparse 存储和转换
2017/05/27 Python
Python命令行解析模块详解
2018/02/01 Python
python web.py开发httpserver解决跨域问题实例解析
2018/02/12 Python
python获取时间及时间格式转换问题实例代码详解
2018/12/06 Python
对python requests发送json格式数据的实例详解
2018/12/19 Python
详解Python遍历列表时删除元素的正确做法
2021/01/07 Python
基于pycharm 项目和项目文件命名规则的介绍
2021/01/15 Python
俄罗斯建筑和装饰材料在线商店:Stroilandia
2020/07/25 全球购物
学校食堂采购员岗位职责
2013/12/05 职场文书
投资合作协议书
2014/04/17 职场文书
推荐信怎么写
2014/05/09 职场文书
上课不认真检讨书
2014/09/17 职场文书
2014年学生工作总结
2014/11/20 职场文书
考研英语辞职信
2015/05/13 职场文书
2015-2016年小学教导工作总结
2015/07/21 职场文书
小学班级口号大全
2015/12/25 职场文书
windows下快速安装nginx并配置开机自启动的方法
2021/05/11 Servers