js原型与继承

js原型与继承

1 原型的初步认识

1.1 代码体验

  • 原型可以理解为父一级 proto
    /**
     * 普通对象
    */
    let obj = {};
    console.log(obj)		//__proto__: Object

1.4 原型方法与对象方法优先级

  • 对象中自己有的用自己的,没有再找原型
    /**
     * 原型方法与对象方法相比较
    */
    let hi = {
      show() {
        console.log('对象方法')
      }
    }
    console.log(hi)

    hi.__proto__.show = function() {
      console.log('原型方法')
    }
    hi.show() //对象方法

1.5 函数有多个长辈

  • 函数可以当对象使用

    • 通过__proto__访问对象中的属性和方法
  • 也可以当构造函数使用

    • 通过prototype能够定义实例对象的属性与方法

在这里插入图片描述

1.7 系统构造函数的原型体现

  
  let arr = []
  console.log(arr.__proto__ === Array.prototype)    

  let obj = {}
  console.log(obj.__proto__ === Object.prototype)

  let str = ''
  console.log(str.__proto__ === String.prototype)

  let bool = true
  console.log((bool.__proto__ === Boolean.prototype))
  
  let reg = /a/g
  console.log(reg.__proto__ === RegExp.prototype)
  

1.8 自定义对象的原型设置


  let obj = {
    name: 'hi'
  }
  let parent = {
    name: 'parent'
  }

  // 获取原型
  console.log(Object.getPrototypeOf(obj) === Object.prototype) //true
  // 设置原型
  Object.setPrototypeOf(obj,parent)
  console.log(Object.getPrototypeOf(obj) === parent) //true

1.9 原型中constructor引用

  • __proto__与prototype区别
    • _proto_ 服务自己
    • prototype 通过构造函数new出来无数个对象时使用这个,这个功能比较强大

  function User(name) {
    this.name = name
  }
  // prototype原型中的constructor指向构造函数
  console.log(User.prototype.constructor === User) // true

  let zs = new User('张三')
  let ls = new User.prototype.constructor('李四')
  
  console.dir(zs.name)
  console.dir(ls.name)

**************************************************************
    
	function User(name) {
      this.name = name
      age: 22
    }
    // 定义多个方法时,会覆盖,要加上constructor,这个对象要放在实例化对象之前,否则会报错
    User.prototype = {
      constructor: User,
      showName() {
        console.log(this.name)
      },
      showAge() {
        console.log(this.age)
      }
    }
    // prototype原型中的constructor指向构造函数
    // console.log(User.prototype.constructor === User) // true

    let zs = new User('张三')

    let ls = new User.prototype.constructor('李四')

    // console.dir(zs.name)
    // console.dir(ls.name)
    // 定义单个方法
    User.prototype.sayHi = function () {
      console.log('hi ' + this.name)
    }


    console.dir(User)
    zs.sayHi()
    ls.sayHi()

    zs.show()
  

1.10 给我一个对象,还你一个世界(根据对象创建对象)


    // 根据对象创建对象
    function User(name) {
      this.name = name
    }

    let zs = new User("张三")
    console.log(zs)

    function createByObject(obj,...args){
      // 通过已知的对象找到该对象的原型,通过原型找到构造函数
      const constructor = Object.getPrototypeOf(obj).constructor
      return new constructor(...args)
    }
    
    let ls = createByObject(zs,'李四')
    console.log(ls)

1.11 原型链总结

  • prototype 原型是一个对象,相应的:任何一个对象都可以成为其他对象的原型
  • 在定义方法时,将共用的放在父级原型中,实现子级的继承

在这里插入图片描述

    // 原型链检测 instanceof
    function A() { }
    function B() { }
    function C() {}
    
    
    let c = new C()
    B.prototype = c
    let b = new B()
    A.prototype = b
    let a = new A()
    
    // 构造函数A 是否在 对象a 的原型链上
    console.log(a instanceof A)
    // 构造函数B 是否在 对象b 的原型链上
    console.log(a instanceof B)
    // 构造函数A 是否在 对象c 的原型链上 
    console.log(a instanceof C)

1.12 原型链检测

    // 原型链检测 instanceof
    function A() { }
    function B() { }
    function C() {}
    
    
    let c = new C()
    B.prototype = c
    let b = new B()
    A.prototype = b
    let a = new A()
    
    // 构造函数A 是否在 对象a 的原型链上
    console.log(a instanceof A)
    // 构造函数B 是否在 对象b 的原型链上
    console.log(a instanceof B)
    // 构造函数A 是否在 对象c 的原型链上 
    console.log(a instanceof C)

		// 原型链检测 isPrototypeOf 检测
    // 构造函数C的原型对象 是否在 对象 a 的原型链
    console.log(C.prototype.isPrototypeOf(a))
		// 原型对象c 是否在 对象 a 的原型链
    console.log(c.isPrototypeOf(a))
    console.log(B.prototype.isPrototypeOf(a))
    console.log(C.prototype.isPrototypeOf(a))
    console.log(A.prototype.isPrototypeOf(c))

1.14 in与hasOwnProperty属性检测差异

  • in 在检测属性时,不仅检测自身的属性,还检测原型链上的属性
  • hasOwnProperty只检测自身的属性
  • obj.hasOwnProperty(prop)
    let a = {
      name: 'tom'
    }
    let b = {
      age: 22
    }

    Object.setPrototypeOf(a,b)

    console.log('name' in a)    //true
    console.log('age' in a)     //true

    console.log(a.hasOwnProperty('name')) //true
    console.log(a.hasOwnProperty('age'))  //false

    // 遍历对象,只遍历自身属性 结果:tom
    for (const key in a) {
      if (a.hasOwnProperty(key)) {
        const element = a[key];
        console.log(element)
      }
    }
    // 遍历原型链,结果 :tom 22
    for(const key in a) {
      console.log(a[key])
    }

1.15 使用call、apply借用原型链

		/**
     * obj 原型中有取得最大值的方法,
     * 对象hi及其原型中没有这种方法,
     * 现在让hi通过call或apply借用obj原型中的max()方法
    */
    let obj = {
      arr: [1, 141, 25, 6356, 1231]
    }
    // obj.__proto__ = {
    //   max() {
    //     return this.arr.sort((a, b) => b - a)[0]
    //   }
    // }
    // 这两种方式等价
    Object.setPrototypeOf(obj, {
      // 取得最大值
      max() {
        return this.arr.sort((a, b) => b - a)[0]
      }
    })
    console.log(obj)
    console.log(obj.max())

    let hi = {
      lessons: { js: 87, php: 63, node: 99, linux: 88 },
      get arr() {
        return Object.values(this.lessons)
      }
    }

    console.log(obj.max.apply(hi)) 
    // Math 中有max() , 让对象hi使用该方法
    let max = Math.max(...[1, 141, 25, 6356, 1231])
    console.log(max)

    let hi = {
      lessons: { js: 87, php: 63, node: 99, linux: 88 },
      get arr() {
        return Object.values(this.lessons)
      }
    }
    console.log(hi)
    console.log(hi.arr)
    let himax = Math.max.call(...(hi.arr))
    let himax2 = Math.max.apply(null,hi.arr)
    console.log(himax)  // 99
    console.log(himax2) // 99

1.17 DOM节点借用Array原型方法

		<button class="btn">按钮1</button>
  	<button>按钮2</button>
  	<button>按钮3</button>
		/**
     * DOM节点借用Array原型方法
    */
    let arr = [14,66,99,31,6]
    let res = arr.filter(item => {
      return item > 30
    })
    console.log(res)    // [66, 99, 31]

    // filter 在哪?
    console.dir(Array)  // 没有
    console.log(Array.prototype)  //有filter
    console.dir(arr.__proto__)    //有
    console.dir([].__proto__)     //有

    // let btns = document.querySelectorAll('button')  // 伪数组
    // console.dir(btns)
    // // 过滤btns找到带class的按钮
    // let btnHasClass = btns.filter(item => {
    //   return item.hasAttribute('class')
    // })
    // console.dir(btnHasClass)  //报错: btns.filter is not a function

    // 由于数组中有这个方法,可以借用数组来完成目的
    // let btns = document.querySelectorAll('button')
    // let btnHasClass = Array.prototype.filter.call(btns,item => {
    //   return item.hasAttribute('class')
    // })
    // console.dir(btnHasClass)  // 借用成功  0: button.btn

    let btns = document.querySelectorAll('button')
    let btnHasClass = [].filter.call(btns,item => {
      return item.hasAttribute('class')
    })
    console.dir(btnHasClass)  // 借用成功  0: button.btn

1.18 合理声明构造函数

/**
     * 第一种方式: 属性、方法都写在构造函数中
     *    缺点:每一个实例对象都有构造函数定义的属性和方法,由于方法是相同的,就造成不必要的内存资源浪费
     
    */
    function Person(name) {
      this.name = name
      this.show = function() {
        console.log(this.name)
      }
    }

    let zs = new Person('张三')
    let ls = new Person('李四')
    console.dir(zs)
    console.dir(ls)

在这里插入图片描述

 		/**
     * 解决方式: 将属性定义在构造函数中,将方法定义在构造函数原型当中,
     * 根据:实例化对象和构造函数共用一个原型
    */
    function Person(name) {
      this.name = name
    }
    Person.prototype.show = function() {
      console.log(this.name)
    }
    let zs = new Person('张三')
    let ls = new Person('李四')
    console.dir(zs)
    console.dir(ls)
    zs.show() //张三
    ls.show() //李四

在这里插入图片描述

1.20 不要滥用原型

<body>
  <!-- 假设要实现点击按钮隐藏按钮的功能,
    发现:没有这个函数,
    报错:Uncaught TypeError: this.hide is not a function
    想法:由于每个节点都是一个对象,在Object构造函数原型上创建这个方法
  -->
  <button class="btn" onclick="this.hide()">按钮1</button>
  <button>按钮2</button>
  <button>按钮3</button>
  <script>
    // 在没有第三方包引入情况下能够完成,
    Object.prototype.hide = function() {
      this.style.display = 'none'
    }
    console.dir(Object)
  </script>
  <!-- 引入第三方包,你发现完成的不是你的功能
    这里第三方包hide方法完成的是其他功能
  -->
  <script src="./prototype.js"></script>
  
  
  /**********************************************
  
// 第三方库 prototype.js
/**
 * 由于第三方库中也有这个方法,就造成功能不稳定的情况
 * 刚开始,可能能够运行,
 * 由于项目当中会引入很多包,这些包与你定义的那些方法会产生耦合,会造成一些未知的情况出现
 * 这会导致你的代码不健壮、不稳定
 */
Object.prototype.hide = function() {
  this.style.background = 'red'
}

1.21 关于原型创建与获取

/**
     * 最开始,js设置原型通过Object.create
     *  缺点:只能设置不能获取
     *  解决: 浏览器厂商自己研发 __proto__ 
    */
    // function Person(name) {
    //   this.name = name
    // }
    let user = {
      name: 'hi518',
      show() {
        console.log(this.name)
      }
    }
    // 只能设置原型,不能获取
    let zs = Object.create(user, {
      name: {
        value: '张三'
      }
    })
    console.log(zs)
    zs.show()

    // __prototype__,能设置、能获取,但是非标准
    let ls = { name: '李四' }
    // 设置原型
    ls.__proto__ = user
    console.log(ls)
    // 获取原型
    console.log(ls.__proto__) //{name: "hi518", show: ƒ}

    // setPrototypeOf与getPrototypeOf
    let ww = { name: '王五' }
    // 设置原型
    Object.setPrototypeOf(ww, user)
    console.log(ww)
    // 获取原型
    let pro = Object.getPrototypeOf(ww)
    console.log(pro)  //{name: "hi518", show: ƒ}

1.23 __proto__原来是属性访问器

  • _proto_ 这个属性并不是严格意义的属性,它是一个getter或setter,会对测试的值进行判断的
    /**
     * __proto__ 是什么?
     *    __proto__这个属性并不是严格意义的属性,
     *    它的本质是getter或setter,
     *    并且他会对测试的值进行判断,判断是不是对象,是对象,能设置,否则不能设置
     * __proto__怎么才能设置它为属性?
     *    __proto__是通过原型才有的,我们只需要把对象的原型设为null,就可以设置__proto__   
    */

    
    // let hd = { name: '后盾'}
    // hd.__proto__ = {
    //   show() {
    //     console.log(this.name)
    //   }
    // }
    // hd.__proto__ = 99
    // hd.show()   //后盾 发现hd.__proto__ = 99并没有真正的修改__proto__

    // __proto__原理
    let hd = {
      action: {},
      get proto() {
        return this.action
      },
      set proto(obj) {
        if(obj instanceof Object) {
          this.action = obj
        }
      }
    }

    hd.proto = {name:'eee'}
    hd.proto = '123'
    
    console.dir(hd.proto)   // 在设置原型时只有是对象的才能设置成功


    // let user = { name: 'user'}
    // user.__proto__ = null

    // let zs = { name: '张三' }
    // zs.__proto__ = null
    // zs.__proto__ = '123'
    // console.log(zs.__proto__)   // 123

    // let ww = Object.create(null)
    // ww.__proto__ = '111'
    // console.log(ww.__proto__)   // 111

2.继承

2.1 改变构造函数原型并不是继承

2.2 继承是原型的继承

  • 第一种继承方式,改变父级

在这里插入图片描述

  • 第二种继承方式,新增对象,新增的对象继承父级,改变构造函数的原型为新增对象

在这里插入图片描述

 /**
     * 改变构造函数的prototype并不是继承 
     *    改变构造函数原型之后,再在原型上添加方法,会添加到改变后的原型上
    */

    function User() {}
    User.prototype.name = function() {
      console.log('user.name')
    }

    /**
     * 第一种方式
     *    改变原型的__proto__
    */
    function Admin() {}
    Admin.prototype.__proto__ = User.prototype
    Admin.prototype.role = function() {
      console.log('admin.role')
    }
    let hd = new Admin()
    hd.role()       // admin.role

    /**
     * 第二种方式
     *    新增一个对象
     * 相当于新建一个对象,这个对象的原型指向User.prototype,
     * 这种方法改变了Admin.prototype,因此在此之前Admin.prototye中定义的方法会消失
     *    注意:这种继承方式要在改变Admin.prototype定义方法,否则会报错
    */
    // function Admin() {}
    // Admin.prototype = Object.create(User.prototype)
    // // 必须改变原型之后定义方法
    // Admin.prototype.role = function() {
    //   console.log('admin.role')
    // }
    // let hd = new Admin()
    // hd.role()       // admin.role

    function Member() {}
    Member.prototype = User.prototype
    Member.prototype.role = function() {
      console.log('member.role')
    }

    
  • 使用第二种方式注意点
    • 改变原型之后才能实例化对象或者添加方法

在这里插入图片描述

2.3 继承对constructor的影响

  • 使用第二种方式完成的继承会丢失constructor
    • 因此要加上 Admin.prototype.constuctor = Admin
  • 加上的constructor是可遍历的
    • 修改constructor,使它不可遍历
		function User() {}
    User.prototype.name = function() {
      console.log('user.name')
    }
    
    /**
     * 第二种方式
     *    新增一个对象
     * 相当于新建一个对象,这个对象的原型指向User.prototype,
     * 这种方法改变了Admin.prototype,因此在此之前Admin.prototye中定义的方法会消失
     *    注意:这种继承方式要在改变Admin.prototype定义方法,否则会报错
    */
    function Admin() {}
    Admin.prototype = Object.create(User.prototype)
    /**
     * 问题1:Admin.prototype少了constructor ?
     *      解决:添加constructor
    */
    Admin.prototype.constructor = Admin
    // 
    /**
     * 问题2:新增的constructor是可遍历的 ?
     *      解决:设置属性constructor
    */
    let a = Object.getOwnPropertyDescriptor(Admin.prototype,'constructor')
    console.dir(a)    //enumerable: true
    // 修改constructor属性,使它不可遍历
    Object.defineProperty(Admin.prototype,'constructor',{
      value: Admin,
      enumerable: false
    })
    // 必须改变原型之后定义方法和实例化对象
    Admin.prototype.role = function() {
      console.log('admin.role')
    }
    console.dir(Admin)      
    let hd = new Admin()
    hd.role()       // admin.role
    // 设置后遍历中不再出现constructor
    for (const key in Admin.prototype) {
      console.log(key)      // role name
    }

2.4 方法重写与父级属性访问

  • 父类中没有要用的方法
    • 在自己原型中定义要用的方法
  • 在自己原型中定义的方法当中,可以调用父类的方法
    function User() {}
    User.prototype.name = function() {
      console.log('user.name')
    }
    User.prototype.site = function() {
      return 'hello'
    }
    
    function Admin() {}
    Admin.prototype = Object.create(User.prototype)
    Admin.prototype.constructor = Admin
    Object.defineProperty(Admin.prototype,'constructor',{
      value: Admin,
      enumerable: false
    })

    // 自己的方法
    Admin.prototype.name = function() {
      console.log('admin.name')
    }
    Admin.prototype.show = function() {
      // 在自己的方法中引用父类的方法
      console.log(User.prototype.site() +' '+ 'admin')
    }

    // 实例化对象
    let hd = new Admin()
    hd.name()             //admin.name
    hd.show()             //hello admin

2.5 面向对象的多态实现

  • 多态: 多个对象的原型相同,在原型当中调用对象的方法,会有不同的展现形式
		function User() {}
    User.prototype.show = function() {
      console.log(this.description())
    }

    function Admin() {}
    Admin.prototype = Object.create(User.prototype)
    Admin.prototype.description = function() {
      return '管理员在此'
    }
    
    function Member() {}
    Member.prototype = Object.create(User.prototype)
    Member.prototype.description = function() {
      return '我是会员'
    }

    function Enterprise() {}
    Enterprise.prototype = Object.create(User.prototype)
    Enterprise.prototype.description = function() {
      return '企业用户'
    }

    for (const obj of [new Admin(),new Member(),new Enterprise()]) {
      obj.show()		//管理员在此 我是会员 企业用户
    }

2.6 使用父类构造函数初始属性

  • 在构造函数中通过call或apply方法调用父类方法
    function User(name, age) {
      this.name = name
      this.age = age
    }

    function Admin(name, age) {
      User.call(this, name, age)
    }
    let hd = new Admin('张三', 18)
    console.log(hd)

/**********************************************************/

    function User(name, age) {
      this.name = name
      this.age = age
    }

    function Admin(...args) {
      User.apply(this, args)
    }
    let hd = new Admin('张三', 18)
    console.log(hd)

2.7 使用原型工厂封装继承

  • 相当于把重复的代码封装为一个函数,通过函数完成继承
    // 函数封装
		function extend(fn1, fn2) {
      if (fn1 instanceof Function && fn2 instanceof Function) {
        fn1.prototype = Object.create(fn2.prototype)
        Object.defineProperty(fn1.prototype, 'constructor', {
          value: fn1,
          enumerable: false
        })
      } 
    }

    function User(name, age) {
      this.name = name
      this.age = age
    }
    User.prototype.show = function () {
      console.log(this.name, this.age)
    }

    function Admin(name, age) {
      User.call(this, name, age)
    }

    extend(Admin, User)
    let hd = new Admin('张三', 18)
    hd.show()


    function Member(name, age) {
      User.call(this, name, age)
    }
    extend(Member, User)
    let ls = new Member('李四', 22)
    ls.show()
    
    function Enterprise(name, age) { }


2.8 对象工厂派生对象并实现继承

  • 相当于封装一个函数返回一个对象
    function User(name, age) {
      this.name = name
      this.age = age
    }
    User.prototype.show = function () {
      console.log(this.name, this.age)
    }

    function admin(name, age) {
      let obj = Object.create(User.prototype)
      obj.role = function () {
        console.log('admin role')
      }
      User.call(obj, name, age)
      return obj
    }
    let zs = admin('张三', 18)
    console.log(zs)
    zs.show()


    function member(name, age) {
      let obj = Object.create(User.prototype)
      User.call(obj, name, age)
      return obj
    }
    let ls = member('李四', 22)
    ls.show()

    function enterprise(name, age) {
      let obj = Object.create(User.prototype)
      User.call(obj, name, age)
      return obj
    }
    let ww = enterprise('王五', 80)
    ww.show()

2.9 多继承造成的困扰

  • js 没有多继承,只能通过继承父级,让父级再继承父级,…
    • 问题:这种方式会让没有关系的混在一起,导致非常混乱
    // 函数封装
    function extend(fn1, fn2) {
      if (fn1 instanceof Function && fn2 instanceof Function) {
        fn1.prototype = Object.create(fn2.prototype)
        Object.defineProperty(fn1.prototype, 'constructor', {
          value: fn1,
          enumerable: false
        })
      }
    }

    function User() {
      Member.call(this)
    }
    function Member() {
      Admin.call(this)
    }
    function Admin() { }

    // Member 有积分功能,
    extend(Member, Admin)
    extend(User, Member)

    User.prototype.address = function () {
      console.log('收货地址')
    }
    Member.prototype.jifen = function () {
      console.log('积分统计')
    }
    Admin.prototype.power = function () {
      console.log('权限功能')
    }

    let zs = new User()
    // 张三使用 自身的收货地址功能、积分功能、权限功能 需要让User继承Member,让Member继承Admin
    zs.address()
    zs.jifen()
    zs.power()

2.10 使用mixin实现多继承

  • mixin 混合功能
    • 理解:有一个类,这个类为其他类提供服务,但是提供服务的手段不是继承
    • 步骤:
      • 将构造函数改为对象
      • 向原型中压入对象属性

    function User() { }
    User.prototype.address = function () {
      console.log('收货地址')
    }
    const member = {
      jifen() {
        console.log('积分统计')
      }
    }
    const admin = {
      power() {
        console.log('权限功能')
      }
    }
    // 直接向User原型中压入对象属性
    // User.prototype.jifen = member.jifen
    // User.prototype.power = admin.power
    // console.log(User.prototype)
    // let zs = new User()
    // zs.address()
    // zs.jifen()
    // zs.power()

    // 当对象中有很多可枚举属性时,可以使用Object.assign
    Object.assign(User.prototype, member, admin)
    console.log(User.prototype)
    let zs = new User()
    zs.address()
    zs.jifen()
    zs.power()

2.11 mixin内部继承与super关键字

  • 通过改变对象内部的__proto__实现

    function User() { }
    User.prototype.address = function () {
      console.log('收货地址')
    }
    const request = {
      ajax() {
        return '请求'
      }
    }
    const member = {
      __proto__: request,
      jifen() {
        console.log(this.__proto__.ajax() + '积分统计')
        // super = this.__proto__ 
        // super 是对象的原型
        // console.log(super.ajax() + '积分统计')
      }
    }
    const admin = {
      power() {
        console.log('权限功能')
      }
    }

    // 当对象中有很多可枚举属性时,可以使用Object.assign
    Object.assign(User.prototype, request, member, admin)
    console.log(User.prototype)
    let zs = new User()
    zs.address()
    zs.jifen()
    zs.power()

    // User.prototype.ajax = request.ajax
    // User.prototype.jifen = member.jifen
    // User.prototype.power = admin.power
    // console.log(User.prototype)
    // let zs = new User()
    // zs.address()
    // zs.jifen()
    // zs.power()

3.继承的几种方式

3.1原型链

  • 基本思想: 利用原型让一个引用类型继承另一个引用类型的属性与方法

  • 原型链定义:实例与原型的链条

  • 问题:

    • 包含引用类型值的原型与实例对象会共用同一个引用类型
    • 在创建子类型的实例时,不能像超类型的构造函数传递参数

在这里插入图片描述

3.2 经典继承

  • 基本思想: 在子类型构造函数的内部调用超类型构造函数------ call() 、apply()

  • 优点: 能够在子类型构造函数中向超类型构造函数传递参数

  • 缺点:方法都在构造函数中定义,函数无法复用

3.3 组合继承

  • 基本思想:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承

3.4 原型式继承

  • 基本思想:借助原型可以基于已有的对象创建新对象
  • 特点:包含引用类型值得属性始终都会共享相应的值,就像使用原型模式一样

3.5 寄生式继承

  • 基本思路:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回对象

3.6 寄生组合式继承

  • 概念:通过借用构造函数来继承属性,通过原型链的混合形式来继承方法
  • 基本思路: 不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型的原型的一个副本而已

4.创建对象的几种模式

4.1 工厂模式

  • 优点:创建多个相似对象
  • 问题:没有解决对象识别的问题(即怎样知道一个对象的类型)
    /**
     * 工厂模式
    */
    function createPerson(name, age, job){
      var o = new Object()
      o.name = name
      o.age = age
      o.job = job
      o.sayName = function() {
        console.log(this.name)
      }
      return o
    }
    var person1 = createPerson('张三',18,'software Engineer')
    var person2 = createPerson('red',31,'teacher')
  

4.2 构造函数模式

  • 优点:可以创建特定类型的对象,可以将他的实例标识为一种特定的类型
    • 检测对象类型使用 instanceof
  • 缺点:每个方法都会在每个实例上重新创建一边

    /**
     * 构造函数模式
    */
    function Person(name,age,job) {
      this.name = name
      this.age = age
      this.job = job
      this.sayName = function() {
        console.log(this.name)
      }
    }
    var person1 = new Person('张三', 18, 'software Engineer')
    var person2 = new Person('red', 31, 'teacher')

4.3 原型模式

  • 优点:所有对象实例共享它所包含的属性和方法
  • 缺点:对于引用类型值得属性,所有实例会共享

    /**
     * 原型模式
    */
    function Person() {}
    Person.prototype.name = '张三'
    Person.prototype.age = 18
    Person.prototype.job = 'software Engineer'
    Person.prototype.sayName = function() {
      console.log(this.name)
    }    
    var person1 = new Person()
    person1.sayName()   //张三
    var person2 = new Person()
    person2.sayName()   //张三

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

  • 概念:构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性
  • 特点:
    • 每个实例都会有自己的一份实例属性的副本,同时又共享着对方法的引用,最大限度的节省了内存
    • 这种混合模式还支持向构造函数传递参数

    /**
     * 混合模式
    */
    function Person(name, age, job) {
      this.name = name
      this.age = age
      this.job = job
      this.friends = ['Tom', 'Red']
    }
    Person.prototype = {
      constructor: Person,
      sayName() {
        console.log(this.name)
      }
    }
    var person1 = new Person('张三', 18, 'software Engineer')    
    var person2 = new Person('李四',31,'teacher')
    person1.friends.push('Van')
    console.log(person1.friends)    // ["Tom", "Red", "Van"]
    console.log(person2.friends)    // ["Tom", "Red"]
    console.log(person1.friends === person2.friends)    // false
    console.log(person1.sayName === person2.sayName)    // true

4.5 动态原型模式

  • 相当于在构造函数中加了条件,过滤掉已经存在的方法

    /**
     * 动态原型模式
    */
    function Person(name, age, job) {
      this.name = name
      this.age = age
      this.job = job
      this.friends = ['Tom', 'Red']
      // 方法
      if(typeof this.sayName != 'function') {
        Person.prototype.sayName = function() {
          console.log(this.name)
        }
      }
    }
    var person1 = new Person('张三', 18, 'software Engineer')    
    var person2 = new Person('李四',31,'teacher')
    person1.friends.push('Van')
    console.log(person1.friends)    // ["Tom", "Red", "Van"]
    console.log(person2.friends)    // ["Tom", "Red"]
    console.log(person1.friends === person2.friends)    // false
    console.log(person1.sayName === person2.sayName)    // true

    // 在实例化之后就已经加上了相应的方法
    console.dir(Person.prototype)   // 里面已经有了 sayName: ƒ ()
    person1.sayName()   // 张三
    person2.sayName()   // 李四

4.6 寄生构造函数

  • 相当于复制内置对象,在添加相应方法

    /**
     * 寄生构造函数模式
    */
    function Person(name, age, job) {
      var o = new Object()
      o.name = name
      o.age = age
      o.job = job
      o.friends = ['Tom', 'Red']
      o.sayName = function() {
        console.log(o.name)
      }
      return o
    }
    var person1 = new Person('张三', 18, 'software Engineer')    
    var person2 = new Person('李四',31,'teacher')
    person1.friends.push('Van')
    console.log(person1.friends)    // ["Tom", "Red", "Van"]
    console.log(person2.friends)    // ["Tom", "Red"]
    console.log(person1.friends === person2.friends)    // false
    console.log(person1.sayName === person2.sayName)    // false

    /**
     * 例子
    */
    function SpecialArray() {
      // 创建数组
      var value = new Array()
      // 添加值
      value.push.apply(value, arguments)
      // 添加方法
      value.toPipedString = function() {
        return this.join('|')
      }
      // 返回数组
      return value
    }
    
    var color = new SpecialArray('red','orange','yellow','green')
    console.log(color)            // ["red", "orange", "yellow", "green", toPipedString: ƒ]
    var a= color.toPipedString()  
    console.log(a)                // red|orange|yellow|green


4.7 稳妥构造函数模式

  • 特点:
    • 没有公共属性
    • 只能通过已有方法访问原有数据(属性)

    /**
     * 稳妥构造函数模式
    */
    function Person(name, age, job) {
      // 创建要返回的对象
      var o = new Object()
      // 可以在这里定义私有变量和函数
      
      // 添加方法
      o.sayName = function() {
        console.log(o.name)
      }
      // 返回对象
      return o
    }
    var person1 = new Person('张三', 18, 'software Engineer')    
    // 只能通过sayName访问name属性
    person1.sayName()
    var person2 = new Person('李四',31,'teacher')
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值