理解链接函数,如何返回jquery-like元素本身,如何避免“...不是函数”

问题描述 投票:-1回答:2

完全实验性的,仅用于学习我试图重新发明轮子,一个叫做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()

我的问题是:

  • [已解决]我如何让这个函数像var elm = $('div#test');一样返回jquery-like元素本身,现在我回到了整个对象。
  • [已解决](请参阅下面的评论)我如何编写一个回退函数,以防止获取$(...).animate is not a function等错误消息,如果我使用的是当前未在此函数中涵盖的方法,如$('div#test').addClass('newclass').animate({top:'2.3em',background:'red'});,因为animate()未实现在这一刻。在php有一个__get()函数,javascript中有类似的东西吗?

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

谢谢。

javascript jquery chained
2个回答
2
投票

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

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的奇怪的黑客攻击(仍然适用于2019年),即使它没有做它在数组上做的事情......或者做任何事情:

class MyElementCollection {
    // ⋮

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

我如何编写一个回退函数,以防止像$(...).animate这样的错误消息不是函数,如果我使用的是当前未在此函数中涵盖的方法,如$('div#test').addClass('newclass').animate({top:'2.3em',background:'red'});,因为此时animate()尚未实现。在php有一个__get()函数,javascript中有类似的东西吗?

你可以用JavaScript中的proxies做到这一点,但我不推荐它 - 它不是很干净。 (您的对象将假装拥有所有属性,而不仅仅是您尚未实现的jQuery中存在的属性,并且不会在原型上找到它们的列表。代理也会带来性能和可理解性惩罚。) ,考虑为所有未实现的东西分配相同的实现:

class MyElementCollection {
    // ⋮
}

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

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

1
投票

看看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 collection创建一个新的jQuery.prototype(这是他们在文档中使用的术语)。该原型具有所有常用的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也是prototypejQuery.fn.init(不要被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,但有一个自定义的prototype,它附加了所有的jQuery方法。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.