一篇认清构造函数

120 阅读3分钟

这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。

ECMAScript 中的构造函数是用于创建特定类型对象的

使用规范

按照惯例,构造函数名称的首字母都是要大写的, 非构造函数则以小写字母开头。这是从面向对象编程语言那里借鉴的,有助于在 ECMAScript 中区分构 造函数和普通函数。

  • 首字母大写 首字母大写不是必须的,它只是约定的习惯,但应该这么做。这有助于区分构造函数还是普通函数
  • 使用new运算符调用 这是构造函数与普通函数的唯一区别
function Person() { 
    this.like = '吃喝玩乐';
    console.log('Person', this)
}
Person(); //作为普通函数使用 
let instanceObj = new Person(); // 做构造函数使用
console.log(instanceObj)

image.png

解释一下上面打印结果,第一行是普通调用函数打印的其内部this值,因为是在全局环境调用的,所以为window
第二行是构造函数被new运算符调用输出的结果,this指向这个创建的实例对象;
第三行打印的就是通过构造函数创建的实例对象了。

注:如果创建实例不需要传参,后面的小括号可加可不加,如下

function Person() { 
    this.like = '吃喝玩乐';
    console.log('Person', this)
}
let obj1 = new Person();
let obj2 = new Person;

上面例子结果是一样的,只要有 new 操作符,就可以调用相应的构造函数。 image.png

new一个函数会发生什么?

  1. 在内存中创建一个新对象。
  2. 这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性。 [[Prototype]]联系着这个对象实例与构造函数的原型对象(而非构造函数)
    构造函数的prototype == 指向原型对象的指针\
  3. 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。\
  4. 执行构造函数内部的代码(给新对象添加属性)。\
  5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
function Person() { 
    return {
        name: 'new一个对象'
    }
}
let obj = new Person()

上面例子中,返回了一个对象,obj就是函数内部返回的这个对象。

使用构造函数创建对象的优缺点

  • 解决创建多个类似对象的问题 避免创建具有同样接口的多个对象需要重复编写很多代码。

  • 解决对象标识问题 新创建的对象是什么类型?
    基于这个构造函数创建的对象,其类型就是这个函数,当然每个对象也都是Object的实例。

function Person() { 
    this.name = 'new一个对象'
}
let obj = new Person()
console.log(obj.constructor == Person); // true
console.log(obj instanceof Person); // true
console.log(obj instanceof Object); // true

问题:定义的方法会在每个实例上 都创建一遍。
对象不止有属性,也会有方法(函数作为某对象的属性值)

// 用构造函数,创建通用方法
function Person() { 
    this.name = 'new一个对象';
    this.sayName = function(){
        console.log('一个叫sayName的方法')
    }
}
let person1 = new Person()
let person2 = new Person()
console.log(person1.name == person2.name) // true
console.log(person1.sayName == person2.sayName) // false

首先我们知道,js中函数也是个对象,每次通过new创建实例对象,都会创建一个sayName的函数,它们只是名字相同,但不相等。
每创建一个Person实例,sayName就会被重复创建一次;但是sayName内部实现都是一样的,这样每次都要创建一个对象显然不是很好。

解决1:
将sayName提取到构造函数外部

function sayName(){
    console.log('一个叫sayName的方法')
}
function Person() { 
    this.name = 'new一个对象';
    this.sayName = sayName;
}
let person1 = new Person()
let person2 = new Person()
console.log(person1.sayName == person2.sayName) // true

解决2:
通过函数原型的方式,函数都有prototype属性(箭头函数除外),将sayName定义在prototype中,所有基于这个函数创建的实例对象都会继承其prototype属性和方法

function Person() { 
    this.name = 'new一个对象';
}
Person.prototype.sayName = function(){
    console.log('一个叫sayName的方法')
}
let person1 = new Person()
let person2 = new Person()
console.log(person1.sayName == person2.sayName) // true
console.log(person1)

image.png