如何在JS中使用行为委派(OLOO)定义私有变量? [英] How to define private variables with behavior delegation (OLOO) in JS?

查看:144
本文介绍了如何在JS中使用行为委派(OLOO)定义私有变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图围绕对象链接其他对象来编写一个节点模块。这是我迄今为止(灵感来自此答案):

 'use strict'

//可组合的原型对象
var parent = {
publicVar:1,
doSomething (){
return externalMethod(this.publicVar)+ 10
}
}

//可组合的原型对象
var child = {
doSomethingChild(){
return this.publicVar + 20
}
}

//外部方法
函数externalMethod(arg){
return arg
}

//父工厂
函数父(){
let privateVar = 2

return Object。 assign({
getPrivate(){
return privateVar
}
},parent)
}

//子工厂
function Child(){
let privateVar = 4
let parent = Parent()//调用父工厂
return Object.assign(parent,child,{
getPrivateChild(){
return privateVar
}
})
}

//节点导出
module.exports = {
父项:父(),
孩子:孩子()
}

稍后我会要求模块像这是:

  Parent = require('./ my-module')。Parent 
Child = require(' /my-module').Child
Parent.getPrivate()// 2
Parent.doSomething()// 11
Child.getPrivateChild()// 4
孩子。 doSomethingChild()// 21

我恐怕可能会有一个更优雅的方式使用0100。我主要关心的是,我认为我应该在孩子工厂里做 let parent = Object.create(Parent),但是如果我这样做,它不起作用。



所以,1)我是否缺少一些东西,2)可以重构吗?

解决方案

你绝对应该喜欢组合(包括mixins)超过单祖先类继承,所以你在正确的轨道上。也就是说,JavaScript没有私有属性,因为您可能会从其他语言中了解它们。对于具有真实数据隐私(通过关闭)的可组合原型,您正在寻找的是功能组合



strong>,它们是一个接收对象并返回一个添加了新功能的对象的函数。



然而,在我看来,通常更好的做法是使用可组合的工厂(例如邮票)。 AFAIK, Stampit 是最广泛使用的可组合工厂的实施。



邮票是一个可组合的工厂函数,它根据其描述符返回对象实例。邮票有一个名为 .compose()的方法。当调用 .compose()方法使用当前邮票作为基础创建新邮票时,以传递为参数的composables列表组成:

  const combinedStamp = baseStamp.compose(composable1,composable2,composable3); 

可压缩是一个印记或POJO(Plain Old JavaScript Object)戳记描述符。



.compose()方法兼作邮票描述符。换句话说,描述符属性附加到邮票 .compose()方法,例如。

可写描述符(或只是描述符)是包含信息的元数据对象必须创建一个对象实例。
描述符包含:




  • 方法 - 一组方法将被添加到对象的委托原型中。

  • 属性 - 将通过赋值添加到新对象实例的一组属性。

  • 初始化器 - 将按顺序运行的一系列函数。邮票详细信息和参数被传递给初始化程序。

  • staticProperties - 将通过分配复制到邮票中的一组静态属性。



基本问题,如如何继承特权方法和私人数据?和继承层次结构有什么好的选择?是许多JavaScript用户的保护者。



我们同时使用 init()回答这两个问题, compose() stamp-utils 库。




  • compose(... composables:[... Composable])=>
  • init(... functions:[... Function])=> ;邮票采用任何数量的初始化函数,并返回一个新邮票。



首先,我们将使用关闭以创建数据隐私:

  const a = init(function(){
const a ='a'

Object.assign(this,{
getA(){
return a;
}
});
});

console.log(typeof a()); //'object'
console.log(a()。getA()); //'a'

它使用函数范围来封装私有数据。请注意,getter必须在函数内定义才能访问闭包变量。



这是另一个:

  const b = init(function(){
const a ='b';

Object.assign(this,{
getB (){
return a;
}
});
});

那些 a 不是打字错误。关键是要证明 a b 的私有变量不会冲突。



但这是真正的对待:

  const c = compose(a,b); 

const foo = c();
console.log(foo.getA()); //'a'
console.log(foo.getB()); //'b'

WAT?是啊。您只是同时从两个来源继承了特权方法和私有数据。



在使用可组合对象时,您应该注意一些经验法则:


  1. 组合不是类继承。不要试图模拟是 - 一种关系,或者是通过父/子关系来考虑事物。相反,使用基于功能的思维。 myNewObject 需要 featureA featureB featureC ,所以: myNewFactory = compose(featureA,featureB,featureC); myNewObject = myNewFactory()。请注意, myNewObject 不是 featureA featureB的实例 等,而是实现使用包含这些功能。 b $ b
  2. 邮票& mixins不应该彼此了解。 (没有隐含的依赖)。

  3. 邮票& mixins应该很小。介绍尽可能少的新属性。

  4. 撰写时,您可以并且应该选择性地继承只需要的道具,并重新命名道具以避免碰撞。

  5. 首选模块,用于代码重用,无论何时(可能是大部分时间)。

  6. 喜欢域模型和状态管理的功能编程。避免共享的可变状态。

  7. 通过继承任何类型(包括混合或邮票),更高级的功能和更高级的组件。

如果你坚持这些准则,你的邮票& mixins将免受常见的遗传问题,如脆弱的基层问题,大猩猩/香蕉问题,必要性问题的重复等等。


I'm trying to wrap my mind around Object Linking Other Objects to write a Node module. This is what I have so far (inspired by this answer):

'use strict'

// Composable prototype object
var parent = {
  publicVar: 1,
  doSomething() {
    return externalMethod(this.publicVar) + 10
  }
}

// Composable prototype object
var child = {
  doSomethingChild() {
    return this.publicVar + 20
  } 
}

// an external method
function externalMethod(arg) {
  return arg
}

// the parent factory
function Parent() {
  let privateVar = 2

  return Object.assign({
    getPrivate() {
      return privateVar
    }
  }, parent)
}

// the child factory
function Child() {
  let privateVar = 4
  let parent = Parent() // call to the Parent factory
  return Object.assign(parent, child, {
    getPrivateChild() {
        return privateVar
    }
  })
}

// Node export
module.exports = {
  Parent: Parent(),
  Child: Child()
}

Later, I will require the module like this:

Parent = require('./my-module').Parent
Child = require('./my-module').Child
Parent.getPrivate() // 2
Parent.doSomething() // 11
Child.getPrivateChild() // 4
Child.doSomethingChild() // 21

I'm afraid there might be a more elegant way of doing this using OLOO. My main concern is that I think I should be doing let parent = Object.create(Parent) in the Child factory, but if I do that it doesn't work.

So, 1) am I missing something and 2) can this be refactored?

解决方案

You absolutely should prefer composition (including mixins) over single-ancestor class inheritance, so you're on the right track. That said, JavaScript doesn't have private properties as you might know them from other languages. We use closures for data privacy in JS.

For composable prototypes with real data privacy (via closure), what you're looking for is functional mixins, which are functions that take an object and return an object with new capabilities added.

However, in my opinion, it's usually better practice to do your functional inheritance using composable factories (such as stamps). AFAIK, Stampit is the most widely-used implementation of composable factories.

A stamp is a composable factory function that returns object instances based on its descriptor. Stamps have a method called .compose(). When called the .compose() method creates new stamp using the current stamp as a base, composed with a list of composables passed as arguments:

const combinedStamp = baseStamp.compose(composable1, composable2, composable3);

A composable is a stamp or a POJO (Plain Old JavaScript Object) stamp descriptor.

The .compose() method doubles as the stamp’s descriptor. In other words, descriptor properties are attached to the stamp .compose() method, e.g. stamp.compose.methods.

Composable descriptor (or just descriptor) is a meta data object which contains the information necessary to create an object instance. A descriptor contains:

  • methods — A set of methods that will be added to the object’s delegate prototype.
  • properties — A set of properties that will be added to new object instances by assignment.
  • initializers — An array of functions that will run in sequence. Stamp details and arguments get passed to initializers.
  • staticProperties — A set of static properties that will be copied by assignment to the stamp.

Basic questions like "how do I inherit privileged methods and private data?" and "what are some good alternatives to inheritance hierarchies?" are stumpers for many JavaScript users.

Let’s answer both of these questions at the same time using init() and compose() from the stamp-utils library.

  • compose(…composables: [...Composable]) => Stamp takes any number of composables and returns a new stamp.
  • init(…functions: [...Function]) => Stamp takes any number of initializer functions and returns a new stamp.

First, we’ll use a closure to create data privacy:

const a = init(function () {
  const a = 'a';

  Object.assign(this, {
    getA () {
      return a;
    }
  });
});

console.log(typeof a()); // 'object'
console.log(a().getA()); // 'a'

It uses function scope to encapsulate private data. Note that the getter must be defined inside the function in order to access the closure variables.

Here’s another:

const b = init(function () {
  const a = 'b';

  Object.assign(this, {
    getB () {
      return a;
    }
  });
});

Those a’s are not typos. The point is to demonstrate that a and b’s private variables won’t clash.

But here’s the real treat:

const c = compose(a, b);

const foo = c();
console.log(foo.getA()); // 'a'
console.log(foo.getB()); // 'b'

WAT? Yeah. You just inherited privileged methods and private data from two sources at the same time.

There are some rules of thumb you should observe when working with composable objects:

  1. Composition is not class inheritance. Don't try to model is-a relationships or think of things in terms of parent/child relationships. Instead, use feature-based thinking. myNewObject needs featureA, featureB and featureC, so: myNewFactory = compose(featureA, featureB, featureC); myNewObject = myNewFactory(). Notice that myNewObject is not an instance of featureA, featureB, etc... instead, it implements, uses, or contains those features.
  2. Stamps & mixins should not know about each other. (No implicit dependencies).
  3. Stamps & mixins should be small. Introduce as few new properties as possible.
  4. When composing, you can and should selectively inherit only the props you need, and rename props to avoid collisions.
  5. Prefer modules for code reuse whenever you can (which should be most of the time).
  6. Prefer functional programming for domain models and state management. Avoid shared mutable state.
  7. Prefer higher order functions and higher order components over inheritance of any kind (including mixins or stamps).

If you stick to those guidelines, your stamps & mixins will be immune to common inheritance problems such as the fragile base class problem, the gorilla/banana problem, the duplication by necessity problem, etc...

这篇关于如何在JS中使用行为委派(OLOO)定义私有变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆