编辑代码

// 1,对象字面量创建
// 创建相似的对象需要不断的重复书写
let person2 = {
    name: 'Tom',
    age: 24,
    sayName: function() {
        console.log(this.name)
    }
}
console.log(person2) // { name: 'Tom', age: 24, sayName: [Function: sayName] }

// 2,工厂模式
// 解决了重复创建多个相似对象的问题,但是没有解决对象识别问题
function createPerson(name, age) {
    let o = new Object()
    o.name = name
    o.age = age
    o.sayName = function() {
        console.log(this.name)
    }
    return o
}
let person3 = createPerson('Tom', 24)
console.log(person3 instanceof Object) // true
console.log(person3 instanceof createPerson) // false
console.log(person3) // { name: 'Tom', age: 24, sayName: [Function (anonymous)] }

// 3,构造函数创建
// 没有显示的创建对象,直接将属性赋值给this,没有return语句
// 构造函数首字母大写
// 利用构造函数创建的对象,具有清晰的类型
// 构造函数与普通函数的唯一区别就是 调用的方式不一样
// 缺点:每个方法需要在每个实例上重新创建一次,可以利用原型模式处理
function Person(name, age) {
    this.name = name
    this.age = age
    this.sayName = function() {
        console.log(this.name)
    }
}
let person1 = new Person('Tom', 24)
console.log(person1 instanceof Object) // true
console.log(person1 instanceof Person) // true
console.log(person1) // Person { name: 'Tom', age: 24, sayName: [Function (anonymous)] }

// 4,原型模式
// prototype 是通过构造函数而创建的对象实例的原型对象
// 使用 prototype 原型的好处就是让所有的对象实例共享它包含的属性和方法
// 理解 prototype: 只要创建了一个新函数,就会根据一个特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象
// 原型模式的缺点: 1,省略了构造函数初始传参。2,原型使得其都是共享,引发了问题。
function Person4() {}
Person4.prototype.name = 'Tom'
Person4.prototype.age = 24
Person4.prototype.sayName = function() {
    console.log(this.name)
}

let person4 = new Person4()
let _person4 = new Person4()

console.log(person4 === _person4) // false
person4.name = 'Nico'
console.log(person4, _person4, person4.name, _person4.name) // Person4 { name: 'Nico' } Person4 {} Nico Tom

// JS内置对象的原型不能整体覆盖,只能单独地动态地为原型添加属性或方法
function Person41() {}
Person41.prototype = { // 对象字面量 本质上重写了 prototype 属性,因此 constructor 属性变成了新对象的 constructor 属性
    name: 'Tom',
    age: 24,
    sayName: function() {
        console.log(this.name)
    }
}

let person41 = new Person41()
console.log(person41 instanceof Person41) // true
console.log(person41 instanceof Object) // true
console.log(person41.constructor == Person41) // false
console.log(person41.constructor == Object) // true

function Person42() {}
Person42.prototype = {
    constructor: Person42, // 可以单独设置,会导致起变成可枚举属性
    name: 'Tom',
    age: 24,
    syaName: function() {
        console.log(this.name)
    }
}

// 5,组合使用构造函数模式和原型模式
// 这是最常用的方式,构造函数定义实例属性,原型定义共享方法
function Person5(name, age) {
    this.name = name;
    this.age = age
}

Person5.prototype = {
    constructor: Person5,
    sayName: function() {
        console.log(this.name)
    }
}

// 6,动态原型模式
// 将原型模式和构造函数模式放在一个构造函数中实现

function Person6(name, age) {
    this.name = name
    this.age = age
    if (this.sayName != 'function') { // 只需要做一个的判断即可
        // 只有在初始化的时候才会执行
        // 不能使用对象字面量形式的写法,因为这样会使得现有实例与新原型切断联系
        person.prototype.sayName = function() {
            console.log(this.name)
        }
        // ... 其他原型上的共享方法
    }
}

// 7,寄生构造函数模式
// 返回的对象与构造函数或者构造函数的原型没有一丝关系,因此这儿不能用 instanceof 
// 这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改 Array 构造函数,因此可以使用这个模式。
function Person7(name, age) {
    let o = new Object()
    o.name = name
    o.age = age
    o.sayName = function() {
        console.log(this.name)
    }
    return o
}

let person7 = new Person7('Tom', 18)
console.log(person7)
console.log(person7 instanceof Person7) //false

// 8,稳妥构造函数模式
// 意思就是传入的初始值,只有通过特定的方法读取,不能通过属性访问等访问。比较安全
// 同样的 返回的对象和构造函数没有关系 不能使用 instanceof 判断类型

function Person8(name, age) {
    var o = new Object()
    o.sayName = function() {
        console.log(name)
    }
    return o
}

let person8 = new Person8('Nio', 29)
person8.sayName()