很多时候我们需要创造一些同类型的对象,在之后可以统一地处理,

$$jsdemo$$
$$edit$$
let mingren = {
    name: "鸣人",
    score: 56,
}

let kenan = {
    name: "柯南",
    score: 98,
}

let students = [mingren, kenan]
for (let student of students) {
    alert(`${student.name}同学的分数是 ${student.score}`)
}

这种定义方法需要写很多重复的代码,并且属性名写错就会造成严重的灾难。

比如以下的代码拼错了一个单词就导致程序异常:

$$jsdemo$$
$$edit$$
let mingren = {
    name: "鸣人",
    score: 56,
}

let kenan = {
    name: "柯南",
    scroe: 98, // 这里拼错了,导致程序不正常
}

let students = [mingren, kenan]
for (let student of students) {
    alert(`${student.name}同学的分数是 ${student.score}`)
}

于是乎,我们想到了可以在通过函数统一地创建对象,如下:

$$jsdemo$$
$$edit$$
function createStudent(name, score) {
    return {
        name,
        score,
    }
}

let mingren = createStudent("鸣人", 56)
let kenan = createStudent("柯南", 98)

let students = [mingren, kenan]
for (let student of students) {
    alert(`${student.name}同学的分数是 ${student.score}`)
}

为了明显地区分出这种这种创造了一个对象的函数,便有了 new 运算符。

构造函数

当我们用 new 运算符调用一个函数时,这个函数就被称为一个构造函数。构造函数会创造一个对象,在构造函数中可以使用 this 访问被创造出来的对象,对象在函数执行完成后返回。

通过 new 运算符改造上面的代码:

$$jsdemo$$
$$edit$$
// 构造函数首字母习惯大写
function Student(name, score) {
    // 使用 new 运算符
    // this = {}

    this.name = name
    this.score = score

    // return this
}

let mingren = new Student("鸣人", 56)
let kenan = new Student("柯南", 98)

let students = [mingren, kenan]
for (let student of students) {
    alert(`${student.name}同学的分数是 ${student.score}`)
}

当一个函数被使用 new 操作符执行时,它按照以下步骤:

  1. 一个新的空对象被创建并分配给 this
  2. 函数体执行。通常它会修改 this,为其添加新的属性。
  3. 返回 this 的值。

$$tip

构造函数首字母习惯大写,这是一个约定俗成的习惯。

$$

构造函数的 return

通常构造函数不用写 return,它会默认返回创造的 this 对象。如果存在 return 语句的话:

  • 如果 return 返回了一个对象,则返回一个对象而不是 this
  • 如果 return 返回了一个原始类型,则忽略并依旧返回 this
$$jsdemo$$
$$edit$$
function Student(name, score) {
    this.name = name
    this.score = score
    return { cat: "加菲" }
}

let mingren = new Student("鸣人", 56)
console.log(mingren) // {cat: '加菲'},返回了创建的对象

function Student2(name, score) {
    this.name = name
    this.score = score
    return 123
}

let kenan = new Student2("柯南", 98)
console.log(kenan) // {name: '柯南', score: 98},忽略原始值依旧返回 this

实战:单例模式

依照以上特性,我们来完成一个编程设计模式中的单例模式

单例模式用于创建那些程序中仅允许出现一次的对象。

$$jsdemo$$
$$edit$$

// 单例模式

function SchoolMaster(name) {
    // 如果不存在则初始化
    if (window.$schoolMaster == undefined) {
        window.$schoolMaster = {
            name,
        }
    }

    return window.$schoolMaster
}

let schoolMaster = new SchoolMaster("邓布利多")
let schoolMaster2 = new SchoolMaster("王校长")

console.log(schoolMaster.name) // 邓布利多
console.log(schoolMaster2.name) // 邓布利多

练习

  1. 补全以下实例。
function People(fisrtname, lastname) {
    // 补全代码
}

const ailun = new People("艾伦", "耶格尔")
alert(ailun.getFullname()) // 艾伦·耶格尔

const bingzhang = new People("利威尔", "阿克曼")
alert(bingzhang.getFullname()) // 利威尔·阿克曼

$$answer

$$jsdemo$$
$$edit$$
function People(fisrtname, lastname) {
    this.getFullname = function () {
        return `${fisrtname}·${lastname}`
    }
}

const ailun = new People("艾伦", "耶格尔")
alert(ailun.getFullname()) // 艾伦·耶格尔

const bingzhang = new People("利威尔", "阿克曼")
alert(bingzhang.getFullname()) // 利威尔·阿克曼

$$

  1. 补全以下代码,创建两个独立的计数器。
function Counter() {
    // 补全代码
}

const conterA = new Counter()
const conterB = new Counter()

alert(`A: ${conterA.count()}`) // 1
alert(`A: ${conterA.count()}`) // 2

alert(`B: ${conterB.count()}`) // 1
alert(`B: ${conterB.count()}`) // 2

$$answer

$$jsdemo$$
$$edit$$
function Counter() {
    this.counter = 0
    this.count = () => ++this.counter
}

const conterA = new Counter()
const conterB = new Counter()

alert(`A: ${conterA.count()}`) // 1
alert(`A: ${conterA.count()}`) // 2

alert(`B: ${conterB.count()}`) // 1
alert(`B: ${conterB.count()}`) // 2

$$