JS 原型链

Why use OO

减少重複的程式码减少记忆体的使用使实体有关联性

类别、物件

类别: 是一个蓝图,未有实体(instance) Ex:车子设计图物件: 透过类别建立出来的实体 Ex:车子

原型链特性

JS prototype Base 继承方式最上层的原型为物件向上寻找属性、方法的特性继承特性 (不同实体,继承同一属性、方法)类别使用 new关键字 建造出实体

JS 原型链

若要建立两个汽车的实体,该如何建立?

Level 0 (直接建立物件)

直接建立两个物件其缺点: 繁琐、无法统一管理、扩展性差、占用记忆体
  const carA = {    wheels: 4,    pressHorn() { console.log('AAA') },  }  const carB = {    wheels: 4,    pressHorn() { console.log('BBB') },  }

Level 1 (prototype建立蓝图)

利用 JS prototype 建立蓝图使用 new 关键字建立实体缺点: function功能相同,但每次创造实体时都複製一份,占用了不必要的记忆体
  const Car = function (brand, sound = '888') {    this.brand = brand    this.wheels = 4    this.pressHorn = function () { console.log(sound) }  }  const carA = new Car('AAA')  const carB = new Car('BBB', 'BBB')

Level 2 (在原型上建立共用方法)

将 Function 挂载到蓝图的 prototype利用原型链 向上寻找的特性,实体无该属性、方法即往上寻找可用的属性、方法写于原型仅佔一份记忆体
  Car.prototype.pressHorn = function () {    console.log(`${this.brand} ${this.sound}`)  }  // true 表 Function 来自同一个记忆体位置  console.log(carA).pressHorn() === carB.pressHorn())

Level 3 (ES5 Reveal Pattern)

封装内部资料,公开想公开的介面ES5 範例
  const Car = function (brand,sound) {    this.brand = brand    this.wheel = 4    this.pressHorn = function (){ console.log(sound) }    return {      brand,      pressHorn: this.pressHorn    }  }  const test = new Car('A','AAA')  console.log(test.wheels) // undefined 不可取得  console.log(test.brand) // 可取得

Level 4 (ES6 Class)

JS 为 prototype base,因此 Class 仅为 prototype的语法糖使用 Class 使语法更精简
  class Car {    // 写于 constructor的内容,皆会在记忆体创一份新的    // 因此 方法避免写于 constructor 内部    constructor(brand = 'default') {      // constructor内容会于实体建立时执行      this.brand = brand      this.init()     }    init() { console.log('init') }    pressHorn(){      console.log(`${this.brand} ${this.sound}`)    }  }

Level 5 (ES6 static/get/set)

ES6 Static 用法

静态方法不须实例化,即可被呼叫但被实例化后,则静态方法不能被呼叫 (私有方法)常用于工具函式 (资料处理)
  class Calculator {    constructor(str) { this.str = str }    static add(a, b) { console.log(a + b) }    hey() { return this.str }  }  const calculatorA = new Calculator('hey')  // 建立实体后,无法透过实体使用该函式  console.log(calculatorA.add) // undefined  console.log(calculatorA.hey()) // 'hey'  // 没有建立实体即可使用  Calculator.add(1, 4) // 3

ES6 get/set 用法 (仅用于私有属性)

可控制 资料 写入 & 读出的过程可于中间作一些格式处理、错误侦测getter不会有传入参数,setter只会有一个传入参数看不是很懂... 範例参考至此
  class Option {    constructor(key, value) {      if (typeof key !== 'undefined') { this[`_${key}`] = value }    }    get color() {      if (this._color !== undefined) { return this._color }       return 'no color prop'    }    set color(value) { this._color = value }  }  const op1 = new Option('color', 'red')  op1.color = 'yellow'  const op2 = new Option('action', 'run')  op2.color = 'yellow'

ES5 getter / setter 写法

使用 Object.defineProperty若要使物件的属性无法被更动 writable: false,
  Object.defineProperty(obj, 'key', {    get() {      // 取得值前 可于此作format....etc      return this.name    },    set(val) {      // 设定值前可以作一些 例外判断 (falsy...etc)      this.name = val      console.log(val)    },  })

Bonus: 预设参数 & 解构

  const User = function ({ name = 'default', age }) {    this.name = name // 可预设但可传入修改    this.age = age    this.gender = 'male' // 可预设不给用传入的  }  // 传入顺序无差,以 Key为基準  let test = new User({ age: 30 })  console.log(test.name, test.gender) // 'default' 'male'

取得原型语法

取得原型1: 双下底线proto (不推荐使用 效能差)取得原型2: Object.getPrototypeOf(obj) (建议使用、IE9+)
// ESLint/MDN 不建议使用 __proto__ 取得原型const CarProto = Car.__proto__// 推荐使用 ES5const CarProto = Object.getPrototypeOf(Car)

instanceof 原理 (判断是否有该原型)

判断目标是否在其原型链之下
  // 判断 'str' 是否在 String 之下  console.log('str' instanceof String)   // false,因为此种表示方法为原始型别,其原型链为 undefined  // 使用 new 关键字建立物件  const newStr = new String('newStr')  console.log(newStr instanceof String )  console.log(newStr instanceof Object )  // 皆true,String & Object 皆于 newStr 原型链上

术语

literal
不使用 constructor(构造函数 new) 建立 dataconstructor 构造函式
一个指定对象实例的类型的类或函数new 关键字
创建一个用 自定义的对象类型 的 instance(实例)
或具有构造函数的内置对象的实例
  // Object.literal  const obj = { test(){ console.log('hi') } }  const mark = new Person()  const str =  new String()

小建议

类别 使用大写作为开头 Car不公开方法 使用_作为开头 static _add()使用 Object.getPrototypeOf(obj) 取得原型使用 ES6 Class使用 预设参数 & 解构

参考资料

保哥 物件导向基础


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章