JavaScript デザイン パターン: ファクトリ デザイン パターン

1. はじめに

JavaScript は弱い型指定言語であり、コンパイルなしで実行できるインタープリタ型スクリプト言語であり、抽象化、カプセル化、継承、ポリモーフィズムという特徴があります。ファクトリ デザイン パターンはカプセル化の特性を体現しており、キーワード new によって同じカプセル化を実装し、特定の種類のものを抽象化します。

2. デザインアイデア

オブジェクトのカプセル化とファクトリーデザインパターンによる作成

1.ファクトリー機能の実装

オブジェクトのプライベート変数、またはオブジェクトの特定の特性は関数を通じて作成され、オブジェクトの共通の動作特性はプロトタイプ チェーン プロトタイプを通じて作成されます。

const Product = function(...options) {
    
    
 this.name = options[0].name
}
Product.prototype.methods = function() {
    
    
	console.log('methods')
}
let product = new Product({
    
    name:"test"})

2. ファクトリクラスの実装

ES6 では、クラス メソッドとコンストラクターの概念を通じてオブジェクトの作成が可能です。静的メソッドはクラスが所有するメソッドであり、オブジェクト インスタンスの作成に使用できます。クラスの設計を使用して、親と子の間で共有属性メソッドを継承および実装することもできます。クラス。

class Product {
    
    
 //构造器
  constructor(options) {
    
    
  	this.name = options.name
  }
  //静态方法
  static getInstance(prop) {
    
    
  	return new Product(prop)
  }
  
}

3. ケース

1.Vueプロジェクトの開発例

Vue もファクトリによって設計されたオブジェクト インスタンスです。vue のインスタンス化のソース コードの一部を解析する例として、ディレクトリは src/core/instance/index.js にあります。

import {
    
     initMixin } from './init'
import {
    
     stateMixin } from './state'
import {
    
     renderMixin } from './render'
import {
    
     eventsMixin } from './events'
import {
    
     lifecycleMixin } from './lifecycle'
import {
    
     warn } from '../util/index'

function Vue (options) {
    
    
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    
    
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

Vue は、initMixin(Vue)、stateMixin(Vue)、eventsMixin(Vue)、lifecycleMixin(Vue)、renderMixin(Vue) の 5 つの初期化と、ステータス、イベント、生産サイクル、およびステータスに関連する論理処理を備えたファンクション ファクトリを通じて作成されます。 initMixin(Vue) を例として分析します。

export function initMixin (Vue: Class<Component>) {
    
    
  Vue.prototype._init = function (options?: Object) {
    
    
    const vm: Component = this
    // a uid
    vm._uid = uid++

    let startTag, endTag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    
    
      startTag = `vue-perf-start:${
      
      vm._uid}`
      endTag = `vue-perf-end:${
      
      vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    if (options && options._isComponent) {
    
    
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
    
    
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {
    
    },
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
    
    
      initProxy(vm)
    } else {
    
    
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    
    
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${
      
      vm._name} init`, startTag, endTag)
    }

    if (vm.$options.el) {
    
    
      vm.$mount(vm.$options.el)
    }
  }
}

initMixin メソッドは、プロトタイプを通じて _init 初期化メソッドをマウントします。

export function initLifecycle (vm: Component) {
    
    
  const options = vm.$options

  // locate first non-abstract parent
  let parent = options.parent
  if (parent && !options.abstract) {
    
    
    while (parent.$options.abstract && parent.$parent) {
    
    
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }

  vm.$parent = parent
  vm.$root = parent ? parent.$root : vm

  vm.$children = []
  vm.$refs = {
    
    }

  vm._watcher = null
  vm._inactive = null
  vm._directInactive = false
  vm._isMounted = false
  vm._isDestroyed = false
  vm._isBeingDestroyed = false
}

initLifecycle は、vue の多くの共通属性オブジェクトをマウントします。

2.elementUIフレームワークインスタンス

テーブルコンポーネントを解析する例として、packages/table/src/table.vue を取り上げます。

data() {
    
    
      const {
    
     hasChildren = 'hasChildren', children = 'children' } = this.treeProps;
      this.store = createStore(this, {
    
    
        rowKey: this.rowKey,
        defaultExpandAll: this.defaultExpandAll,
        selectOnIndeterminate: this.selectOnIndeterminate,
        // TreeTable 的相关配置
        indent: this.indent,
        lazy: this.lazy,
        lazyColumnIdentifier: hasChildren,
        childrenColumnName: children
      });
      const layout = new TableLayout({
    
    
        store: this.store,
        table: this,
        fit: this.fit,
        showHeader: this.showHeader
      });
      return {
    
    
        layout,
        isHidden: false,
        renderExpanded: null,
        resizeProxyVisible: false,
        resizeState: {
    
    
          width: null,
          height: null
        },
        // 是否拥有多级表头
        isGroup: false,
        scrollPosition: 'left'
      };
    }

this.store = createStore(this, {}) はインスタンス要素/packages/table/src/store/helper.js を作成する関数です

export function createStore(table, initialState = {
    
    }) {
    
    
  if (!table) {
    
    
    throw new Error('Table is required.');
  }

  const store = new Store();
  store.table = table;
  // fix https://github.com/ElemeFE/element/issues/14075
  // related pr https://github.com/ElemeFE/element/pull/14146
  store.toggleAllSelection = debounce(10, store._toggleAllSelection);
  Object.keys(initialState).forEach(key => {
    
    
    store.states[key] = initialState[key];
  });
  return store;
}

constlayout = new TableLayout({}); es6 クラスと独自のメソッド updateScrollY を使用してインスタンスを作成します。

class TableLayout {
    
    
  constructor(options) {
    
    
    this.observers = [];
    this.table = null;
    this.store = null;
    this.columns = null;
    this.fit = true;
    this.showHeader = true;

    this.height = null;
    this.scrollX = false;
    this.scrollY = false;
    this.bodyWidth = null;
    this.fixedWidth = null;
    this.rightFixedWidth = null;
    this.tableHeight = null;
    this.headerHeight = 44; // Table Header Height
    this.appendHeight = 0; // Append Slot Height
    this.footerHeight = 44; // Table Footer Height
    this.viewportHeight = null; // Table Height - Scroll Bar Height
    this.bodyHeight = null; // Table Height - Table Header Height
    this.fixedBodyHeight = null; // Table Height - Table Header Height - Scroll Bar Height
    this.gutterWidth = scrollbarWidth();

    for (let name in options) {
    
    
      if (options.hasOwnProperty(name)) {
    
    
        this[name] = options[name];
      }
    }

    if (!this.table) {
    
    
      throw new Error('table is required for Table Layout');
    }
    if (!this.store) {
    
    
      throw new Error('store is required for Table Layout');
    }
  }

  updateScrollY() {
    
    
    const height = this.height;
    if (height === null) return false;
    const bodyWrapper = this.table.bodyWrapper;
    if (this.table.$el && bodyWrapper) {
    
    
      const body = bodyWrapper.querySelector('.el-table__body');
      const prevScrollY = this.scrollY;
      const scrollY = body.offsetHeight > this.bodyHeight;
      this.scrollY = scrollY;
      return prevScrollY !== scrollY;
    }
    return false;
  }

おすすめ

転載: blog.csdn.net/qq_29510269/article/details/106251569