Mercurial > js4py
diff source/javascript-objects.rst @ 94:454054afcc0a europython-2011
$ cp -piv ../python-classes-tutorial/source/javascript-objects.rst source/ # and some other changes
| author | Jonathan Fine <jfine@pytex.org> |
|---|---|
| date | Sun, 19 Jun 2011 20:24:03 +0100 |
| parents | |
| children | 4e6cfa066d1a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/javascript-objects.rst Sun Jun 19 20:24:03 2011 +0100 @@ -0,0 +1,268 @@ +JavaScript objects +================== + +Like Python classes +------------------- + +In JavaScript all objects are part of an inheritance tree. The +**create** function adds a node to the inheritance tree. + +.. code-block:: javascript + + // A JavaScript object. + js> root = {} + + // Prototype inheritance. + js> create = function (obj) { + var f = function () {return this;}; + f.prototype = obj; + return new f; + } + + js> a = create(root) + js> b = create(a) + + js> a.name = 5 + js> a.name + 5 + js> b.name + 5 + +In Python classes inherit in the same way. + +.. doctest:: + + >>> root = type # Most classes are instance of type. + >>> class a(root): pass + >>> class b(a): pass # Class inheritance. + + >>> a.name = 5 # Just like JavaScript. + >>> a.name + 5 + >>> b.name + 5 + +class explanation +^^^^^^^^^^^^^^^^^ + +In Python we can subclass anything whose type is **type** (or a +subclass of type). A subclass (and its instances) inherits properties +from the super-class. + + >>> type(root) == type(a) == type(b) == type + True + + + +Custom item methods +------------------- + +In JavaScript attribute and item access are the same. + +.. code-block:: javascript + + js> a = create(root) + + js> a.name = 5 + js> a['name'] + 5 + + js> a['key'] = 6 + js> a.key + 6 + + js> a[1] = 6 + js> a['1'] + 6 + +In Python we can defined our own item methods. (The programmer owns +the dot.) + +.. doctest:: + + >>> class A(object): + ... + ... def __getitem__(self, key): + ... return getattr(self, str(key)) + ... def __setitem__(self, key, value): + ... return setattr(self, str(key), value) + + >>> a = A() + >>> a.name = 5 + + >>> a['name'] + 5 + + >>> a['key'] = 6 + >>> a.key + 6 + + >>> a[1] = 6 + >>> a['1'] + 6 + +Because **type(a)** is **A**, which has the special item methods, we +get the special item behaviour. + +.. doctest:: + + >>> type(a) is A + True + + +On metaclass +------------ + +Using previous definition, we cannot subclass **a** to create **b**. + +.. doctest:: + + >>> class b(a): pass + Traceback (most recent call last): + class b(a): pass + TypeError: Error when calling the metaclass bases + object.__new__() takes no parameters + +This is because **a** is not a type. The solution involves Python +metaclasses (an advanced topic). + +.. doctest:: + + >>> isinstance(a, type) + False + + +metaclass construction +^^^^^^^^^^^^^^^^^^^^^^ +We will subclass type, not object, and add to it the special item +methods. + + +.. doctest:: + + >>> class ObjectType(type): + ... + ... def __getitem__(self, key): + ... return getattr(self, str(key)) + ... + ... def __setitem__(self, key, value): + ... return setattr(self, str(key), value) + +Here is a fancy way of calling **ObjectType**. + + .. doctest:: + + >>> class root(object): + ... __metaclass__ = ObjectType + + +Here is a more direct (and equivalent) construction (create an +instance of **ObjectType**, whose instances are objects). + +.. doctest:: + + >>> root = ObjectType('root', (object,), {}) + >>> isinstance(root(), object) + True + +metaclass demonstration +^^^^^^^^^^^^^^^^^^^^^^^ +.. doctest:: + + >>> class a(root): pass + >>> class b(a): pass + + >>> a.name = 5 + >>> a.name + 5 + >>> b.name + 5 + >>> a['name'] + 5 + >>> b['name'] + 5 + + >>> a[1] = 6 + >>> a['1'] + 6 + + +metaclass explanation +^^^^^^^^^^^^^^^^^^^^^ + +Because **type(root)** is a subclass of type we can subclass root. + +.. doctest:: + + >>> issubclass(type(root), type) + True + +Because the **type(root)** is **ObjectType**, which has special item +methods, we get the special item behaviour. + +.. doctest:: + + >>> type(root) == type(a) == type(b) == ObjectType + True + + +Never instantiated +------------------ + +We can't call JavaScript objects (unless they are a function). But +**create** creates ordinary JavaScript objects. + +.. code-block:: javascript + + js> a = create(root) + js> a(1, 2, 3) + TypeError: a is not a function + + +We will monkey-patch the previous Python class, to provide custom +behaviour when called. + +.. doctest:: + + >>> def raise_not_a_function(obj, *argv, **kwargs): + ... raise TypeError, obj.__name__ + ' is not a function' + + >>> ObjectType.__call__ = raise_not_a_function + + >>> a(1, 2, 3) + Traceback (most recent call last): + a(1, 2, 3) + TypeError: a is not a function + + +Conclusion +---------- + +JavaScript objects are like Python classes (because they inherit like +Python classes). + +For JavaScript attribute and item access are the same. This is +achieved in Python by providing custom item methods. + +In Python the custom item methods must be placed on the type of the +object (or a superclass of its type). + +Ordinary JavaScript objects are not functions and cannot be called. A +Python class can be called (to create an instance of the object). But +we can override this behaviour by supplying a custom method for call. + +To summarize: +.. + + **JavaScript objects are like Python classes with custom item + methods (on the metaclass) that are never instantiated.** + +It's worth saying again: + +.. + + **JavaScript objects are like Python classes with custom item + methods (on the metaclass) that are never instantiated.** + + +
