3

我正在尝试构建自己的类似 jquery 的小型库,但在创建这种链接模式时我遇到了非常艰难的时期。基本上,我有一个类,其中包含一堆方法,可以更轻松地操作文档。这是一个例子

function MF(selector){
    var DO; // Stands for DocumentObject
    this.select = function(selector){
        return document.getElementById(selector);
    }
    if(typeof selector === 'string'){
        DO = this.select(selector);
    }else if(selector instanceof HTMLElement){
        DO = selector;
    }

    this.children = function children(selector){
        return DO.getElementsByClassName(selector);
    }
    return {
        MF: ???
    }
}(null);

我的思考可能是错误的,但我发现为了为文档对象( html 元素)提供额外的方法,我需要扩展 HTMLElement 原型或将元素与我的类一起传递。我选择了第二个选项。我只是不知道在我的课堂上要返回什么,以便我可以继续进行链接。就本示例而言,我的目标只是能够编写以下代码行:

MF('someDiv').children('someClass');

在绝望的尝试中,我尝试返回一个MF默认情况下不应该有实例的新实例,并导致自己陷入无限循环。我真的不知道我应该回到那里。任何帮助是极大的赞赏!

4

4 回答 4

2

return this;将允许访问构造函数的方法。如果方法不需要返回另一个值,则在构造函数的最底部和属于它的每个方法的最底部执行此操作。

function MF(selector){
  var doc = document;
  this.select = function(selector){
    return doc.getElementById(selector);
  }
  // there are problems with some of your code
  this.someMethod = function(){
    /* do stuff - If you want to access an Element then
      var thisIsNowGlobal = this.select('someId');
      thisIsNowGlobal.innerHTML = 'someText';
      Note, the keyword this is global not var
      If you wrote this.select('someId').innerHTML the property would not exist

      When a property of an Object is assigned to a variable or argument
      the this value changes to the global context.
   */
    return this;
  }
  return this;
}
于 2013-11-02T23:19:26.987 回答
2

看起来您也尝试使用模块模式,并且没有利用原型..

当你想要链接某些东西时,你要么需要this在可链接的方法中返回它自己(),要么返回你的Objectnew的一个实例。在下面的示例中,我通过使用新实例来实现链接(孩子看起来需要是一个列表,所以我做了一个数组)。

var MF = (function () { // module pattern start
    function MF(selector) {
        if (!(this instanceof MF)) return new MF(selector); // always construct
        this.node = null; // expose your DO
        if (typeof selector === 'string') {
            this.node = document.getElementById(selector);
        } else if (selector instanceof HTMLElement) {
            this.node = selector;
        } else {
            throw new TypeError('Illegal invocation');
        }
    }
    MF.prototype = {}; // set up any inheritance
    MF.prototype.select = function (selector) {
        return new MF(document.getElementById(selector)); // returns new instance
    };
    MF.prototype.children = function (selector) {
        var MFs = [],
            nodes = this.node.getElementsByClassName(selector),
            i;
        for (i = 0; i < nodes.length; ++i) {
            MFs[i] = new MF(nodes[i]);
        }
        return MFs; // array of items of new instances
    };
    return MF; // pass refence out
}()); // module pattern end

然后,例如,你可以链式..

MF(document.body).children('answer')[0].children('post-text')[0].node;
于 2013-11-02T23:42:08.157 回答
1

你可以很容易地做到这一点。请记住,如果您在其上定义了方法,return this那么this您可以按顺序调用它们。

var MyUtilThing = function(){};
MyUtilThing.prototype.doStuff = function doStuff (){ // give it a name, helps in debugging
  // do your thing
  console.log('doing stuff');
  return this; // this is your instance of MyUtilThing
}

var thing = new MyUtilThing();
thing.doStuff().doStuff().doStuff(); // etc

必须显式创建实例的一种方法是在构造函数中执行此操作。

var MyUtilThing = function(selector){
  var F = function(){};
  F.prototype = MyUtilThing.prototype;
  var toReturn = new F();
  toReturn.initialize(selector);
  return toReturn;
};

MyUtilThing.prototype.initialize = function initialize(selector){
  this.selector = selector;
};

MyUtilThing.prototype.doStuff = function doStuff (){ // give it a name, helps in debugging
  // do your thing
  console.log('doing stuff to', this.selector);
  return this; // this is your instance created in the constructor (the blank function with the same prototype as MyUtilThing)
}

var thing = MyUtilThing('div'); // no use of new here!
thing.doStuff().doStuff().doStuff(); // etc

但是,在那里进入一些稍微沉重的领域。最好的办法是尝试并准确了解thisJS 中的使用方式,您将获得很长的路要走。

于 2013-11-02T23:43:36.227 回答
1

传统上,jQuery 启用链接的方式是为每种类型的返回值创建一个包装器对象。例如,在您的情况下,为以下内容创建自己的包装器是值得的HTMLElement

function HTMLElementWrapper(element) {
    if (element instanceof HTMLElementWrapper) return element;
    this.element = element;
}

现在您有了一个,您可以按如下HTMLElementWrapper方式重构您的函数:MF

function MF(selector) {
    return new HTMLElementWrapper(typeof selector === "string" ?
        document.getElementById(selector) : selector);
}

MF函数现在返回一个HTMLElementWrapper具有两个方法select和的对象children

HTMLElementWrapper.prototype.select = function (selector) {
    return new HTMLElementWrapper(this.element.getElementById(selector));
};

HTMLElementWrapper.prototype.children = function (selector) {
    return new NodeListWrapper(this.element.getElementsByClassName(selector));
};

当然,要使children函数正常工作,您需要创建一个NodeListWrapper构造函数:

function NodeListWrapper(list) {
    if (list instanceof NodeListWrapper) return list;
    this.list = list;
}

现在您可以按如下方式链接方法:

MF("someDiv").select("something").children("someClass");

.children("someClass")您需要将这些方法添加到NodeListWrapper.prototype.

于 2013-11-02T23:59:06.813 回答