javascript - 我可以将构造函数的原型(prototype)方法绑定(bind)到构造的实例,同时保持关注点分离吗?

标签 javascript

假设我有一个对象构造函数和一个原型(prototype)方法,例如:

function Human(name) {
  this.name = name;
}

Human.prototype.sayName = function(){
  console.log('my name' + this.name);
};

在我的代码的其他地方,我定义了一个 human 的实例:

let jeff = new Human('jeff');

最后我想将 jeff.sayName 作为回调传递给其他一些函数,比如(对于一个特别简单的例子)

function callFunction(callback) {
  callback();
}

callFunction(jeff.sayName);

当我在上面调用 callFunction(jeff.sayName) 时,上下文需要绑定(bind)到 jeff 本身,比如 调用函数(jeff.sayName.bind(jeff))。但这很笨拙,我不想每次使用此方法作为回调时都担心这种逻辑。

另一种方法是将 Human.prototype.sayName 替换为类似

的内容
Human.prototype.createSayName = function(context){
  return function() {
    console.log(context.name);
  };
};

然后用

定义函数
jeff.sayName = jeff.createSayName(jeff)

但这有点尴尬,我想让 jeff 不知道它从 Human.prototype 继承的方法。

所以理想情况下我想在 Human.prototype 本身上处理这个逻辑,比如

Human.prototype.sayName.bind(WhateverObjectHoldsThisMethod)

但我不知道 javascript 有办法做到这一点。

TL;DR 我想要一种将对象的原型(prototype)方法绑定(bind)到继承它的任何对象的方法,而不必在每次将该方法作为参数传递或每次定义新对象时都这样做。对不起,如果这没有什么意义。谢谢!

最佳答案

由于词法环境、作用域解析、原型(prototype)继承和环境记录在 JavaScript 中的工作方式,如果不修改调用回调函数的函数,您所要求的是不可能的。

但是,您可以使用箭头函数来调用您希望的 Human#sayName 引用,而不是将 Human#sayName 引用作为回调传递打电话。

它并不完美,但它简单、干净且可读。

function Human(name) {
  this.name = name;
}

Human.prototype.sayName = function(){
  console.log('my name' + this.name);
};

let jeff = new Human('jeff');

function callFunction(callback) {
  callback();
}

callFunction(_ => jeff.sayName());

为了更好地理解我之前提到的那些花哨的词,以及它们在 JavaScript 中的工作原理,我建议阅读 ECMAScript 2017 Language Specification 的第 8.1 节。 .第 8.1.1.3 小节包含您要查找的特定信息,但到该部分的其余部分对于理解该小节是必要的。

基本上,当您将 Human#sayName 传递给 callFunction 时,您传递的是对原始 sayName 函数的引用,因此您可能以及这样做:(请原谅双关语)

function callFunction(callback) {
  callback();
}

callFunction(function(){
  console.log('my name' + this.name);
});

函数的内容在执行之前不会被评估,这意味着在执行时,this 的值已经改变。雪上加霜的是,原始函数不知道您通过哪个实例请求它。它实际上从未存在于 jeff 对象上。它存在于函数对象的原型(prototype)中,当您执行对象属性查找时,JavaScript 引擎搜索原型(prototype)链以找到该函数。

您很可能会得到您所要求的行为,但不会受到您所设置的约束。例如,如果函数不必存在于原型(prototype)链上,而是可以存在于实例上(请记住,这会为每个实例创建一个新的函数对象,因此会增加成本),您可以定义函数在构造函数中,然后使用不会被覆盖的标识符存储对正确 this 的引用:

function Human(name) {
  const _this = this;
  this.name = name;
  this.sayName = function(){
    console.log('my name' + _this.name);
  };
}

let jeff = new Human('jeff');

function callFunction(callback) {
  const _this = { name: 'hello' }; // does not affect output
  callback();
  callback.call(_this); // does not affect output
}

callFunction(jeff.sayName);

这将是一个更安全的选择,因为您知道 _this 将始终引用您期望它在构造函数上下文中引用的对象,即在该函数中定义的所有函数对象对象将继承其父作用域的标识符,并且这些标识符不会受到调用上下文的影响。

或者,您可以更进一步,根本不依赖 this 的值:

function Human(name) {
  const sayName = function(){
    console.log('my name' + name);
  };
  
  Object.assign(this, { name, sayName });
}

let jeff = new Human('jeff');

function callFunction(callback) {
  const name = 'hello'; // does not affect output
  callback();
  callback.call({ name: 'world' }); // does not affect output
}

callFunction(jeff.sayName);

这样做的好处是:

  • 更易于阅读,
  • 更少的代码,
  • 允许您明确说明通过对象公开的属性和方法,并且
  • 永远不必担心 this 的值是多少。

关于javascript - 我可以将构造函数的原型(prototype)方法绑定(bind)到构造的实例,同时保持关注点分离吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47897716/

相关文章:

javascript - 如果他在单独的文件中创建,如何在 chrome 控制台中调用 Vue 实例

javascript - eslint 对象简写错误,传入变量

PHP 在加载时调用 javascript

javascript - wysihtml5 编辑器 - 不设置按钮样式

javascript - 使用存储在范围变量中的数据而不是进行 ajax 调用

javascript - NicEditor 字体大小从 <font size ='1' > 标签更改为 <span style ='font-size: 8px' >

javascript - TweenMax 重复 GSAP

javascript - 打开抽屉时在 React Native 中隐藏 StatusBar

javascript - 为什么这段 JavaScript 代码返回未定义?

javascript - jQuery 从嵌套元素中获取元素属性