深入理解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 相关文章推荐
js 小数取整的函数
May 10 Javascript
jquery 之 $().hover(func1, funct2)使用方法
Jun 14 Javascript
js 通过html()及text()方法获取并设置p标签的显示值
May 14 Javascript
Javascript进制转换实例分析
May 14 Javascript
对javascript继承的理解
Oct 11 Javascript
js初始化验证实例详解
Nov 26 Javascript
微信小程序开发之入门实例教程篇
Mar 07 Javascript
jquery实现一个全局计时器(商城可用)
Jun 30 jQuery
微信小程序获取循环元素id以及wx.login登录操作
Aug 17 Javascript
javascript 中事件冒泡和事件捕获机制的详解
Sep 01 Javascript
微信小程序chooseImage的用法(从本地相册选择图片或使用相机拍照)
Aug 22 Javascript
vue权限问题的完美解决方案
May 08 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计划任务之关闭浏览器后仍然继续执行的函数
2010/07/22 PHP
自制PHP框架之设计模式
2017/05/07 PHP
用prototype实现的简单小巧的多级联动菜单
2007/03/24 Javascript
js 深拷贝函数
2008/12/04 Javascript
SwfUpload在IE10上不出现上传按钮的解决方法
2013/06/25 Javascript
js带按钮的提示框可供选择示例代码
2013/09/17 Javascript
js中的如何定位固定层的位置
2014/06/15 Javascript
JavaScript匿名函数用法分析
2015/02/13 Javascript
js实现背景图片感应鼠标变化的方法
2015/02/28 Javascript
JavaScript基础篇(6)之函数表达式闭包
2015/12/11 Javascript
原生js封装的一些jquery方法(详解)
2016/09/20 Javascript
老生常谈javascript变量的命名规范和注释
2016/09/29 Javascript
AngularJS 中使用Swiper制作滚动图不能滑动的解决方法
2016/11/15 Javascript
详解AngularJS 模块化
2017/06/14 Javascript
JS滚动到指定位置导航栏固定顶部
2017/07/03 Javascript
详解tween.js 中文使用指南
2018/01/05 Javascript
angularJs-$http实现百度搜索时的动态下拉框示例
2018/02/27 Javascript
微信小程序 轮播图实现原理及优化详解
2019/09/29 Javascript
python中argparse模块用法实例详解
2015/06/03 Python
详解使用python的logging模块在stdout输出的两种方法
2017/05/17 Python
python中关于for循环的碎碎念
2017/06/30 Python
教你利用Python玩转histogram直方图的五种方法
2018/07/30 Python
python批量获取html内body内容的实例
2019/01/02 Python
Python3.7 基于 pycryptodome 的AES加密解密、RSA加密解密、加签验签
2019/12/04 Python
Django 解决阿里云部署同步数据库报错的问题
2020/05/14 Python
Python requests上传文件实现步骤
2020/09/15 Python
在Python中字典按值排序的实现方法
2020/11/12 Python
荷兰美妆护肤品海淘网站:Beautinow(中文)
2020/11/22 全球购物
高中生学习生活的自我评价
2013/10/09 职场文书
应届毕业生通用的自荐书范文
2014/02/07 职场文书
中职三好学生事迹材料
2014/08/24 职场文书
交通事故和解协议书
2015/01/27 职场文书
mysql获取指定时间段中所有日期或月份的语句(不设存储过程,不加表)
2021/06/18 MySQL
linux下安装redis图文详细步骤
2021/12/04 Redis
我家女友可不止可爱呢 公开OP主题曲无字幕动画MV
2022/04/11 日漫
JS高级程序设计之class继承重点详解
2022/07/07 Javascript