admin管理员组

文章数量:1180422

I'm trying to understand jQuery classes but it is not going very well.

My goal is to use a class this way (or to learn a better way to do it):

var player = new Player($("playerElement"));
player.InitEvents();

Using other people's examples, this is what I tried:

$.Player = function ($) {

};

$.Player.prototype.InitEvents = function () {

    $(this).keypress(function (e) {
        var key = e.which;
        if (key == 100) {
            MoveRight();
        }
        if (key == 97) {
            MoveLeft();
        }
    });
};

$.Player.prototype.MoveRight = function () {
    $(this).css("right", this.playerX += 10);
}

$.Player.prototype.MoveLeft = function () {
    $(this).css("right", this.playerX -= 10);
}

$.Player.defaultOptions = {
    playerX: 0,
    playerY: 0
};

The end goal is to have a character moving on the screen left and right using the keyboard letters A and D.

I have a feeling that I'm doing something very wrong with this "class" but I'm not sure why.

(sorry for my English)

I'm trying to understand jQuery classes but it is not going very well.

My goal is to use a class this way (or to learn a better way to do it):

var player = new Player($("playerElement"));
player.InitEvents();

Using other people's examples, this is what I tried:

$.Player = function ($) {

};

$.Player.prototype.InitEvents = function () {

    $(this).keypress(function (e) {
        var key = e.which;
        if (key == 100) {
            MoveRight();
        }
        if (key == 97) {
            MoveLeft();
        }
    });
};

$.Player.prototype.MoveRight = function () {
    $(this).css("right", this.playerX += 10);
}

$.Player.prototype.MoveLeft = function () {
    $(this).css("right", this.playerX -= 10);
}

$.Player.defaultOptions = {
    playerX: 0,
    playerY: 0
};

The end goal is to have a character moving on the screen left and right using the keyboard letters A and D.

I have a feeling that I'm doing something very wrong with this "class" but I'm not sure why.

(sorry for my English)

Share Improve this question edited Feb 10, 2017 at 11:15 Renaat De Muynck 3,3471 gold badge23 silver badges21 bronze badges asked Jan 19, 2013 at 19:50 samysamy 1,9697 gold badges40 silver badges64 bronze badges 4
  • this inside the instance methods references the instance object itself so you can't use $(this).keypress, $(this).css etc as this doesn't reference a DOM element nor query string. Your function calls are wrong too, it should read this.MoveRight() but as you're inside of a jQuery handler which sets the this context to the DOM element itself, you will need to assign the instance's this to a variable a level up in the scope chain so you can access it inside the handler to call its MoveRight/MoveLeft methods. – Fabrício Matté Commented Jan 19, 2013 at 19:57
  • can you show me how you will change my code to fix the problem i made? it will be much easier for me to see what i did worng if i could compre a good code vs mine. (if is not too hard) – samy Commented Jan 19, 2013 at 20:03
  • I can try man, but it is closer to a complete rewrite than a "fix". :P I'll see if I can make a simple example. – Fabrício Matté Commented Jan 19, 2013 at 20:04
  • Here's the commented version jsfiddle.net/gVhj8 – Fabrício Matté Commented Jan 19, 2013 at 20:26
Add a comment  | 

2 Answers 2

Reset to default 23

An important issue is that you have to assign the passed jQuery object/element to a this.element - or another this.propertyName - so you can access it later inside the instance's methods.

You also cannot call MoveRight()/MoveLeft() directly like that because those functions are not defined up in the scope chain, but rather in the prototype of your instance's Constructor, hence you need a reference to the instance itself to call these.

Updated and commented code below:

(function ($) { //an IIFE so safely alias jQuery to $
    $.Player = function (element) { //renamed arg for readability

        //stores the passed element as a property of the created instance.
        //This way we can access it later
        this.element = (element instanceof $) ? element : $(element);
        //instanceof is an extremely simple method to handle passed jQuery objects,
        //DOM elements and selector strings.
        //This one doesn't check if the passed element is valid
        //nor if a passed selector string matches any elements.
    };

    //assigning an object literal to the prototype is a shorter syntax
    //than assigning one property at a time
    $.Player.prototype = {
        InitEvents: function () {
            //`this` references the instance object inside of an instace's method,
            //however `this` is set to reference a DOM element inside jQuery event
            //handler functions' scope. So we take advantage of JS's lexical scope
            //and assign the `this` reference to another variable that we can access
            //inside the jQuery handlers
            var that = this;
            //I'm using `document` instead of `this` so it will catch arrow keys
            //on the whole document and not just when the element is focused.
            //Also, Firefox doesn't fire the keypress event for non-printable
            //characters so we use a keydown handler
            $(document).keydown(function (e) {
                var key = e.which;
                if (key == 39) {
                    that.moveRight();
                } else if (key == 37) {
                    that.moveLeft();
                }
            });

            this.element.css({
                //either absolute or relative position is necessary 
                //for the `left` property to have effect
                position: 'absolute',
                left: $.Player.defaultOptions.playerX
            });
        },
        //renamed your method to start with lowercase, convention is to use
        //Capitalized names for instanceables only
        moveRight: function () {
            this.element.css("left", '+=' + 10);
        },
        moveLeft: function () {
            this.element.css("left", '-=' + 10);
        }
    };


    $.Player.defaultOptions = {
        playerX: 0,
        playerY: 0
    };

}(jQuery));

//so you can use it as:
var player = new $.Player($("#playerElement"));
player.InitEvents();

Fiddle

Also note that JavaScript does not have actual "classes" (at least not until ES6 gets implemented) nor Methods (which by definition are associated exclusively to Classes), but rather Constructors which provide a sweet syntax that resembles classes. Here's an awesome article written by TJ Crowder regarding JS's "fake" methods, it is a little advanced but everyone should be able to learn something new from reading it:
http://blog.niftysnippets.org/2008/03/mythical-methods.html

When you use this inside your Player prototype functions, this points to the current Player object.

But when you use $(this).keypress it requires that this points to an HTML element.

The two simply are incompatible. There is only one this and it points to the current Player object, not to an HTML element.

To fix your problem, you will need to pass the HTML element into the Player object upon its creation or into the relevant function calls.

You can pass the element into the Player object upon construction like this:

$.Player = function ($, element) {
        this.element = element;

};

$.Player.prototype.InitEvents = function () {

    $(this.element).keypress(function (e) {
        var key = e.which;
        if (key == 100) {
            MoveRight();
        }
        if (key == 97) {
            MoveLeft();
        }
    });
 };

 $.Player.prototype.MoveRight = function () {
     $(this.element).css("right", this.playerX += 10);
 }

 $.Player.prototype.MoveLeft = function () {
     $(this.element).css("right", this.playerX -= 10);
 }

$.Player.defaultOptions = {
    playerX: 0,
    playerY: 0
};

本文标签: Creating a simple JavaScript class with jQueryStack Overflow