JavaScript创建对象(二)——构造函数模式

简介: 在JavaScript创建对象(一)—— 工厂模式中留下了一个问题,就是创建一个对象怎么判断一个对象的类型。换句话说使用下面这种方式:function createPerson(name, age, job){var o = new Object(); o.

在JavaScript创建对象(一)—— 工厂模式中留下了一个问题,就是创建一个对象怎么判断一个对象的类型。换句话说使用下面这种方式:

function createPerson(name, age, job){
var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        console.log(this.name);
    }
    return o;
}

只是函数的名字叫createPerson,其实返回的对象的本质还是一个Object。现在只能你说他是人他就是人,你说他是狗他就是狗,有没有一种方法比如类似下面这样:

var p1 = createPerson('zhangsan', 18, 'JavaScript');
if(p1 == Person){
    alert('p1是人');
}
if(p1 == Dog){
    alert('p1是狗');
}

在程序上有一种判断逻辑可以区分对象的类型呢?答案是有的,这就是本篇文章要讨论的构造函数模式。

现在使用构造函数模式改写上面的工厂模式:

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    }
}
var p1 = new Person('zhangsan', 18, 'JavaScript');
var p2 = new Person('lisi', 20, 'Java');

我们比较构造函数模式和工厂模式发现构造函数模式有以下不同:

  1. 没有显式的创建对象,如工厂模式 var o = new Object();
  2. 直接将属性和方法赋给了this,而不是o
  3. 没有return语句
  4. 创建对象使用new关键字,而不是直接调用函数

现在我们可以来讨论如何解决判断对象类型的问题了,很简单,可以使用如下代码:

alert(p1.constructor == Person); //true
alert(p2.constructor == Person); //true

同理,判断一个对象是否是狗,也可以这样:

function Dog(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    }
}
var d1 = new Dog('xiaohei', 2, 'kanjia');
var d2 = new Dog('xiaohua', 3, 'huyuan');
alert(d1.constructor == Dog); //true
alert(d2.constructor == Dog); //true

这样就可以用代码在逻辑上判断一个对象的类型了。

检测对象的类型除了可以使用对象的constructor属性还可以使用instanceof关键字。JavaScript和Java一样,所有对象均继承自Object,使用instanceof可以检测到继承链,但是constructor不行,所以从这个角度来说instanceof更可靠一些,因为一个对象是子类型,当然也是父类型。看下面代码:

alert(p1.constructor == Object); //false
alert(p1.constructor == Person); //true
alert(p1 instanceof Object); //true
alert(p1 instanceof Person); //true

至此构造函数函数不止具有了工厂模式的优点,还解决了工厂模式的缺点,那么构造函数模式有没有缺点呢?当然也是有的。
以上述p1p2为例,这两个对象都具有sayName函数,而且功能也一样。在构造函数中sayName是一个指针,
function(){alert(this.name)}是一个匿名函数,我们知道js中函数也是对象,每次创建对象都要执行构造函数,那么也就会创建一个匿名函数对象,若是创建若干个对象,那么就要创建若干个匿名函数对象,功能都相同,没必要不说,还占用内存。

通过以下代码可以说明p1sayNamep2sayName确实是两个实例。

alert(p1.sayName == p2.sayName); //false

那么我们可以尝试像下面这样解决这个问题,把函数定义到构造函数之外:

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}
function sayName(){
    alert(this.name);
}
var p1 = new Person('zhangsan', 18, 'JavaScript');
var p2 = new Person('lisi', 20, 'Java');
alert(p1.sayName == p2.sayName); //true

这样虽然解决了多个函数做同一件事的问题,但是又带来了新的问题:定义在全局作用域中的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。比如这里的sayName(),实际上是为Person准备的,但因为是定义在全局作用域所以可以直接调用sayName(),也就是window.sayName(), 那么此时的this.name就是window.name了,违背了定义的初衷。另一个问题是:如果Person上需要定义很多方法,那么就要定义很多个全局函数,若再加上Dog等等其他对象的方法,这些方法全都定义为全局函数,相互揉杂在一起,那么我们自定义的引用类型就丝毫没有封装性可言了。

那么,构造函数模式多个函数做同一件事的问题到底怎么解决呢?请看下一篇——原型模式。

文章参考:《JavaScript高级程序设计》第3版

目录
相关文章
|
1月前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
1月前
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
21天前
|
JSON 前端开发 JavaScript
JavaScript中对象的数据拷贝
本文介绍了JavaScript中对象数据拷贝的问题及解决方案。作者首先解释了对象赋值时地址共享导致的值同步变化现象,随后提供了五种解决方法:手动复制、`Object.assign`、扩展运算符、`JSON.stringify`与`JSON.parse`组合以及自定义深拷贝函数。每种方法都有其适用场景和局限性,文章最后鼓励读者关注作者以获取更多前端知识分享。
18 1
JavaScript中对象的数据拷贝
|
3天前
Next.js 实战 (三):优雅的实现暗黑主题模式
这篇文章介绍了在Next.js中实现暗黑模式的具体步骤。首先,需要安装next-themes库。然后,在/components/ThemeProvider/index.tsx文件中新增ThemeProvider组件,并在/app/layout.tsx文件中注入该组件。如果想要加入过渡动画,可以修改代码实现主题切换时的动画效果。最后,需要在需要的位置引入ThemeModeButton组件,实现暗黑模式的切换。
|
21天前
|
JavaScript 前端开发
JS中的构造函数的含义和用法
构造函数是JavaScript中用于创建新对象的特殊函数,通常首字母大写。通过`new`关键字调用构造函数,可以初始化具有相同属性和方法的对象实例,从而实现代码复用。例如,定义一个`Person`构造函数,可以通过传入不同的参数创建多个`Person`对象,每个对象都有自己的属性值。此外,构造函数还可以包含方法,使每个实例都能执行特定的操作。构造函数的静态属性仅能通过构造函数本身访问。ES6引入了`class`语法糖,简化了类的定义和实例化过程。
24 1
|
21天前
|
存储 前端开发 JavaScript
JavaScript常用的内置构造函数
JavaScript提供的内置构造函数在数据处理、对象创建、异常处理和异步操作等方面发挥了重要作用。掌握这些构造函数及其用法,对于开发高效、可靠的JavaScript应用至关重要。通过合理使用这些内置构造函数,开发者可以更好地管理和操作各种类型的数据,提升开发效率和代码质量。
23 1
|
24天前
|
前端开发 JavaScript UED
探索JavaScript的异步编程模式
【10月更文挑战第40天】在JavaScript的世界里,异步编程是一道不可或缺的风景线。它允许我们在等待慢速操作(如网络请求)完成时继续执行其他任务,极大地提高了程序的性能和用户体验。本文将深入浅出地探讨Promise、async/await等异步编程技术,通过生动的比喻和实际代码示例,带你领略JavaScript异步编程的魅力所在。
21 1
|
1月前
|
JavaScript 前端开发 图形学
JavaScript 中 Math 对象常用方法
【10月更文挑战第29天】JavaScript中的Math对象提供了丰富多样的数学方法,涵盖了基本数学运算、幂运算、开方、随机数生成、极值获取以及三角函数等多个方面,为各种数学相关的计算和处理提供了强大的支持,是JavaScript编程中不可或缺的一部分。
|
2月前
|
前端开发 JavaScript UED
探索JavaScript中的异步编程模式
【10月更文挑战第21天】在数字时代的浪潮中,JavaScript作为一门动态的、解释型的编程语言,以其卓越的灵活性和强大的功能在Web开发领域扮演着举足轻重的角色。本篇文章旨在深入探讨JavaScript中的异步编程模式,揭示其背后的原理和实践方法。通过分析回调函数、Promise对象以及async/await语法糖等关键技术点,我们将一同揭开JavaScript异步编程的神秘面纱,领略其带来的非阻塞I/O操作的魅力。让我们跟随代码的步伐,开启一场关于时间、性能与用户体验的奇妙之旅。
|
1月前
|
前端开发 JavaScript UED
探索JavaScript的异步编程模式
【10月更文挑战第33天】在JavaScript的世界里,异步编程是提升应用性能和用户体验的关键。本文将带你深入理解异步编程的核心概念,并展示如何在实际开发中运用这些知识来构建更流畅、响应更快的Web应用程序。从回调函数到Promises,再到async/await,我们将一步步解锁JavaScript异步编程的秘密,让你轻松应对各种复杂的异步场景。