我正在试图弄清楚如何合并工厂函数闭包并同时使用Object.Assign/Object.Create继承.可能很简单,但我想不出该怎么做.首先,我构建了我想要的利用闭包的工厂.我有作用于我的私有变量的方法.

const projectFactory = function() {
    let _title = "new project";

    const _tabs = [];

    const getTitle = function() {
        return _title;
    }

    const setTitle = function(input) {
        _title = input;
        return;
    }

    const createTab = function() {
        // some method that fills the tabs array
        return;
    }

    return {
        getTitle,
        setTitle,
        createTab
    }
};

const factory1 = projectFactory(); 
console.log(factory1); // methods set on object, _title and _tabs not visible

当调用这个工厂函数时,我要创建的所有对象的所有方法都将是相同的.因此,我决定取出我的方法并将它们存储在一个要引用的对象中,然后使用Object.Assign/Object.Create重写我的工厂函数,以这种方式继承方法.

const projectMethods = {
    getTitle() {
        return this.title;
    },
    setTitle(input) {
        this.title = input;
        return;
    },
    createTab() {
        // some method that fills the tabs array
        return;
    }
};
const projectFactory = function() {
    let title = "new project";

    const tabs = [];

    return Object.assign(Object.create(projectMethods), {
        title, tabs
    });
};

const factory1 = projectFactory();
console.log(factory1); // methods available on prototype, title and tabs visible

但现在,因为我要在工厂中返回整个对象,所以我不再有私有变量.我如何才能达到这样的效果:

console.log(factory1); // methods available on prototype, title and tabs not visible

推荐答案

实际上有a pattern which allows shared (locally enclosed / private) state amongst modularized mixin-functionality/behavior个.当然,它必须完全基于闭包.因此,有一件事不能很好地协同工作,那就是闭包and,Object.assign,后者发生在这样的闭包之外(如OP的示例代码所示).另一方面,仅使用闭包的方法已经足够了.

将这样的模式应用于第一次迭代,OP提供的代码可以实现为……

// function based mixin which incorporates shared state.
function withStatfulProjectBehavior(sharedState) {
  const project = this;

  function getTitle() {
    return sharedState.title;
  }
  function setTitle(value) {
    return (sharedState.title = value);
  }

  function createTab() {
    // some method that fills the tabs array ... e.g. ...
    return sharedState.tabs.push({
      title: 'new tab',
    });
  }

  return Object.assign(project, {
    getTitle,
    setTitle,
    createTab,
  });
}

// `project` factory function.
function createProject() {
  const sharedState = {
    tabs: [],
    title: 'new project',
  }
  const project = {};

  return withStatfulProjectBehavior.call(project, sharedState);
};

const project = createProject();
console.log({ project });

console.log(
  'project.getTitle() ...',
  project.getTitle()
);
console.log(
  "project.setTitle('Foo Bar') ...",
  project.setTitle('Foo Bar')
);
console.log(
  'project.getTitle() ...',
  project.getTitle()
);

console.log(
  'project.createTab() ...',
  project.createTab()
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

而第二次迭代的结果可能已经看起来像...

// helper functionality
function validateTitle(value) {
  if (typeof value !== 'string') {
    // throw new TypeError('A title has to by a string value.');
    console.warn('A title has to by a string value.');
    return false;
  } else {
    return true;
  }
}

// - function based, context aware mixin which applies generically
//   implemented protected property access at its context and over
//   shared state.
function withSharedProtectedProperty(
  state, { key, validation, enumerable = false }
) {
  const type = this;

  Reflect.defineProperty(type, key, {
    get: () => state[key],
    set: value => {
      if (validation(value)) {
        return state[key] = value;
      }
    },
    enumerable,
  });
}

// - function based, context aware mixin which applies tab
//   specific behavior at its context and over shared state.
function withSharedTabManagement(state) {
  this.addTab = function addTab(title = 'unnamed tab') {
    // some method that fills the tabs array ... e.g. ...
    return state.tabs.push({ title });
  };
}

// `project` factory function.
function createProject() {
  const state = {
    tabs: [],
    title: 'new project',
  }
  const project = {};

  withSharedProtectedProperty.call(project, state, {
    enumerable: true,
    key: 'title',
    validation: validateTitle,
  });
  withSharedTabManagement.call(project, state);

  return project;
};

const project = createProject();
console.log({ project });

console.log(
  'project.title ...', project.title
);
console.log(
  "project.title = 1234 ...", (project.title = 1234)
);
console.log(
  'project.title ...', project.title
);
console.log(
  "project.title = 'Foo Bar' ...", (project.title = 'Foo Bar')
);
console.log(
  'project.title ...', project.title
);

console.log(
  'project.addTab() ...', project.addTab()
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

你也可以看看……

一百零二

"[@stuffz] ... You cannot have private variables and shared inherited methods at the same time. It's just impossible to have a shared closure." – Bergi

"@PeterSeliger Sharing state between multiple methods of an instance is the easy thing. What is not possible is sharing the methods (via prototype inheritance)." – Bergi

...我的其中一句回答是……

一百零二

...任何直接实现OP的原始示例代码,基于上面提到的粘合代码,那么应该非常接近...

// project specific module scope.

// state management via `WeakMap` instance.
function getState(reference) {
  return projectStates.get(reference);
}
const projectStates = new WeakMap;

// object based mixin of stateful (later prototypal) project behavior.
const statefulProjectMethods = {
  getTitle() {
    return getState(this).title;
  },
  setTitle(input) {
    return getState(this).title = input;
  },
  createTab(title = 'unnamed tab') {
    // some method that fills the tabs array ... e.g. ...
    return getState(this).tabs.push({ title });
  },
};

// `project` factory function.
/*export */function createProject(protoMethods) {
  const project = Object.create(protoMethods);

  projectStates.set(project, {
    tabs: [],
    title: 'new project',
  });
  return project;
};
// end of project specific module scope.


// another module's scope
// import createProject from '/project.js';
const project = createProject(statefulProjectMethods);

console.log(
  "project.hasOwnProperty('getTitle') ...", project.hasOwnProperty('getTitle')
);
console.log(
  "project.hasOwnProperty('setTitle') ...", project.hasOwnProperty('setTitle')
);
console.log(
  "project.hasOwnProperty('createTab') ...", project.hasOwnProperty('createTab')
);
console.log(
  "Object.getPrototypeOf(project) ...", Object.getPrototypeOf(project)
);

console.log(
  'project.getTitle() ...', project.getTitle()
);
console.log(
  "project.setTitle('Foo Bar') ...", project.setTitle('Foo Bar')
);
console.log(
  'project.getTitle() ...', project.getTitle()
);

console.log(
  'project.createTab() ...', project.createTab()
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Javascript相关问答推荐

无法在page. evalve()内部使用外部函数

单击按钮后未清除 Select

橡皮擦完全让画布变成白色

仅在React和JS中生成深色

一次仅播放一个音频

使用JavaScript单击上一个或下一个特定按钮创建卡滑动器以滑动单个卡

使用TMS Web Core中的HTML模板中的参数调用过程

如何使用JavaScript将文本插入空div

切换时排序对象数组,切换不起作用

如何从Intl.DateTimeFormat中仅获取时区名称?

PDF工具包阿拉伯字体的反转数字

Angular 订阅部分相互依赖并返回数组多个异步Http调用

Rxjs流中生成IMMER不能在对象上操作

Docent.cloneNode(TRUE)不克隆用户输入

使用RxJS from Event和@ViewChild vs KeyUp事件和RxJS主题更改输入字段值

如何在Java脚本中对数据进行签名,并在PHP中验证签名?

如何找到带有特定文本和测试ID的div?

如何根据输入数量正确显示alert ?

我怎样才能点击一个元素,并获得一个与 puppeteer 师导航页面的URL?

我在哪里添加过滤器值到这个函数?