理解 JavaScript(三)

JavaScript 中的构造器

什么是构造器?

构造器也叫构造函数,它就是一个普通的函数,只不过它的主要目的是用于和 new 操作符配合来创建特定类型的对象。(关于 new 操作符,我的理解 JavaScript(一)里有进一步描述)

举例:

var me = new Person('Albert', 'Yu', 32);    // Person 即是构造函数

在本例中,me 对象具有特定的类型,可称之为:一个 Person 对象。而 Person 就是它的类型名字。

那么构造函数内部又是如何工作的?

function Person(firstName, lastName, age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
};

和普通的函数相比,有两个明显的区别,解释如下:

  1. 函数名称首字母大写:这其实只是一个约定而不是强制行为,绝大多数 JavaScript 程序员都会遵守这个约定,即首字母大写的函数名是一个构造函数;当然,首字母小写的函数一样可以充当构造函数
  2. 使用 this 捆绑局部变量:一般性的函数都是直接创建本地变量来保存值,而构造函数使用 this 捆绑局部变量是为了配合 new 操作符。因为 new 操作符会创建一个新对象,并且把新对象绑定在构造函数内部的 this 上,于是被捆绑在 this 上的局部变量事实上就成为新对象的属性了

因此,这样的构造函数所创建的对象大致等价于:

var me = {
    firstName: 'Albert',
    lastName: 'Yu',
    age: 32
};

那么既然如此,我们为啥还要写构造函数?

构造函数的用处

1. 用作创建对象的模板

如果只需要创建少量对象(比如一个),编写构造函数的确没太大意义。但是遇到需要重复创建对象的场合,构造函数显然是 DRY(Don't Repeat Youself)的上佳选择。

不仅仅是为了减少重复编码的工作量,而且经常会有对于输入参数进行验证并抛出错误的功能设计,也可以借助构造函数来完成。

2. 用作对象类型的标记

使用构造函数创建的对象可以很方便的检查它们的类型,比如:

var me = new Person('Albert', 'Yu', 32);
var you = {
    firstName: 'Super',
    lastName: 'Man',
    age: 18
};

console.log(me instanceof Person);          // true
console.log(you instanceof Person);         // false 

3. 为对象创建共享属性

这可能是使用构造函数最重要的理由了。基于原型继承的 JavaScript 可以很方便的为对象扩充成员属性:

var me = new Person('Albert', 'Yu', 32);
me.firstName;                               // "Albert"
me.lastName;                                // "Yu"
me.age;                                     // 32

// 我想要标记所有的 Person 对象都是活着的……
Person.prototype.isAlive = true;
me.isAlive;                                 // true

// 我还想要输出用户的全名……
me.firstName + ' ' + me.lastName;           // "Albert Yu"
// 这样太二了吧?

Person.prototype.fullName = function() {
    return [this.firstName, this.lastName].join(' ');
};
me.fullName();                              // "Albert Yu"
// 嗯,文艺多了……

更好的构造函数

有一点特别需要注意的!

使用构造函数创建对象一定要使用 new 操作符

这是因为(再次强调):真正创建新对象的不是构造函数,而是 new 操作符。构造函数只是充当新对象的模板,它接收 new 创建的对象然后用模板填充这个对象的属性设置。

鉴于此,忘记使用 new 的话是比较危险的。因为没有 new 创建新对象的时候,构造函数内的 this 会被捆绑给全局对象,通常是 window(浏览器)或者 global(Node.js),让我们看看会发生啥事儿吧……

var me = new Person('Albert', 'Yu', 32);
me.firstName;                                    // "Albert"

var you = Person('Super', 'Man', 18);
you.firstName;                                   // undefined... WTF?!

this.firstName;                                  // "Super"...Shiiiit!
// 上面的 this 是全局对象

那么如何改进构造函数呢?简单。

function Person(firstName, lastName, age) {
    if (this instanceof Person) {                // 想一想还有没有其他的判断方式?
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    } else {
        throw new Error('不用 `new` 是不可以的哟~~~');
    }
}

这个思路就是先判断构造函数接收到的 this 是不是自己的实例,若是则一切好说,若不是则抛出错误强制用户使用 new 操作符。

当然,这个改进虽然可靠了,但还是不够“聪明”,要是能让构造函数自己判断来自动使用 new 该多好呀!没错,这是更好地方式,不过在这里我就不演示了,还是由您自己来动手实践一下吧?(提示:考虑一下构造函数的原型对象。如果想不出来的话不要紧,我们以后接着聊)


太极客(Very Geek)
As a designeer, I hope you can prove me wrong.

正在更新 Elixir 语言的系列文章:[链接]

31.1k 声望
3.1k 粉丝
0 条评论
推荐阅读
为 Koa 框架封装 webpack-dev-middleware 中间件
我见到有很多朋友在 SegmentFault 上面问一些不太好回答的问题,“JavaScript/Node 学好了能做什么?”,“前端架构师每天都做些什么?”等等。这些问题并非不能回答,但是第一、问题本身太过泛泛,很难回答的既针对...

n͛i͛g͛h͛t͛i͛r͛e͛25阅读 12.4k评论 6

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy49阅读 7.2k评论 12

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs42阅读 6.9k评论 12

封面图
「多图预警」完美实现一个@功能
一天产品大大向 boss 汇报完研发成果和产品业绩产出,若有所思的走出来,劲直向我走过来,嘴角微微上扬。产品大大:boss 对我们的研发成果挺满意的,balabala...(内心 OS:不听,讲重点)产品大大:咱们的客服 I...

wuwhs32阅读 3.5k评论 5

封面图
安全地在前后端之间传输数据 - 「3」真的安全吗?
在「2」注册和登录示例中,我们通过非对称加密算法实现了浏览器和 Web 服务器之间的安全传输。看起来一切都很美好,但是危险就在哪里,有些人发现了,有些人嗅到了,更多人却浑然不知。就像是给门上了把好锁,还...

边城29阅读 6.4k评论 5

封面图
2022大前端总结和2023就业分析
我在年前给掘金平台分享了《2022年热点技术盘点》的前端热点,算是系统性的梳理了一下我自己对前端一整年的总结。年后,在知乎上看到《前端的就业行情怎么样?》,下面都是各种唱衰前端的论调,什么裁员,外包化...

i5ting27阅读 2.3k评论 4

封面图
深入理解React Diff算法
fiber上的updateQueue经过React的一番计算之后,这个fiber已经有了新的状态,也就是state,对于类组件来说,state是在render函数里被使用的,既然已经得到了新的state,那么当务之急是执行一次render,得到持有新...

nero31阅读 11.8k评论 3

正在更新 Elixir 语言的系列文章:[链接]

31.1k 声望
3.1k 粉丝
宣传栏