使用自定义原型函数找不到的方法

时间:2016-02-01 20:10:55

标签: javascript jquery prototype

我需要创建一个几乎像jQuery的自定义原型。在几个Google搜索术语之后,我设法遇到了一些我发现的代码:

var myQuery, $;

(function() {

myQuery = $ = function(selector) {
    return new MyQuery(selector);
};

var MyQuery = function(selector) {
    // Lets make a really simplistic selector implementation for demo purposes
    var nodes = document.getElementsByTagName(selector);
    for (var i = 0; i < nodes.length; i++) {
        this[i] = nodes[i];
    }
    this.length = nodes.length;
    return this;
};

// Expose the prototype object via myQuery.fn so methods can be added later
myQuery.fn = MyQuery.prototype = {
    // API Methods
    hide: function() {
        for (var i = 0; i < this.length; i++) {
            this[i].style.display = 'none';
        }
        return this;
    },
    remove: function() {
        for (var i = 0; i < this.length; i++) {
            this[i].parentNode.removeChild(this[i]);
        }
        return this;
    },
    hideAll: function(){
      alert("Hiding all.");  
    }
    // More methods here, each using 'return this', to enable chaining
};

}());

代码完美无缺,但我需要自定义它。在我这样做的那一刻:

myQuery("p").hide();

页面上隐藏了所有<p>个标记。但是当我这样做时:

myQuery.hideAll();

我收到错误: TypeError:myQuery.hideAll不是函数

我真的只需要能够允许选择器,有时候我不需要通过选择器发送。

谢谢!

3 个答案:

答案 0 :(得分:1)

Based on the initial answer you accepted, do not assign the entire prototype to your Constructor as static methods! Your prototypical methods are meant to be used for instances of "MyOuery" only.

Don't use this line:

Object.assign(myQuery, MyQuery.prototype);

What that line does is reassign all of the prototype methods onto the actual constructor as static methods. What I mean by this is that now you can do this:

myQuery.hide() // That method should not be available at that level
// If you were to call that code right now, you would get this error:
// Uncaught TypeError: Cannot read property 'style' of undefined

The hide() method should only be available to instances of MyQuery. You create an instance of MyQuery whenever you call the constructor with a query selector or tag. In other words: $('div'). But as you can see, that method is available where it should not be, and is creating errors and potentially bugs for you in the future.

The correct way to do this:

You need to create a static method on the MyQuery constructor itself, instead of on the prototype.

Take this rework of your code. I've only made a few changes.

(function () {
    function MyQuery(selector) {
        if (!(this instanceof MyQuery)) {
            return new MyQuery(selector);
        }

        var nodes = document.querySelectorAll(selector);
        for (var i = 0; i < nodes.length; i++) {
            this[i] = nodes[i];
        }

        this.length = nodes.length;
    }

    // Create the static method here
    MyQuery.hideAll = function () {
        var all = document.querySelectorAll('*');
        for (var i = 0; i < all.length; i++) {
            all[i].style.display = 'none'
        }
    };

    MyQuery.fn = MyQuery.prototype = {
        hide: function () {
            for (var i = 0; i < this.length; i++) {
                this[i].style.display = 'none'
            }
            return this;
        },
        remove: function () {
            for (var i = 0; i < this.length; i++) {
                this[i].parentNode.removeChild(this[i]);
            }
            return this;
        }
    };

    window.myQuery = window.$ = MyQuery;
})();

Here is what you can, and can't do with this code:

// You can now do this anytime:
$.hideAll();

// But you cannot do this, because the method isn't even available to it
// This gives the following expected error: Uncaught TypeError: $.hide is not a function
$.hide(); // Can't do it from this context! Yay!

Once again, the methods that are on the prototype in this case are for instances of MyQuery that need to be used. So, those methods should only be available when instantiating the constructor with a selector.

var divs = $('div');
divs.hide().remove(); // Or some other variation.

I hope this helps you understand the architecture of your library and the difference between the static methods and the prototypical ones.

答案 1 :(得分:0)

hideAll是MyQuery对象的函数,而不是myQuery。

我想你应该试试 更改为MyQuery()hideAll();

myQuery()将返回一个MyQuery对象

答案 2 :(得分:-1)

如果你想让myQuery.hideAll在这里工作,那就是快速的解决方案:

...
    },
    hideAll: function(){
        alert("Hiding all.");  
    }
    // More methods here, each using 'return this', to enable chaining
};
// you need this line so you can also access the functions without calling myQuery as a function
Object.assign(myQuery, MyQuery.prototype);

你现在不能调用myQuery.hideAll(),因为hideAll不是myQuery中的函数,而是在MyQuery.prototype中(因此你需要一个MyQuery实例,你可以通过调用myQuery()来获得。

对象只将MyQuery.prototype中的字段副本分配给myQuery,这样您就可以在不需要实例的情况下访问它们。

LE:这是一个工作小提琴:https://jsfiddle.net/udjejgzj/