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

 更新时间:2015年03月03日 10:41:25   投稿:junjie  
这篇文章主要介绍了深入理解JavaScript系列(26):设计模式之构造函数模式详解,本文讲解了基本用法、构造函数与原型、只能用new吗?、强制使用new、原始包装函数等内容,需要的朋友可以参考下

介绍

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

基本用法

在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 parseUrl函数(来自国外的获取网址url参数)

    javascript parseUrl函数(来自国外的获取网址url参数)

    在外国一博客看到一个很好的函数,获取网址url等地址参数。非常不错,值得参考与收藏。
    2010-06-06
  • JavaScript实现页面5秒后自动跳转的方法

    JavaScript实现页面5秒后自动跳转的方法

    这篇文章主要介绍了JavaScript实现页面5秒后自动跳转的方法,涉及javascript递归调用与计时函数setTimeout的使用技巧,非常具有实用价值,需要的朋友可以参考下
    2015-04-04
  • Javascript简写条件语句(推荐)

    Javascript简写条件语句(推荐)

    下面小编就为大家带来一篇Javascript简写条件语句(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • js 调整select 位置的函数

    js 调整select 位置的函数

    js 调整select 位置的函数,向上移动,向下移动,移动到最上,移动到最后 这里把项目中写过的几个js函数来给大家分享,功能是通过js来实现对select 中的option的位置进行移动,代码如下
    2008-02-02
  • BootStrap TreeView使用实例详解

    BootStrap TreeView使用实例详解

    这篇文章主要为大家详细介绍了BootStrap TreeView使用实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • CSS代码格式化和压缩的方法与技巧

    CSS代码格式化和压缩的方法与技巧

    下面脚本之家编辑就将一些css的格式化与压缩技巧简单的介绍下,一把情况需要尽量的缩写样式,减少体积,然后用工具进行压缩,然后再测试网页。
    2007-06-06
  • 实例分析js和C#中使用正则表达式匹配a标签

    实例分析js和C#中使用正则表达式匹配a标签

    本文通过2个实例,对比分析了在js和c#中使用正则表达式匹配a标签的异同,小伙伴们自己参考下吧,有利于深刻理解正则表达式的使用。
    2014-11-11
  • js实现图片懒加载效果

    js实现图片懒加载效果

    这篇文章主要为大家详细介绍了js实现图片懒加载效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • fetch 使用及如何接收JS传值

    fetch 使用及如何接收JS传值

    这篇文章主和要介绍了fetch 使用及如何接收JS传值,文中给大家提到了fetch基本使用方式和接收js传值的使用方式,需要的朋友可以参考下
    2017-11-11
  • 你不知道的JS ES6字符串标签函数分享

    你不知道的JS ES6字符串标签函数分享

    字符串标签函数是一种特殊的函数调用语法,本文将深入探讨ES6中字符串标签函数的工作原理,并结合具体的代码展示它的威力,快跟随小编一起学习起来吧
    2023-06-06

最新评论