对象建立模式

每个对象都是基于引用类型建立的, 也能够是开发人员本身建立的数组

建立对象

字面量模式

  • 缺点:
    • 会产生大量重复代码, 使用变体 工厂模式
let Person = {
        name: 'agan',
        age: 30,
        job: 'it',
        sayName: function() {
            console.log(this.name)
        }
    }
复制代码

工厂模式

这种模式抽象了建立具体函数的过程, 用函数来封装以特定接口建立对象的细节函数

function createPerson(name, age, job) {
        let o = new Object()
        o.name = name
        o.age = age
        o.job = job
        o.sayName = function() {
            console.log(this.name)
        }
        return o
    }
    let p1 = createPerson('agan1', 30, 'it')
    let p2 = createPerson('agan2', 40, 'cs')
复制代码

函数 createPerson 接受三个参数来构建一个对象, 该函数能够屡次调用. 工厂模式虽然解决了建立多个类似对象的问题, 却没有解决对象识别的问题(即怎样知道一个对象的类型)ui

构造函数模式

构造函数能够用来建立特定类型的对象, 咱们能够建立自定义的构造函数, 从而能够定义自定义对象类型的属性和方法this

  • 任何函数均可以当成构造函数
  • 只要把一个函数经过new的方式来进行调用, 咱们就把这一次函数的调用方式称之为: 构造函数的调用
function Person(name, age, job) {
        this.name = name
        this.age = age
        this.job = job
        this.sayName = function() {
            console.log(this.name)
        }
    }
    let p1 = new Person('agan1', 30, 'it')
    let p2 = new Person('agan2', 40, 'cs')
复制代码

PersoncreatePerson 不一样:spa

  1. 没有显式建立对象
  2. 将属性与方法给了 this
  3. 没有 return 语句

p1p2 保存着 Person 的两个不一样实例, 但都有一个 constructor (构造函数) 属性, 指向 Personprototype

console(p1.constructor == Person); //true
    console(p2.constructor == Person); //true
复制代码

constructor 是用来表示对象类型的, instanceof 检测对象类型更靠谱指针

alert(person1 instanceof Object);  //true
    alert(person1 instanceof Person);  //true
    alert(person2 instanceof Object);  //true
    alert(person2 instanceof Person);  //true
复制代码
构造函数的执行过程

var p1=new Person();code

  • 一、建立一个对象 (咱们把这个对象称之为Person构造函数的实例)- _p1
  • 二、建立一个内部对象,this,将this指向该实例(_p1)
  • 三、执行函数内部的代码,其中,操做this的部分就是操做了该实例(_p1)
  • 四、返回值:
    • a、若是函数没有返回值(没有return语句),那么就会返回构造函数的实例(p1)对象

    • b、若是函数返回了一个基本数据类型的值,那么本次构造函数的返回值是该实例(_p1)接口

      function fn(){}
          var f1 = new fn();    //f1就是fn的实例
          function fn2(){
              return "abc";
          }
          var f2 = new fn2();   //f2是fn2构造函数的实例
      复制代码
    • c、若是函数返回了一个复杂数据类型的值, 那么本次函数的返回值就是该值

      function fn3(){
              return [1,3,5]; 
              //数组是一个对象类型的值,
              //因此数组是一个复杂数据类型的值
              //-->本次构造函数的真正返回值就是该数组
              //-->再也不是fn3构造函数的实例
          }
          var f3 = new fn3();   //f3仍是fn3的实例吗?错
          //f3值为[1,3,5]
      复制代码
将构造函数当函数
  • 任何函数均可以是构造函数, 必须使用 new 调用
  • 任何函数只要不经过 new 调用就是普通函数
// 看成构造函数
    let person = new Person("agan", 29, "se");
    person.sayName() // 'agan'
    
    // 做为普通函数调用
    Person("agan1", 27, "IT"); // 添加到 window
    window.sayName(); // "agan1"
    
    // 在另外一个对象的做用域中调用
    var o = new Object();
    Person.call(o, "agan3", 25, "Nurse")
    o.sayName(); // "agan3"
复制代码
构造函数问题

就是每一个方法都要在每一个实例上从新建立一遍, sayName 指向不一样的地址

person1.sayName === person2.sayName // true
复制代码

能够将 sayName 方法转移到构造函数外部解决这个问题

function Person(name, age, job) {
        this.name = name
        this.age = age
        this.job = job
        this.sayName = sayName
    }
    function sayName() {
        console.log(this.name)
    }
    let person1 = new Person("agan1", 29, "se");
    let person2 = new Person("agan2", 27, "it");
    
    person1.sayName === person2.sayName  // true
复制代码

上面代码中将 sayName 函数转移到了构造函数的外部, 在函数内部咱们将 sayName 指向这个全局函数, 这样就解决了多个函数作一件事情的问题, 但又带来了两个新的问题:

  • 全局做用域的函数只能被某个对象调用
  • 若是对象须要多个方法那么就须要定义多个全局函数

原型模式

咱们建立的每个函数都有一个 prototype 属性, 这是一个指针, 指向了一个对象, 这个对象包含了特定类型对象的全部公共属性和方法, 至关于一个父类

function Person() {
    }
    
    Person.prototype.name = 'agan'
    Person.prototype.age = '30'
    Person.prototype.job = 'it'
    Person.prototype.sayName = function() {
        console.log(this.name)
    }
    
    let p1 = new Person()
    let p2 = new Person()
    
    p1.sayName() // 'agan'
    p2.sayName() // 'agan'

    console.log(p1.sayName === p2.sayName) // true
复制代码

上述代码中 person1person2 访问的是同一组对象和方法 假如咱们想在全部的实例中共享一个数据, 那么咱们可使用原型模式, 但是通常实例都有本身的属性

组合使用构造函数与原型模式

构造函数用于建立实例属性, 原型模式用于定义方法和共享的属性

function Person(name, age, job) {
        this.name = name
        this.age = age
        this.job = job
        this.friends = ['a', 'b']
    }
    
    Person.prototype = {
        constructor: Person,
        sayName: function() {
            console.log(this.name)
        }
    }
    
    var p1 = new Person('agan', 29, 'it')
    var p2 = new Person('agan2', 30, 'cs')
    p1.friends.push('c')
    console.log(p1.friends) // 'a' 'b' 'c'
    console.log(p2.friends) // 'a' 'b'
    console.log(p1.friends === p2.friends) // false
    console.log(p1.sayName === p2.sayName) // true
复制代码

在上面的代码中, 实例属性是定义在构造函数中的, 实例共享的属性和方法是在原型中定义的

动态原型模式

动态原型模式将全部信息都封装在了构造函数中, 在构造函数中初始化原型

function Person(name, age, job) {
        this.name = name
        this.age = age
        this.job = job
        
        // 方法
        if (typeof this.sayName !== 'function') {
            Person.prototype.sayName = function() {
                console.log(this.name)
            }
        }
    }
    let f = new Person('agan', 30, 'it')
    f.sayName()
复制代码

上述代码中, sayName方法只有在不存在的状况下, 才会添加到原型中, 这段代码只会在初次调用构造函数时才会执行

寄生构造函数模式

基本思想: 建立一个函数, 该函数用来封装建立对象的代码, 而后再返回建立的对象 跟工厂模式相似

function Person(name, age, job) {
        let o = new Object()
        o.name = name
        o.age = age
        o.job = job
        o.sayName = function() {
            console.log(this.name)
        }
        return o
    }
    let f = new Person('agan', 20, 'it')
    f.sayName() // 'agan'
复制代码
  • 说明:
    • 返回的对象与构造函数或者与构造函数的原型属 性之间没有关系
    • 构造函数返回的对象与在构造函数外部建立的对象没有什么不一样
    • 不能依赖 instanceof 操做符来肯定对象类型

稳妥构造函数

稳妥对象: 指的是没有公共属性, 其方法也不引用 this 对象

  • 稳妥构造函数与寄生构造函数不一样点:
    • 函数内部新建立对象的实例方法不引用 this
    • 函数外部不使用 new 操做符调用构造函数
    • 函数内部中变量不能挂在到要返回的变量上
function Person(name, age, job) {
        let o = new Object()
        // 定义私有变量和函数
        let name = name
        let age = age
        let job = job
        
        // 添加方法
        o.sayName = function() {
            console.log(name)
        }
        return o
    }
复制代码

这种模式中建立的对象, 除了使用 sayName 方法外没有其余方法访问name的值

let f = Person('agan', 30, 'it')
    f.sayName() // agan
复制代码
相关文章
相关标签/搜索