【问题标题】:understanding chained functions, how to return jquery-like the element itself, how avoid "... is not a function"了解链式函数,如何返回类似 jquery 的元素本身,如何避免“......不是函数”
【发布时间】:2019-06-28 10:01:37
【问题描述】:

完全是实验性的,仅用于学习我正在尝试重新发明轮子,一个名为 jquery 的轮子。这样做的最初目的是了解可链接的函数。 所以我写了一个这样做的函数,就我所见,非常好,就像这样:(有点缩短)

var $ = function(s) {
    var el = document.querySelectorAll(s);

    var myobj = {
        addClass(className) {
            el.forEach(elem => {elem.classList.add(className)});
            return this;
        },
        toggleClass(className) {
            el.forEach(elem => {elem.classList.toggle(className)});
            return this;
        },
        removeClass(className) {
            el.forEach(elem => {elem.classList.remove(className)});
            return this;
        },
        html(html) {
            if (typeof html == 'string') {
                el.forEach(elem => {elem.innerHTML = html});
                return this;
            } else {
                return el[0].innerHTML;
            }
        }
    };

    return myobj;
};

(这确实是一个工作示例)现在我可以使用类似 jquery 的语法来操作元素(或元素的节点列表)的类和 html,如下所示:$('#test').html('new html content'); 或像 $('a').html('my new link').addClass('newclassname'); 一样链接,即真的很酷。

如果有兴趣,这里是完整的script snapshot of today,包括css()attr(),还包括缺少的jquery函数id()

我的问题是:

  • [已解决]我怎样才能让这个函数像 jquery 一样返回像 var elm = $('div#test'); 这样的元素本身,现在我要取回整个对象。

  • [已解决] (请参阅下面的 cmets) 我如何编写一个备用函数,以防止收到像 $(...).animate is not a function 这样的错误消息,如果我我正在使用此功能当前未涵盖的方法,例如$('div#test').addClass('newclass').animate({top:'2.3em',background:'red'});,因为目前尚未实现animate()。在php 中有一个__get() 函数,在javascript中有类似的吗?

请不要问我为什么要做这一切,因为我提到这是为了学习和理解

谢谢。

【问题讨论】:

  • 不确定你的第一个问题是什么意思——当你使用 jQuery 时,$('div#test') 将返回一个 jQuery 集合而不是单个 DOM 元素。 jQuery 集合在概念上与您的 myobj 几乎相同(尽管它的构建方式不同)。
  • @David 这不是 jQuery。 OP 正在尝试编写自己的代码来模仿 jQuery 的行为,以此作为一种学习努力。
  • @TylerRoper 是的,我知道。但我仍然不明白他所说的“……元素本身……”是什么意思
  • 使用 jQuery 也不会得到“只是元素本身”——它是一个 jQuery 对象(试试$('div#test') instanceof jQuery)。也许您指的是它在控制台中的外观,例如元素数组?
  • @freedomn-m 我从来不知道__noSuchMethod。我打算建议a Proxy to capture all gets。编辑:似乎__noSuchMethod__ 是非标准的,也被标记为过时的。所以代理可能是更好的选择。

标签: javascript jquery chained


【解决方案1】:

我怎样才能让这个函数像 jquery 一样返回像 var elm = $('div#test'); 这样的元素本身,现在我要返回整个对象。

jQuery 总是给你一个完整的 jQuery 对象。如果您指的是您定义的方法(addClass,...)如何不与对象一起出现在浏览器控制台中,这是通过与 prototype 共享这些属性来实现的,而不是创建一组新的每个对象的功能。 ES6 类是创建构造函数的便捷方式,允许您使用方法定义原型并创建继承自它的对象:

class MyElementCollection {
    constructor(el) {
        this.el = el;
    }

    addClass(className) {
        this.el.forEach(elem => {elem.classList.add(className)});
        return this;
    }

    // ⋮
}

var $ = function(s) {
    var el = document.querySelectorAll(s);

    return new MyElementCollection(el);
};

然后,如果您希望它像 Firefox 上的数组一样显示,并且像 jQuery 一样通过索引 $('div#test')[0] 访问元素,则将元素分配给这些索引并为您的集合指定 length

class MyElementCollection {
    constructor(el) {
        el.forEach((elem, i) => {
            this[i] = elem;
        });

        this.length = el.length;
    }

    each(action) {
        for (let i = 0; i < this.length; i++) {
            action(this[i]);
        }
    }

    addClass(className) {
        this.each(elem => {elem.classList.add(className)});
        return this;
    }

    // ⋮
}

然后,如果您希望它在 Chrome 上显示为数组,您可以执行 defining a splice method 的奇怪 hack(在 2019 年仍然适用),即使它不会做与数组相同的事情……或者做任何事:

class MyElementCollection {
    // ⋮

    splice() {
        throw new Error('Not implemented');
    }
}

如果我正在使用当前未包含在此函数中的方法,例如 $('div#test').addClass('newclass').animate({top:'2.3em',background:'red'});,我如何编写一个后备函数,以防止出现错误消息,例如 $(...).animate is not a function,因为 animate() 不是暂时没有实施。在php 中有一个__get() 函数,在javascript中有类似的吗?

可以在 JavaScript 中使用 proxies 来执行此操作,但我不推荐它 - 它不是很干净。 (您的对象将假装拥有所有属性,而不仅仅是 jQuery 中存在的那些您尚未实现的属性,并且不会在原型上找到它们的列表。代理也会带来性能和可理解性的损失。)取而代之的是,考虑为所有未实现的东西分配相同的实现:

class MyElementCollection {
    // ⋮
}

const notImplemented = () => {
    throw new Error('Not implemented');
};

['animate', …].forEach(name => {
    MyElementCollection.prototype[name] = notImplemented;
});

【讨论】:

  • 抱歉这么晚了,但感谢您如此耐心地教我。为此类模型 +1。
【解决方案2】:

查看 jquery 的 github repo。相关文件如下:

https://github.com/jquery/jquery/blob/master/src/core.js

https://github.com/jquery/jquery/blob/master/src/core/init.js

jQuery 做的事情比我在这里做的更复杂,所以这只是一个基本的解释。

jQuery 函数(以及它的 $ 别名)在其函数体中调用 new jQuery.fn.init(selector, root)。这会从jQuery.prototype 创建一个新的jQuery collection(这是他们在文档中使用的术语)。该原型附加了所有常用的 jQuery 方法(如onfindanimate 等)。

他们这样做:

function jQuery (selector, root) {
  return new jQuery.fn.init(selector, root);
}

jQuery.fn = jQuery.prototype = { /* skipped */ }

jQuery.fn.init = function (selector, root) { /* skipped */ }
jQuery.fn.init.prototype = jQuery.fn;

这里的关键是最后一行,他们在其中使jQuery.prototype 也成为jQuery.fn.initprototype(不要被fn 混淆,这只是他们使用的另一个别名,所以他们不会必须一直输入prototype)。

当您扫描jQuery.fn.init 构造函数时,您会注意到它们按索引将所有匹配的DOM 元素存储到新集合中。简化:

jQuery.fn.init = function (selector, root) {
  let elms = Array.from(root.querySelectorAll(selector));
  elms.forEach((el, index) => {
    this[index] = el;
  }, this);
}

他们在幕后做了更多的事情,但这基本上解释了 jQuery 集合是什么:一种自定义类型的对象,部分看起来像 NodeList,但 带有自定义 @ 987654342@ 附加了所有的 jQuery 方法.

这意味着,您的第一个问题是基于错误的假设:使用$('div#test') 将返回一个带有单个元素的jQuery 集合。你将永远从 jQuery 获得“原始”DOM 节点。

至于您的第二个问题:您可以为此使用代理。

【讨论】:

  • 对于第一个问题,在您回答之前,它已经解决了一点。但是 +1 因为正确答案(对我来说是可以理解的)好的,回到第二个问题:我已经让代理的东西正常工作了。我可以让函数通过(如果存在)或获取自定义消息。但我仍然在控制台上收到浏览器错误消息并破坏其余代码。这是实际的测试脚本(非常短)ddlab.de/stackoverflow/jquery-like-chained-js-functions/…
  • 第二个问题也解决了: 难度不是很大,只是有时候新东西需要一点点理解。代理的东西终于帮了我。真正的魔法发生在处理程序中,在 else 部分:如果传递的属性不是一个函数,那么从现在开始让它成为一个,它只创建自定义消息,但抑制javascript错误和代码中断。我现在对此感到非常高兴。再次感谢那些把我带到代理(或反向)的人。这是最后的ddlab.de/stackoverflow/jquery-like-chained-js-functions/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多