向原型添加方法时的范围困难

Scope difficulties when adding methods to prototype

我想创建一个工厂来生产函数构造函数作为产品。工厂拥有用于向产品原型添加方法的方法。实例化的产品包含这些用于访问产品文档字段的函数。

https://jsfiddle.net/84fzwvj7/2/

function Factory() {
    this.Product = function(doc = {}) {
        this.doc = doc;
    }
}

Factory.prototype.addDocField = function(name) {
    this.Product.prototype["set" + name] = function(value) {
        this.doc[name] = value;
    }
    this.Product.prototype["get" + name] = function() {
        return this.doc[name];
    }
    return this;
}

var MyClass = new Factory().addDocField("Test").Product;
var obj = new MyClass();
console.dir(obj.doc.Test);          // undefined
obj.setTest("Lorem Ipsum");
console.dir(obj.doc.Test);          // "Lorem Ipsum"

此方法适用于只需要 getters / setters 的文档字段。但是我需要像这样的更复杂的字段访问器:

// ... Object was created before with an array like field
obj.users.create(login);
obj.users.deleteById("46891");

遗憾的是,我想不出一种方法来定义 createdeleteById 函数并将它们的 this 关键字绑定到 obj。我尝试将原型方法添加到一个对象,但那是我无法弄清楚的地方,如何使我的范围正确:

https://jsfiddle.net/5n5pachh/3/

Factory.prototype.addUserField = function(name) {
    this.Product.prototype[name] = {};

    // Using a classic function does not work because ...
    this.Product.prototype[name].create = function(login) {
        console.dir(this); // ... 'this' is bound to this.Product.prototype[name]
    }

    // Using an arrow function does not work because ...
    this.Product.prototype[name].create = function(login) {
        console.dir(this); // ... 'this' is bound to Factory.prototype.addUserField
    }

    // None of the above functions work how I want them to, because they can't
    // access the products doc field (i.e.: this.doc)

    return this;
}

(如何)是否可以将 createdeleteById 函数的 this 关键字绑定到我的 obj 实例?

您只需要使用 bindthis 作用域绑定到您的函数上。如果我明白你想要 this 代表什么,这就意味着将 .bind(this.Product); 标记到你的函数的末尾:

this.Product.prototype[name].create = function(login) {
    console.dir(this); 
}.bind(this.Product);

但我认为这不能完全解决您的问题 - 当您调用 addUserField 时,还没有 Product 的实例供您绑定。所以你从上面得到的是 this 引用 Product 的定义,而不是你的带有 doc 的实例。为此,您需要重构代码。

这里有一个解决方案,可以将您的工厂更改为实际创建 Product 的实例,与您的不完全相同,但希望能满足相同的要求

function Factory() {
        
    this.createProduct = function(doc){
       var product = {doc:doc};
       userFields.forEach(function(uf){
           product[uf.name] = {};
           product[uf.name].create = uf.create.bind(product) ;
       })
       return product;
    }
    
    var userFields = [];
    this.addUserField = function(name){
      userFields.push({
           name: name,
           create: function(login){
              console.dir(this.doc); 
           }
       }) ;
       return this;
    }
}



// Use case
var obj = new Factory().addUserField("users").createProduct({foo:"bar"});
console.log(obj.doc)
obj.users.create();