深入理解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 相关文章推荐
jquery validate 自定义验证方法介绍 日期验证
Feb 27 Javascript
利用JavaScript的AngularJS库制作电子名片的方法
Jun 18 Javascript
javascript+HTML5的Canvas实现Lab单车动画效果
Aug 07 Javascript
jquery背景跟随鼠标滑动导航
Nov 20 Javascript
使用js获取地址栏参数的方法推荐(超级简单)
Jun 14 Javascript
Bootstrap轮播插件使用代码
Oct 11 Javascript
Node.js中的require.resolve方法使用简介
Apr 23 Javascript
vue-cli常用设置总结
Feb 24 Javascript
vue移动端实现红包雨效果
Jun 23 Javascript
layui实现把数据表格时间戳转换为时间格式的例子
Sep 12 Javascript
element form 校验数组每一项实例代码
Oct 10 Javascript
vue实现树形结构样式和功能的实例代码
Oct 15 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面向对象法则
2012/02/23 PHP
PHP原生模板引擎 最简单的模板引擎
2012/04/25 PHP
PHP文件操作之获取目录下文件与计算相对路径的方法
2016/01/08 PHP
PHP GD库相关图像生成和处理函数小结
2016/09/30 PHP
php微信公众平台交互与接口详解
2016/11/28 PHP
Laravel 对某一列进行筛选然后求和sum()的例子
2019/10/10 PHP
学习ExtJS fit布局使用说明
2009/10/08 Javascript
JavaScript实用技巧(一)
2010/08/16 Javascript
JQuery动态创建DOM、表单元素的实现代码
2011/08/09 Javascript
javascript特殊用法示例介绍
2013/11/29 Javascript
JQuery实现table行折叠效果以JSON做数据源
2014/05/26 Javascript
JQuery动画与特效实例分析
2015/02/02 Javascript
jQuery在页面加载时动态修改图片尺寸的方法
2015/03/20 Javascript
javascript仿百度输入框提示自动下拉补全
2016/01/07 Javascript
基于javascript实现右下角浮动广告效果
2016/01/08 Javascript
jQuery实现页面点击后退弹出提示框的方法
2016/08/24 Javascript
xmlplus组件设计系列之下拉刷新(PullRefresh)(6)
2017/05/03 Javascript
浅谈vue实现数据监听的函数 Object.defineProperty
2017/06/08 Javascript
Vue-cli中为单独页面设置背景色的实现方法
2018/02/11 Javascript
React降级配置及Ant Design配置详解
2018/12/27 Javascript
js实现简单页面全屏
2019/09/17 Javascript
Python中使用hashlib模块处理算法的教程
2015/04/28 Python
Django的分页器实例(paginator)
2017/12/01 Python
Python matplotlib通过plt.scatter画空心圆标记出特定的点方法
2018/12/13 Python
Python面向对象程序设计多继承和多态用法示例
2019/04/08 Python
执行Django数据迁移时报 1091错误及解决方法
2019/10/14 Python
opencv 实现特定颜色线条提取与定位操作
2020/06/02 Python
HTML5 标准将把互联网视频扔回到黑暗时代
2010/02/10 HTML / CSS
html5使用window.postMessage进行跨域实现数据交互的一次实战
2021/02/24 HTML / CSS
纪念九一八事变演讲稿:牢记历史,捍卫主权
2014/09/14 职场文书
作风整顿剖析材料
2014/09/30 职场文书
教师党员个人整改措施
2014/10/27 职场文书
因个人工作失误检讨书
2019/06/21 职场文书
理解深度学习之深度学习简介
2021/04/14 Python
JPA如何使用entityManager执行SQL并指定返回类型
2021/06/15 Java/Android
从结婚开始的恋爱故事。小说《我的美好婚事》TV动画化决定
2022/04/07 日漫