构造函数 — 如此简单?

吐槽君 分类:javascript

1、构造函数

上一节中我们聊到了类,它是在ES6之后才出现的,在ES6之前,对象不是基于类创建的,而是一种称之为 构造函数 的特殊函数来定义对象和它们的特征

我们之前说过,创建对象有3种方法,分别是:

  1. 对象字面量
  2. new object()
  3. 构造函数

前两种是这样的:

// 对象字面量
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 我们就知道它会做四件事:

  1. 在内存中创建一个新的对象
  2. this 指向这个新对象
  3. 执行构造函数里面的代码,为这个对象添加属性和方法
  4. 最后返回新对象

这就是构造函数不需要 return 的原因

构造函数不一定要写成函数声明的形式,赋值给变量的函数表达式也是可以表示构造函数的

var Person = function() {}
// 实例对象
var p = new Person()
 

记住:构造函数就是一个函数,一个可以创建对象的函数

1.2 构造函数的成员

在构造函数中可以添加一些成员,我们分为实例成员静态成员

实例成员:在函数内部 this 创建的对象成员称为实例成员,只能由实例化的对象来访问

console.log(p.namer);  // 张三
console.log(p.age);  // 2
p.say();  // 张三2岁了
 
  • nameragesay就称为实例成员

静态成员:在构造函数本身添加的成员称为静态成员,只能由构造函数本身来访问

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岁了
 

p1p2 分别保存着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属性中包含只是一个指向构造函数外部的指针,而p1p2 共享了全局函数say()

总结:

  1. 构造函数就是一个对象,其作用就是用来创建对象
  2. 没有用new 实例化的构造函数就是一个普通函数
  3. 使用new 会执行4步 -- 创建对象修改this 指向执行代码return 函数(烂记于心)
  4. 每实例一个对象就要开辟一个内存空间,为避免内存浪费,可以把公共方法定义到构造函数外(全局对象上 或 构造函数的原型对象上)

回复

我来回复
  • 暂无回复内容