【问题标题】:Javascript/ Typescript .bind() get Variable in bound functionJavascript/ Typescript .bind() 获取绑定函数中的变量
【发布时间】:2020-02-17 16:58:21
【问题描述】:

我想将一个变量绑定到一个回调函数,但 mongoose 已经在该函数中嵌入了一个名为 val 的参数。当我想用 .bind() 将另一个值绑定到回调函数时,我丢失了原始的 val 属性。有没有办法我可以保留原始参数并将我的变量与 .bind() 添加到它? 下面是一个例子。

// gets val parameter from validator
const maxLengthValidator = (val: string): boolean => {
  if (val.length > 255) return false;
  return true;
};

const userSchema = new Schema({
  email: {
    type: String,
    required: true,
    // unique: true
    validate: [
      {
        validator: maxLengthValidator, // includes a val attribute from post request
        msg: 'too short'
      },
    ]
  },
  password: {
    type: String,
    required: true
  }
});

export default model('User', userSchema);

我想做什么:

// gets val parameter from validator
const maxLengthValidator = (val: string, boundary: number): boolean => {
  if (val.length > boundary) return false;
  return true;
};

...
validate: [
  {
    validator: maxLengthValidator.bind(this, 255), // doesn't work, would need to specify the string for the val parameter
    msg: 'too short'
  },
]
...

所以我可以在验证器对象中设置一个最大长度。

【问题讨论】:

  • 为什么不直接做validator: (val: string): boolean => maxLengthValidator(val, 255)?
  • 或者,您可以交换maxLengthValidator 的参数顺序并使用现有的绑定调用。或者使用 const maxLengthValidator = (boundary: number): (string) => boolean => (val: string): boolean => { 和 validator: maxLengthValidator(255) 之类的柯里化
  • 有时你只是看不到明显的:D 谢谢大家!

标签: javascript mongodb typescript validation mongoose


【解决方案1】:

咖喱

你可以改变你的函数,所以不是接受两个参数,而是接受一个,然后返回一个接受第二个参数的第二个函数。这是已知的技术,称为curry。

// maxLengthValidator :: number -> string -> boolean 
const maxLengthValidator = (boundary: number) => (val: string) : boolean  => {
  if (val.length > boundary) return false;
  return true;
};

// ... later ...

  validator: maxLengthValidator(255);

这样,您可以通过调用函数来创建不同的验证器:maxLengthValidator(10) 将为您提供一个可以使用 10 个字符的验证器。由于它是一个函数,你也可以将它分配给一个变量:

//userNameLengthValidator :: string -> boolean
const userNameLengthValidator: (val: string) => boolean = maxLengthValidator(10);

交换参数以适应部分分配

不是取值然后最大长度,而是先取长度。与 currying 函数相同,但这个函数仍然可以采用两个参数:

const maxLengthValidator = (boundary: number, val: string): boolean => {
  if (val.length > boundary) return false;
  return true;
};

// ... later ...

  validator: maxLengthValidator.bind(this, 255);

同样的基本思想。不过,这一次您有更多的灵活性 - 您可以使用两个参数 maxLengthValidator(10, username) 调用该函数,或者只使用一个参数部分应用它。后者产生几乎与柯里化相同的东西,因为你仍然会得到一个具有相同签名的新函数:

//userNameLengthValidator :: string -> boolean
const userNameLengthValidator: (val: string) => boolean = maxLengthValidator.bind(this, 10);

尽管有相似之处currying is not partial application。此外,可以对任何数量(任何数量的参数)的函数进行柯里化,以便能够一次获取一个或多个。例如,您可以在 Lodash 中看到 _.curry

function add4(a, b, c, d) {
  console.log(a + b + c + d);
}

const curryAdd4 = _.curry(add4); 

curryAdd4(1)(2)(3)(4);
curryAdd4(1, 2, 3, 4);
curryAdd4(1)(2, 3)(4);
curryAdd4(1, 2, 3)(4);
curryAdd4(1)(2, 3, 4);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

使用占位符或从右侧部分应用

您可以保持签名不变,只需更改您的部分应用程序。 Function#bind 不是很灵活,但您可以自己编写或(更好)使用库。我将再次使用 Lodash,因为它对这些有很好的实现:

带有占位符的部分应用程序

允许您在部分应用时跳过一些参数。所以你可以跳过第一个而只设置第二个:

const maxLengthValidator = (val, boundary) => {
  if (val.length > boundary) return false;
  return true;
};

//userNameLengthValidator :: string -> boolean
const usernameLengthValidator = _.partial(maxLengthValidator, _, 10);

console.log(usernameLengthValidator("usernameTooLong"));
console.log(usernameLengthValidator("user"));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

从右侧部分应用

部分应用从右到左,所以会先设置boundary:

const maxLengthValidator = (val, boundary) => {
  if (val.length > boundary) return false;
  return true;
};

//userNameLengthValidator :: string -> boolean
const usernameLengthValidator = _.partialRight(maxLengthValidator, 10);

console.log(usernameLengthValidator("usernameTooLong"));
console.log(usernameLengthValidator("user"));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

【讨论】:

  • 谢谢!以前从未听说过这种技术,对我帮助很大!
猜你喜欢
  • 2015-07-14
  • 2016-03-14
  • 2018-09-02
  • 1970-01-01
  • 1970-01-01
  • 2018-05-24
  • 1970-01-01
  • 2012-02-12
相关资源
最近更新 更多