1

我有两个文件 index.js 和 actor.js

索引.js

const {Actors} = import("./Actors");

const act1= new Actors({
  name: 'Tony Stark',
  alias: 'Iron Man',
  gender: 'man',
  age: 38,
  powers: ["intelligence", "durability", "magnetism"]
})

const act2= new Actors({
  name: 'Natasha Romanoff',
  alias: 'Black Widow',
  gender: 'woman',
  age: 35,
  powers: ["agility", "martial arts"]
})

const examine = (avenger) => {
  console.count('Actors');
  console.group('*** Actors introduced ***');
  console.log(avenger.toString());
  console.groupEnd();
  console.group('*** Actors called ***');
  console.log(avenger());
  console.groupEnd();
  console.group('*** Actors\'s internals ***');
  console.log(avenger, '\n');
  console.groupEnd();
}

examine(act1);

examine(act2);

Actors.js

class Actors {
    constructor(obj) {
        this.name = obj.name;
        this.age = obj.age;
        this.alias = obj.alias;
        this.gender = obj.gender;
        this.powers = obj.powers;
    }


    toString() {
        return `name:${this.name} \ngender:${this.gender}\nage:${this.age}`;
    }
    avenger() {
        let str = '';
        this.powers.forEach(element => {
            str += `$element \n`;
        });
        return `${this.alias}` + str;
    }


}
module.exports.Actors= Actors;

我应该如何更改 class Actors,以便函数调用avenger()起作用 console.log(avenger());,并产生这个预期的结果:

IRON MAN
intelligence
durability
magnetism
4

3 回答 3

2

如果您确实需要一个Actors调用的实例,则该构造函数必须返回一个函数对象。基本上有两种方法可以做到这一点:

  • 构造函数创建一个本地函数,将所有其他属性分配给该对象,然后返回它。为确保此函数对象标识为Actors实例,您需要将其原型从 更改FunctionActors

  • 创建,Actors的子类Function并调用super以确保实例执行我们需要它执行的代码。如果我们希望该代码动态访问实例的其他属性,我们必须克服这样一个事实,即在没有特定this绑定的情况下调用该函数。所以要么:

    • 我们需要事先绑定该函数并让构造函数返回函数,或者
    • 我们使用不推荐使用的引用获得对执行函数本身的arguments.callee引用。

无论哪种方式,它都会很丑陋,但我想这是一个代码挑战。不过,您不应该在严肃的编码中真正使用这种模式。

这里还有另一个障碍:nameFunction 原型的只读属性,因此您不能只使用属性访问器为其分配新值。相反,您需要明确说明您希望实例获得具有该名称的自己的属性。

1.返回本地函数对象

这是使用第一个选项的工作解决方案:

class Actors {
    constructor(obj) {
        const Avenger = () => `${Avenger.alias.toUpperCase()}\n${Avenger.powers.join("\n")}`;
        Avenger.toString = () => `name:${Avenger.name} \ngender:${Avenger.gender}\nage:${Avenger.age}`;
        const {name, ...rest} = obj;
        Object.defineProperty(Avenger, "name", {value: name});
        Object.assign(Avenger, rest);
        Object.setPrototypeOf(Avenger, Actors.prototype);
        return Avenger;
    }
}


const act1= new Actors({
  name: 'Tony Stark',
  alias: 'Iron Man',
  gender: 'man',
  age: 38,
  powers: ["intelligence", "durability", "magnetism"]
})

const act2= new Actors({
  name: 'Natasha Romanoff',
  alias: 'Black Widow',
  gender: 'woman',
  age: 35,
  powers: ["agility", "martial arts"]
})

const examine = (avenger) => {
  console.count('Actors');
  console.group('*** Actors introduced ***');
  console.log(avenger.toString());
  console.groupEnd();
  console.group('*** Actors called ***');
  console.log(avenger());
  console.groupEnd();
  console.group('*** Actors\'s internals ***');
  console.log(avenger, '\n');
  console.groupEnd();
  console.log(avenger instanceof Actors);
}

examine(act1);
examine(act2);

注意 Stack Snippets 有它自己的实现console,当你运行上面的代码片段时,它的输出console.log(avenger)不是函数的源,就像你在其他地方(Chrome、Firefox、...)一样。

2.子类Function

这是我上面列出的第二个选项的可能实现。

优点:

  • 不改变现有对象的原型
  • 构造函数不返回不同的对象,但this
  • 作为前一点的结果:Actors原型方法可以在实例上使用

缺点:

  • Function调用构造函数 - 必须使用字符串参数,这会导致每次调用构造函数时都会解析运行时代码(除非引擎应用了一些优化)。
  • 已弃用arguments.callee。这是必需的,因为该函数将在没有this绑定的情况下被调用,因此对实例的唯一可用引用是 thiscallee引用。
  • 函数的来源将显示一个匿名函数,而不是一个带有名称的函数Avenger(在评论中澄清的请求)。

这里是:

class Actors extends Function {
    constructor(obj) {
        super(`return arguments.callee.avenger()`);
        let {name, ...rest} = obj;
        Object.defineProperty(this, "name", {value: name});
        Object.assign(this, rest);
    }
    toString() {
        return `name: ${this.name}\ngender: ${this.gender}\nage: ${this.age}`;
    }
    avenger() {
        return `${this.alias.toUpperCase()}\n${this.powers.join("\n")}`;
    }
}

const act1= new Actors({
  name: 'Tony Stark',
  alias: 'Iron Man',
  gender: 'man',
  age: 38,
  powers: ["intelligence", "durability", "magnetism"]
})

const act2= new Actors({
  name: 'Natasha Romanoff',
  alias: 'Black Widow',
  gender: 'woman',
  age: 35,
  powers: ["agility", "martial arts"]
})

const examine = (avenger) => {
  console.count('Actors');
  console.group('*** Actors introduced ***');
  console.log(avenger.toString());
  console.groupEnd();
  console.group('*** Actors called ***');
  console.log(avenger());
  console.groupEnd();
  console.group('*** Actors\'s internals ***');
  console.log(avenger, '\n');
  console.groupEnd();
  console.log(avenger instanceof Actors);
}

examine(act1);
examine(act2);

没有可用的选项代表良好的编码习惯。您没有充分的理由希望构造函数按照您在问题中的要求运行,除非这回答了一些漂亮的代码挑战,除此之外别无他用。

于 2021-08-11T12:34:40.103 回答
1

代替

console.log(avenger());

试着打电话

console.log(avenger.avenger());
于 2021-08-11T08:54:01.983 回答
1

由于除了对avenger()vs的不正确定位之外,OP 的真正问题avenger.avenger()似乎是错误实现的原型avenger方法本身,我不仅建议修复后者的实现,还建议重构和/或重命名类及其方法(请参阅我的评论关于上面OP的问题)。

将代码重构为更明确的措辞,包括constructor函数和原型toString方法的代码优化以及修复以前的原型avenger方法并将其重命名为record,可能会导致类似于下一个提供的代码...

class Avenger {
  constructor(obj) {
    return Object.assign(this, obj); 
  }
  toString() {
    return [

      `name: ${ this.name }`,
      `gender: ${ this.gender }`,
      `age: ${ this.age }`,

    ].join('\n');
  }
  record() {
    return [
    
      this.alias.toUpperCase(),
      this.powers.join('\n'),

    ].join('\n');
  }
}

const character1 = new Avenger({
  name: 'Tony Stark',
  alias: 'Iron Man',
  gender: 'man',
  age: 38,
  powers: ["intelligence", "durability", "magnetism"]
});
const character2 = new Avenger({
  name: 'Natasha Romanoff',
  alias: 'Black Widow',
  gender: 'woman',
  age: 35,
  powers: ["agility", "martial arts"]
});

const examineAvenger = (avenger) => {
  console.count('Avenger');
  console.group('*** Avenger introduced ***');
  // console.log(avenger.toString());
  // console.log(String(avenger));
  console.log(avenger + '');
  console.groupEnd();
  console.group('*** Avenger called ***');
  console.log(avenger.record());
  console.groupEnd();
  console.group('*** Avenger\'s internals ***');
  console.log(avenger, '\n');
  console.groupEnd();
}

examineAvenger(character1);
examineAvenger(character2);
.as-console-wrapper { min-height: 100%!important; top: 0; }

编辑

在对如何提供可调用类型的基于类的(ab)使用进行了大量评论之后,我在此提供了一种基于工厂函数的方法,它不会混淆可调用复仇者对象的真实性质......

function toAvengerString(state) {
  return [

    `name: ${ state.name }`,
    `gender: ${ state.gender }`,
    `age: ${ state.age }`,

  ].join('\n');
}
function toAvengerRecord(state) {
  return [

    state.alias.toUpperCase(),
    state.powers.join('\n'),

  ].join('\n');
}

function createAvenger(initialState) {
  // does create closures over/for each callable avenger ('s `localState`).

  // loosely decouple the `localState` from its initial configuration object.
  const localState = Object.assign({}, initialState);

  // create the callable avenger's behavior
  // as forwarder to a single outer `toAvengerRecord` function.
  const callableAvenger = () => toAvengerRecord(localState);

  // implement the `toString` behavior
  // as forwarder to a single outer `toAvengerString` function.
  Object.defineProperty(callableAvenger, 'toString', {
    value: () => toAvengerString(localState),
  });
  // implement any avenger object's property by iterating
  // over the own properties of the initially provided state
  // by creating setter/getter functionality for each property
  // which read from and write to the encapsulated `localState`.
  Object
    .getOwnPropertyNames(localState)
    .forEach(key => Object.defineProperty(callableAvenger, key, {
      set: value => localState[key] = value,
      get: () => localState[key],
      enumerable: true,
    }));

  // return the custom writable and callable avenger type.
  return callableAvenger;
}

const character1 = createAvenger({
  name: 'Tony Stark',
  alias: 'Iron Man',
  gender: 'man',
  age: 38,
  powers: ["intelligence", "durability", "magnetism"]
});
const character2 = createAvenger({
  name: 'Natasha Romanoff',
  alias: 'Black Widow',
  gender: 'woman',
  age: 35,
  powers: ["agility", "martial arts"]
});

const examineAvenger = (avenger) => {
  console.count('Avenger');
  console.group('*** Avenger introduced ***');
  // console.log(avenger.toString());
  // console.log(String(avenger));
  console.log(avenger + '');
  console.groupEnd();
  console.group('*** Avenger called ***');
  console.log(avenger());
  console.groupEnd();
  console.group('*** Avenger\'s internals ***');
  console.log(avenger, '\n');
  console.groupEnd();
}

examineAvenger(character1);
examineAvenger(character2);

console.log(
  "character1.name = 'Mark Ruffalo' ...",
  character1.name = 'Mark Ruffalo'
);
console.log(
  "character1.alias = 'Hulk' ...",
  character1.alias = 'Hulk'
);
console.log(
  "character1.gender = 'male' ...",
  character1.gender = 'male'
);
console.log(
  "character1.age = 'not important' ...",
  character1.age = 'not important'
);
console.log(
  "character1.powers = ['super human', 'super smart', 'super strong', 'super durable'] ...",
  character1.powers = ['super human', 'super smart', 'super strong', 'super durable']
);
examineAvenger(character1);
.as-console-wrapper { min-height: 100%!important; top: 0; }

于 2021-08-11T10:37:23.310 回答