构造函数 — 如此简单?
1、构造函数
上一节中我们聊到了类,它是在ES6之后才出现的,在ES6之前,对象不是基于类创建的,而是一种称之为 构造函数 的特殊函数来定义对象和它们的特征
我们之前说过,创建对象有3种方法,分别是:
- 对象字面量
new object()
- 构造函数
前两种是这样的:
// 对象字面量
var obj = {}
// 利用 new object()
var obj = new object()
下面我们来详细说一下构造函数
1.1 初识构造函数
构造函数:是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值
我们可以把对象中一些共用的属性和方法抽取出来,封装到这个函数里面
来创建一个构造函数:
function Person(namer, age) {
this.namer = namer
this.age = age;
this.say = function() {
console.log(this.namer + this.age + '岁了');
}
}
- 构造函数的首字母都是要大写的(虽然可以不用,但是为了与普通函数作区别,墙裂建议大写)
实例化构造函数:
var p = new Person('张三', 2)
p.say()
创建Person
构造函数的实例,需要用到 new
关键字,是的,又和new
见面了
看到 new
我们就知道它会做四件事:
- 在内存中创建一个新的对象
- 让
this
指向这个新对象 - 执行构造函数里面的代码,为这个对象添加属性和方法
- 最后返回新对象
这就是构造函数不需要 return
的原因
构造函数不一定要写成函数声明的形式,赋值给变量的函数表达式也是可以表示构造函数的
var Person = function() {}
// 实例对象
var p = new Person()
记住:构造函数就是一个函数,一个可以创建对象的函数
1.2 构造函数的成员
在构造函数中可以添加一些成员,我们分为实例成员 和 静态成员
实例成员:在函数内部 this
创建的对象成员称为实例成员,只能由实例化的对象来访问
console.log(p.namer); // 张三
console.log(p.age); // 2
p.say(); // 张三2岁了
namer
、age
、say
就称为实例成员
静态成员:在构造函数本身添加的成员称为静态成员,只能由构造函数本身来访问
Person.sex = '李四'
console.log(Person.sex); // 李四
1.3 “没有”new
的构造函数
构造函数和普通函数的唯一区别就是调用方式不同,任何函数只要使用new
操作符调用就是构造函数,不使用new
就是普通函数
所以,没有使用new
调用的构造函数就是一个没有灵魂的构造函数,好吧,不仅没有灵魂,还没有构造,只剩函数
// 作为构造函数
var p = new Person('张三', 2)
p.say(); // 张三2岁了
// 作为普通函数
Person('张三', 2)
console.log(window.say()); // 张三2岁了
如果在函数里面加个return
,就真的是一个普普通通的函数了
function Person(namer, age) {
this.namer = namer
this.age = age;
this.say = function() {
console.log(this.namer + this.age + '岁了');
}
return this.say()
}
Person('张三', 2); // 张三2岁了
没有了new
的加持,Person
落魄到既要自己return
函数,this
又是指向全局window
1.4 构造函数的问题
构造函数很好用,但是也存在浪费内存问题,因为每创建一个对象,构造函数就会创建开辟一个新的内存空间
实例两个对象
var p1 = Person('张三', 2); // 张三2岁了
var p2 = Person('李四', 3); // 张三2岁了
p1
和 p2
分别保存着Person
的不同实例,而且这两个对象都有一个constructor
属性指向 Person
console.log(p1.constructor === Person); // true
console.log(p2.constructor === Person); // true
但是这个两个不同实例的函数同名却不相等
console.log(p1.say === p2.say); // false
这是因为他们的内存空间不同
但是因为它们做的事相同,都是调用函数,没必要浪费内存空间,所以有什么解决办法吗?
解决办法:把函数转移到构造函数外
function Person(namer, age) {
this.namer = namer
this.age = age;
this.say = say
}
function say() {
console.log(this.namer + this.age + '岁了');
}
var p1 = new Person('张三', 2); // 张三2岁了
var p2 = new Person('李四', 3); // 张三2岁了
console.log(p1.say === p2.say); // true
这样,say
属性就等于全局函数say()
,say
属性中包含只是一个指向构造函数外部的指针,而p1
和 p2
共享了全局函数say()
总结:
- 构造函数就是一个对象,其作用就是用来创建对象
- 没有用
new
实例化的构造函数就是一个普通函数 - 使用
new
会执行4步 -- 创建对象、修改this
指向、执行代码、return
函数(烂记于心) - 每实例一个对象就要开辟一个内存空间,为避免内存浪费,可以把公共方法定义到构造函数外(全局对象上 或 构造函数的原型对象上)