Mercurial > js4py-pl
changeset 195:48d35d049b19
$ hg merge core-javascript # into default, ready for ReadTheDocs
| author | Jonathan Fine <jfine@pytex.org> |
|---|---|
| date | Mon, 13 Feb 2012 10:05:46 +0000 |
| parents | f6e1dca9ff21 (current diff) 77e87f1b5811 (diff) |
| children | 4456cb0c4d7c |
| files | source/inheritance.rst |
| diffstat | 46 files changed, 3293 insertions(+), 132 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Wed Oct 19 11:45:25 2011 +0100 +++ b/.hgignore Mon Feb 13 10:05:46 2012 +0000 @@ -3,6 +3,8 @@ \#* *.orig *.pyc +*.zip +*.exe # Generated by LaTeX Beamer. *.aux @@ -15,7 +17,6 @@ *.vrb syntax:re - ^local/ ^build/ - +core-javascript/_build/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/Makefile Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,139 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make <target>' where <target> is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + -rm -rf core-javascript-work.zip + zip -r core-javascript-work.zip core-javascript-work + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + -cp -r core-javascript-work/ $(BUILDDIR)/html/ + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CoreJavaScript.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CoreJavaScript.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/CoreJavaScript" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/CoreJavaScript" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +gettext: + $(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt."
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/arguments.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,21 @@ +Arguments +========= + +This can be omitted at a first reading. The main point here is that +in JavaScript + +#. There is no checking for number and type of arguments. + +#. The arguments actually supplied are stored in a special variable, + called *arguments*. + +#. The value of *arguments* is almost, but not exactly, an array. + + +Test code +--------- + +.. literalinclude:: /core-javascript-work/tutorial.js + :language: javascript + :start-after: start return_arguments + :end-before: end return_arguments
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/bind-is-transient.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,69 @@ +Bind is transient +================= + + +Test code +--------- + +Here's the test code for attribute bind. + +.. literalinclude:: /core-javascript-work/tutorial.js + :language: javascript + :start-after: start transient-bind + :end-before: end transient-bind + + +Discussion +---------- + +We create an object that has a *return_this* method. The outcome of +calling *return_this* depends on how it is called. + +There's a logic in the test code that produces contradictory, or at +least surprising, outcomes. + +From + +.. code-block: javascript + + obj.return_this === return_this; + method_deferred === obj.return_this; + method_deferred === return_this; + +we would expect + +.. code-block: javascript + + obj.return_this() === return_this(); + method_deferred() === obj.return_this(); + method_deferred() === return_this(); + +But this means that in *obj.return_this()* the *obj* is not relevant. +But JavaScript (or its designers) wanted objects to have methods, so +they introduced a special rule. + + +Explanation +----------- + +JavaScript does not distinguish classes and instances. Python, and +perhaps other dynamic languages, does. This allows Python to supply a +bound method, which retains a reference to the object, when a function +belonging to the class is called on an instance. + +Here's a Python (version 3) command line dialogue that illustrates +this. + +>>> def method(): pass # The function. +... +>>> class A: method = method # The class. +>>> A.method # Function from class. +<function method at 0x15be518> +>>> A.method is method # Same as the original function. +True +>>> a = A() # Instance. +>>> a.method # Bound method, not original function. +<bound method A.method of <__main__.A object at 0x16b9d10>> + +The odd behaviour shown in the JavaScript test code is a consequence +of JavaScript not distinguishing between classes and instances.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/closures.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,86 @@ +Closures +======== + +You'll see closures a lot in JavaScript. The reasons for this are: + +#. In JavaScript, :doc:`bind-is-transient`. + +#. It is callback functions attached to DOM elements that respond to + events. + +#. Most code attaches callbacks to individual DOM elements (rather + than delegating to a parent element). + + +Test code +--------- + +Here's the test code for closures. + +.. literalinclude:: /core-javascript-work/tutorial.js + :language: javascript + :start-after: start closures + :end-before: end closures + + +Discussion +---------- + +The factory function creates two functions, *get* and *set*, which +refer to the variable *value* of the factory function. The *get* +function returns the value of this variable, while the *set* function +changes it. + +To test the factory function we create two get-set pairs. We then +test that each *set* operation changes the value return by its partner +*get* operation. + +Finally, we test that the two pairs don't interfere with each other. + + +Explanation +----------- + +Objects continue to exist so long as a reference remains to them. +When the last reference to an object is removed (or when the object is +part of *cyclic garbage*) the object can be destroyed. When the +return value of a function is stored (say as the value of a variable) +then this return value continues to exist. Similarly, anything +referred to by the return value continues to exist. + +Closures come about when the return value of a function is itself a +function defined within the outer function (or contains references to +such functions). + +Suppose the outer function returns an *inner function* (or in other +words a function that is defined within the outer function). Suppose +also that the inner function uses a variable (or parameter) of the +outer function. In this situation the inner function holds a +reference to the value of this variable. The code in the inner +function can read (or get) this value. It can also write to this +variable. + +One final point. Each call of the outer function creates a new +instantiation of the function variable. + + +Exercises +--------- + +A programmer, perhaps in a hurry, misses out a *var* in the example +above. So now it reads: + +.. code-block:: javascript + + var get_set_factory = function(){ + + value; // Here's the missing *var*. + + +#. Which tests to you expect to pass, and which to fail? + +#. Make this change and run the tests. Which actually fail? + +#. Explain what is happening. + +#. What does this tell us about coding and testing?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/conf.py Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*- +# +# Core JavaScript documentation build configuration file, created by +# sphinx-quickstart on Wed Oct 19 11:43:46 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Core JavaScript' +copyright = u'2011, Jonathan Fine' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.0' +# The full version, including alpha/beta/rc tags. +release = '0.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'CoreJavaScriptdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'CoreJavaScript.tex', u'Core JavaScript Documentation', + u'Jonathan Fine', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + +# Added at the end of the \documentclass[options]{...}. +# 'english' added to keep Babel quiet. +# http://groups.google.com/group/sphinx-dev/browse_thread/thread/ee301b14175ad5bb/c06c6600192a17da +latex_elements = { + 'classoptions': ',openany,english', +} + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'corejavascript', u'Core JavaScript Documentation', + [u'Jonathan Fine'], 1) +] + + +# -- Options for Epub output --------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'Core JavaScript' +epub_author = u'Jonathan Fine' +epub_publisher = u'Jonathan Fine' +epub_copyright = u'2011, Jonathan Fine' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +#epub_exclude_files = [] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/answers/closure-exercises.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,205 @@ +var something; + +TEST('anon', function() +{ + var answer; + + // start + answer = function(){ + return 1; + }; + // stop + + assert( answer() === 1 ); +}); + + +TEST('anon', function() +{ + var outer_function = function(){ + + var answer; + + // start + answer = function(){ + return 1; + }; + // stop + + return answer; + }; + + var my_inner_function = outer_function(); + + assert( typeof my_inner_function === 'function' ); + assert( my_inner_function() === 1 ); +}); + + +TEST('anon', function() +{ + var outer_function = function(){ + + var answer; + + // start + answer = function(){ + return 1; + }; + // stop + + return answer; + }; + + assert( outer_function()() === 1 ); +}); + + +TEST('anon', function() +{ + var outer_function = function(n){ + + var answer; + + // start + answer = function(){ + return n; + }; + // stop + + return answer; + }; + + assert( outer_function(1)() === 1 ); + assert( outer_function(2)() === 2 ); + assert( outer_function(3)() === 3 ); +}); + + +TEST('anon', function() +{ + var outer_function = function(n){ + + // start + return function(){ + return n; + }; + // stop + }; + + assert( outer_function(1)() === 1 ); + assert( outer_function(2)() === 2 ); + assert( outer_function(3)() === 3 ); +}); + + +TEST('anon', function() +{ + var value = 1; + + var get_value = function(){ + return value; + }; + + something = 1; + assert( get_value() === something ); +}); + + +TEST('anon', function() +{ + var value = 1; + + var set_value = function(n){ + value = n; + }; + + set_value(2); + something = 2; + assert( value === something ); +}); + + +TEST('anon', function() +{ + var value = 1; + + var get_value = function(){ + return value; + }; + + var set_value = function(n){ + value = n; + }; + + something = 1; + assert( get_value() === something ); + + set_value(2); + something = 2; + assert( get_value() === something ); +}); + + +TEST('anon', function() +{ + var value = 1; + + var ddd = function(aaa, bbb){ + return { + // start + aaa: aaa, + bbb: bbb + // stop + }; + }; + + var obj = ddd(1, 2); + + assert( obj.aaa === 1 ); + assert( obj.bbb === 2 ); + +}); + + +TEST('anon', function() +{ + + var get_set_factory = function(){ + + // start + var value; + + var get = function(){ + return value; + }; + + var set = function(new_value){ + value = new_value; + }; + // stop + + return { + get : get, + set : set + }; + }; + + var tmp; + tmp = get_set_factory(); + var get_a = tmp.get; + var set_a = tmp.set; + + tmp = get_set_factory(); + var get_b = tmp.get; + var set_b = tmp.set; + + + set_a('aaa'); + assert( get_a() == 'aaa' ); + + set_b('bbb'); + assert( get_b() == 'bbb' ); + + assert( get_a() == 'aaa' ); +});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/closure-exercises.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,168 @@ +var something; + +TEST('anon', function() +{ + var answer; + + // Please write your answer here. + + assert( answer() === 1 ); +}); + + +TEST('anon', function() +{ + var outer_function = function(){ + + var answer; + + // Please write your answer here. + + return answer; + }; + + var my_inner_function = outer_function(); + + assert( typeof my_inner_function === 'function' ); + assert( my_inner_function() === 1 ); +}); + + +TEST('anon', function() +{ + var outer_function = function(){ + + var answer; + + // Please write your answer here. + + return answer; + }; + + assert( outer_function()() === 1 ); +}); + + +TEST('anon', function() +{ + var outer_function = function(n){ + + var answer; + + // Please write your answer here. + + return answer; + }; + + assert( outer_function(1)() === 1 ); + assert( outer_function(2)() === 2 ); + assert( outer_function(3)() === 3 ); +}); + + +TEST('anon', function() +{ + var outer_function = function(n){ + + // Please write your answer here. + }; + + assert( outer_function(1)() === 1 ); + assert( outer_function(2)() === 2 ); + assert( outer_function(3)() === 3 ); +}); + + +TEST('anon', function() +{ + var value = 1; + + var get_value = function(){ + return value; + }; + + assert( get_value() === something ); +}); + + +TEST('anon', function() +{ + var value = 1; + + var set_value = function(n){ + value = n; + }; + + set_value(2); + assert( value === something ); +}); + + +TEST('anon', function() +{ + var value = 1; + + var get_value = function(){ + return value; + }; + + var set_value = function(n){ + value = n; + }; + + assert( get_value() === something ); + + set_value(2); + assert( get_value() === something ); +}); + + +TEST('anon', function() +{ + var value = 1; + + var ddd = function(aaa, bbb){ + return { + // Please write your answer here. + }; + }; + + var obj = ddd(1, 2); + + assert( obj.aaa === 1 ); + assert( obj.bbb === 2 ); + +}); + + +TEST('anon', function() +{ + + var get_set_factory = function(){ + + // Please write your answer here. + + return { + get : get, + set : set + }; + }; + + var tmp; + tmp = get_set_factory(); + var get_a = tmp.get; + var set_a = tmp.set; + + tmp = get_set_factory(); + var get_b = tmp.get; + var set_b = tmp.set; + + + set_a('aaa'); + assert( get_a() == 'aaa' ); + + set_b('bbb'); + assert( get_b() == 'bbb' ); + + assert( get_a() == 'aaa' ); +});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/counters.css Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,17 @@ +body { + background: #DDD; + font-family: sans-serif; +} + +#example { + padding: 20px; +} + +#example span { + padding: 10px; + margin: 10px; + border: 10px solid blue; + background: #DDF; + foreground: blue; + font-weight: bold; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/counters.html Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,18 @@ +<html> +<head> +<script src="library.js"></script> +<script src="counters.js"></script> +<link rel="stylesheet" type="text/css" href="counters.css" /> +<title>JS for Python: example: counters</title> +</head> +<body> +<h1>Counters</h1> + +<p>Click on a counter to increment its value.</p> + +<div id="example"> +<p>This will disappear if JavaScript is working properly.</p> +</div> + +<p>Return to documentation of <a href="../counters-example.html">Counters example</a>.</p> +</body>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/counters.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,79 @@ +(function() +{ + // Define a Counter class. + var counter = {}; // Prototype object for Counter. + + counter.__init__ = function(name){ + + this.name = name; + this.count = 0; + }; + + counter.onclick = function(event){ + + this.count ++; + }; + + counter.html = function(){ + + return this.name + ' ' + this.count; + }; + + Counter = SimpleClass(counter); + + + // Make explicit use of global variables. + var global = (function(){return this;})(); + + + // Interaction. + var onclick_factory = function(models){ + + var onclick = function(event){ + + event = event || global.event; // For IE event handling. + var target = event.target || event.srcElement; + var id = target.id; + if (id) { + var id_num = +id.slice(1); + var model = models[id_num]; + model.onclick(); + var html = model.html(); + if (html){ + global.document.getElementById(id).innerHTML = html; + } + } + }; + return onclick; + }; + + + // Set up the web page. + global.onload = function(){ + + var models = [ + Counter('apple'), + Counter('banana'), + Counter('cherry'), + Counter('date') + ]; + + var element = document.getElementById('example'); + + element.innerHTML = ( + '<span id="a0">apple 0</span>' + + '<span id="a1">banana 0</span>' + + '<span id="a2">cherry 0</span>' + + '<span id="a3">date 0</span>' + ); + + element.onclick = onclick_factory(models); + element = undefined; // Avoid IE memory leak. + }; + +})(); + + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/demo_testtools.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,14 @@ +TEST('all-pass', function() +{ + assert( 2 + 2 === 4 ); + assert( 'the ' + 'cat' === 'the cat'); +}); + + +TEST('2-4-fail', function() +{ + assert( true ); + assert( false ); + assert( 1 ); + assert( 0 ); +});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/exercise-1.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,14 @@ +var AAA = (function (BBB) +{ + BBB.fff = function(ccc) + { + return { + ggg: function(ddd) + { + return BBB.hhh(ddd, ccc.iii()); + } + }; + }; + return BBB; + +}(AAA || {}));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/exercise-2.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,11 @@ +var copy_attributes = function(tgt, src){ + + tgt.aaa = src.get_aaa(); + tgt.bbb = src.get_bbb(); + tgt.ccc = src.get_ccc(); + tgt.ddd = src.get_ddd(); + tgt.eee = src.get_eee(); + tgt.fff = src.get_fff(); + tgt.ggg = src.get_ggg(); + tgt.hhh = src.get_hhh(); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/library.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,25 @@ +// start create +var create = function(parent){ + + var tmp = function(){}; + tmp.prototype = parent; + var child = new tmp(); + + return child; +}; +// end create + + +// start SimpleClass +var SimpleClass = function(prototype){ + + var cls = function(){ + + var instance = create(prototype); + instance.__init__.apply(instance, arguments); + return instance; + }; + + return cls; +}; +// end SimpleClass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/solution-1-a.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,15 @@ +var AAA = function () +{ + var BBB = AAA || {}; + BBB.fff = function(ccc) + { + return { + ggg: function(ddd) + { + return BBB.hhh(ddd, ccc.iii()); + } + }; + }; + return BBB; + +}();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/solution-1-b.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,5 @@ +var AAA = function () +{ + var BBB = AAA || {}; + return BBB; +}();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/solution-1-c.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,1 @@ +var AAA = AAA || {};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/solution-1-d.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,11 @@ +var AAA = AAA || {}; + +AAA.fff = function(ccc) +{ + return { + ggg: function(ddd) + { + return AAA.hhh(ddd, ccc.iii()); + } + }; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/solution-2-a.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,10 @@ +var copy_attributes = function(tgt, src){ + + var keys = ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff', 'ggg', 'hhh']; + var i; + var key; + + for(i=0; i< keys.length; i++){ + tgt[key] = src['get_' + key](); + } +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/solution-2-b.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,11 @@ +var copy_attributes_factory = function(keys){ + + return function(src, tgt){ + var i; + var key; + + for(i=0; i< keys.length; i++){ + tgt[key] = src['get_' + key](); + }; + }; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/solution-2-c.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,15 @@ +var Fields = function(keys){ + this.keys = keys; +}; + +Fields.prototype = { + copy_attributes : function(src, tgt){ + + var keys = this.keys; + var i; + + for(i=0; i< keys.length; i++){ + tgt[key] = src['get_' + key](); + }; + } +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/solution-2-d.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,23 @@ +// Rhino $ js -f library.js -f solution-2-d.js - + +var fields = {}; + +fields.__init__ = function(keys){ + this.keys = keys; +}; + +fields.copy_attributes = function(src, tgt){ + + var keys = this.keys; + var key; + var i; + + for(i=0; i< keys.length; i++){ + key = keys[i]; + tgt[key] = src['get_' + key](); + }; +}; + +var Fields = SimpleClass(fields); + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/test_library.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,40 @@ +var telltail = 'global'; + +var TEST = function(title, code){ + print( 'Testing: ' + title ); + code(); +}; + +var assert = function(arg){ + if(!arg){ + ddt(); + } +}; + + +// start SimpleClass +TEST('SimpleClass', function() +{ + var fruit = {}; + + fruit.__init__ = function(name, colour){ + + this.name = name; + this.colour = colour; + }; + + fruit.greet = function(){ + + return "Hello, I'm a " + this.color + " " + this.name + "."; + }; + + var Fruit = SimpleClass(fruit); + + var bn = Fruit('banana', 'yellow'); + var rc = Fruit('cherry', 'red'); + var gs = Fruit('Granny Smith', 'green'); + + assert( bn.greet() === "Hello, I'm a undefined banana." ); + +}); +// end SimpleClass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/testtools.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,39 @@ +var telltail = 'global'; +var _assertion_count = 0; + +// Define a global function 'log'. +var log; +if (this.hasOwnProperty('jsArguments')){ + + log = function(s){ + print(s + '\n'); + }; +} else { + log = print; +} + +// +var TEST = function(title, code){ + + var x; + + log( 'Testing: ' + title ); + _assertion_count = 0; + + try { + code(); + } catch (x) { + log('! Exception at ' + x.fileName +':' + x.lineNumber); + log('! [' + x.name + '] ' + x.message ); + } + +}; + +var assert = function(arg){ + _assertion_count += 1; + if (!arg){ + var msg = '! assert ' + _assertion_count + ' has failed'; + log(msg); + } +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/core-javascript-work/tutorial.js Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,203 @@ +// [Rhino] $ js -f testtools.js -f tutorial.js +// [JSDB] $ jsdb -load testtools.js -load tutorial.js + +// start warm-up +TEST('warm-up', function() +{ + assert( '0' == 0 ); + assert ( 0 == '' ); + assert( '0' != '' ); + + var a = NaN; + assert( a != a ); + +}); +// end warm-up + + +// start closures +TEST('closures', function() +{ + // A factory for creating get-set pairs. + var get_set_factory = function(){ + + var value; + var get = function(){ + return value; + }; + var set = function(new_value){ + value = new_value; + }; + + return { + get: get, + set: set + }; + }; + + // Create and unpack two get-set pairs. + var tmp; + tmp= get_set_factory(); + var get_a = tmp.get; + var set_a = tmp.set; + + tmp= get_set_factory(); + var get_b = tmp.get; + var set_b = tmp.set; + + // Test that both pairs work. + set_a(12); + assert( get_a() === 12 ); + + set_b(13); + assert( get_b() === 13 ); + + // Test that each pair has its own value. + assert( get_a() === 12 ); + +}); +// end closures + + +// start return_this +TEST('return_this', function() +{ + var return_this = function(){ + return this; + }; + + var default_this = return_this(); + assert( default_this.telltail === 'global' ); + + var obj = {}; + obj.return_this = return_this; + assert( obj.return_this === return_this ); + + assert( obj.return_this() === obj ); + assert( obj['return_this']() === obj ); + + assert( return_this.call(obj) === obj ); + assert( return_this.apply(obj) === obj ); + + assert( return_this.call(undefined).telltail === 'global' ); + assert( return_this.apply(undefined).telltail === 'global' ); + +}); +// end return_this + + +// start transient-bind +TEST('transient-bind', function() +{ + // Set up the test object. + var return_this = function(){ + return this; + }; + var obj = { + return_this : return_this + }; + assert( obj.return_this === return_this ); + + // Deferring method call changes the outcome. + var method_deferred = obj.return_this; + assert( method_deferred === obj.return_this ); + assert( method_deferred() !== obj.return_this() ); // AAA + + // The two outcomes, precisely stated. + assert( obj.return_this() === obj ); + assert( method_deferred().telltail === 'global' ); + + // Another view on the matter. + assert( method_deferred === return_this ); + assert( method_deferred() === return_this() ); // BBB + +}); +// end transient-bind + + +// start millers-device +TEST('millers-device', function() +{ + var array = []; + var obj = {}; + var fn = function(){}; + + assert( typeof array === 'object' ); + assert( '' + obj === '[object Object]' ); + + assert( obj.toString() === '[object Object]' ); + + var object_toString = obj.toString; + + assert( object_toString.call(array) === '[object Array]' ); + assert( object_toString.call(fn) === '[object Function]' ); + +}); +// end millers-device + + +// start return_arguments +TEST('return_arguments', function() +{ + var return_arguments = function(){ + return arguments; + }; + + var my_arguments = return_arguments(0, 1); + + assert( my_arguments.length === 2 ); + + assert( my_arguments[0] === 0 ); + assert( my_arguments[1] === 1 ); + + assert( '' + my_arguments === '[object Object]' ); + assert( '' + [0, 1] === '0,1' ); + + var array_slice = [].slice; + var my_arguments_fixed = array_slice.call( my_arguments ); + + assert( '' + my_arguments_fixed === '0,1'); + + var obj = {}; + assert( obj.toString.call( my_arguments_fixed ) === '[object Array]' ); + +}); +// end return_arguments + + +// start new-operator +TEST('new-operator', function() +{ + // A 'traditional class'. It records 'this' as a side effect. + var this_in_Fn; + var Fn = function(){ + this_in_Fn = this; + }; + + // Call the 'class' without the 'new'. + this_in_Fn = null; // Clear any previous value. + var non_instance = Fn(); + assert( non_instance === undefined ); + assert( this_in_Fn.telltail === 'global'); + + // Call the 'class' with the 'new'. + this_in_Fn = null; // Clear any previous value. + var instance = new Fn(); + assert( instance === this_in_Fn ); + assert( instance.telltail === undefined ); + + // Demonstrate that instance is a descendant of Fn.prototype. + Fn.prototype.telltail = 'instance-of-Fn'; + assert( instance.telltail === 'instance-of-Fn' ); + + // We can give Fn a new prototype. + instance.new_telltail = 'child-of-instance'; + Fn.prototype = instance; + + // And now an instance of Fn has two telltails. + var second_instance = new Fn(); + assert( second_instance.telltail === 'instance-of-Fn' ); + assert( second_instance.new_telltail === 'child-of-instance' ); + +}); +// end new-operator
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/counters-example.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,38 @@ +Counters example +================ + +Goal +---- + +The goal is to create a web page which contains several independent +counters. Each time a counter is clicked, it is incremented. Here's +you can `try out a working example +<./core-javascript-work/counters.html>`_ of what's wanted. + +Below is the complete code of this example. To simplify the matter, +it is completely self-contained. It uses no library code, other than +the definition of *SimpleClass*. + +In general library code is a good idea. This example is designed to +teach you the basics of JavaScript, and not the use of a library. We +hope that what you learn here will help you choose a library, and +build libraries of your own. + + +counters.html +------------- + +.. literalinclude:: /core-javascript-work/counters.html + :language: html + +counters.css +------------ + +.. literalinclude:: /core-javascript-work/counters.css + :language: css + +counters.js +----------- + +.. literalinclude:: /core-javascript-work/counters.js + :language: javascript
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/equality.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,146 @@ +Equality +======== + +Equality in JavaScript can be a little odd. + + +Double equals +------------- + +JavScript has two operations for equality (and inequality). One of +them is '==' (and '!=' for inequality). It's the oddest. + + +Not transitive +^^^^^^^^^^^^^^ + +If *a* is equal to *b*, and if *b* is equal to *c* , then we expect +*a* to be equal to *c*. This is called the transitive property. + + +.. code-block:: javascript + + + js> '0' == 0 // 'a' is equal to 'b' + true + js> 0 == '' // 'b' is equal to 'c' + true + js> '0' == '' // 'a' is not equal to 'c' + false + + + +Not reflexive +^^^^^^^^^^^^^ + +We expect *a* to be equal to *a*. This is called the reflexive +property. + +.. code-block:: javascript + + js> NaN == NaN + false + +Fortunately, this seems to be the only example. + + +Is symmetric +^^^^^^^^^^^^ + +If *a* is equal to *b* then we expect *b* to be equal to *a*. This is +called the symmetric property. In JavaScript equality is symmetric. + + + +Triple equality +--------------- + +JavaScript also has '===' (and '!==' for inequality). + + +Is transitive +^^^^^^^^^^^^^ + +Unlike double equals, triple equals is always transitive. + +.. code-block:: javascript + + js> '0' === 0 // unequal + false + js> 0 === '' // unequal + false + js> '0' === '' // unequal + false + + +Not reflexive +^^^^^^^^^^^^^ + +.. code-block:: javascript + + js> NaN === NaN + false + + +By the way, there's a thread on Facebook with subject `Time and Date +on my wall shows NaNNaNNaN at NaN:NaN +<http://www.facebook.com/topic.php?uid=4915599711&topic=54061>`_. I +wonder how that happened. + +Is symmetric +^^^^^^^^^^^^ + +Triple equality is still symmetric. + + +Don't use double equality +------------------------- + +Double equality does implicit conversions and besides has some odd +rules. My advice is **don't use double equality**. + +Triple equality does not do conversions. If you want to do +conversions in your comparision my advice is to make them explicit. + + +Compare as string +----------------- + +The easiest conversion is to string. Here are two immutables, a +number and a string. + +.. code-block:: javascript + + js> a = 0 + 0 + js> b = '0' + 0 + + +These quantities are double equal but not triple equal. + +.. code-block:: javascript + + js> a == b + true + js> a === b + false + +Here's how to do an explicit conversion to string before comparison, +which gives an equality. + +.. code-block:: javascript + + js> '' + a === '' + b // Converts variables to strings. + true + +.. note:: + + It's always easier to read a triple equal comparison, because + you're not distracted by the complex double equal rules. + + +Compare as number +----------------- + +[To follow later.]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/exercise-1.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,35 @@ +Exercise one +============ + +Here's some JavaScript code I found somewhere. I've changed the names +of everything to hide the origin of the code. But it's not something +I've made up. + +The exercise is + +#. Understand and describe what it does. + +#. Provide clearer code that does the same thing. + +#. Suggest a better way of going about things. + +.. literalinclude:: /core-javascript-work/exercise-1.js + :language: javascript + + +Hints +----- + +#. The code creates and calls an anonymous function, a bit like this. + + .. code-block:: javascript + + js> a = function(x){return x + 1}(2) + 3 + +#. What happens if AAA is an object? + +#. What happens if AAA is undefined? + +#. For now, ignore the assignment to BBB.fff. What value does AAA + get? (The answer depends the initial value of AAA.)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/exercise-2.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,59 @@ +Exercise two +============ + +Here's some more JavaScript code I found. I've changed the names of +everything to hide the origin of the code. But it's not something +I've made up. + +.. literalinclude:: /core-javascript-work/exercise-2.js + :language: javascript + +The exercise is + +#. Rewrite the function copy_attributes so that it makes a loop over + + .. code-block:: javascript + + var keys = ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff', 'ggg', 'hhh']; + +#. Write a copy_attributes_factory function so that we can write + + .. code-block:: javascript + + var copy_attributes = copy_attributes_factory(keys); + +#. Write a Fields class such that something like this will work: + + .. code-block:: javascript + + var aaa = Fields(keys); + aaa.copy_attributes(src, tgt); + + +Hints +----- + +#. In JavaScript attribute access and item access are the same. + First we use item access to set: + + .. code-block:: javascript + + js> a = {} + [object Object] + js> key = 'attrname' + attrname + js> a[key] = 42 + 42 + + and then we use attribute access to get: + + .. code-block:: javascript + + js> a.attrname + 42 + +#. This applies even for function (aka method) calls. + +#. Have the factory function store the keys in a closure. + +#. There are many ways of writing Fields. Choose one that suits you.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/getting-started.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,76 @@ +Getting started +=============== + +What you need +------------- + +You'll need on your machine + +#. A JavaScript interpreter which provides a command line + +#. An editor on your machine. + +You'll also to download and unzip the course :download:`work folder +</core-javascript-work.zip>`. + + +Windows and Linux +----------------- + +For Windows the easiest thing to do is to `download the JSDB +interpreter <http://www.jsdb.org/download.html>`_, and use notepad (or +some other editor). + +For Linux you and install Rhino and use your favourite editor. + +.. code-block:: sh + + $ sudo apt-get install rhino + + +Are you ready? +-------------- + +You'll want to be able to run the JavaScript interpreter from the +command line when in the work folder. This will be automatic (via the +PATH) with Linux and Rhino. For Windows the easiest thing to do is to +place the jsdb.exe file in the work folder. + +When you're ready type *js* at a command prompt. This starts the +interpreter and gives you a *js>* prompt. You'll get something like +this. + +.. code-block:: javascript + + core-javascript-work$ js + Rhino 1.7 release 2 2010 01 20 + js> + +Now type the command as below at the JavaScript prompt (with Return at the end of +each line) and you'll get responses as below. + + +.. code-block:: javascript + + js> a = '0' + 0 + js> b = 0 + 0 + js> c = '' + + js> a == b + true + js> b == c + true + js> a == c + false + + +You might be surprised by the last response from the interpreter, but +every JavaScript interpreter does this. + + +Yes, you're ready +----------------- + +To exit the interpreter use Ctrl-C or Ctrl-D.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/global.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,122 @@ +The global object +================= + +JavaScript has a global object. It is, so to speak, the object of +last resort. If there's no other suitable object then JavaScript will +use the global object (rather than reporting an error). + +.. note:: + + Douglas Crockford writes **JavaScript's global object ... is far and + away the worst part of JavaScript's many bad parts.** + + +Getting global +--------------- + +Here's how to get the global object. + +.. code-block:: javascript + + js> return_this = function(){return this;} + + function () { + return this; + } + + js> global = return_this() + [object global] + +The programmer who wants to can always obtain access to the global +object. + + +Global variables +---------------- + +Global variables are nothing more than attributes of the global +object. + +.. code-block:: javascript + + js> s = 'Hello world.' + Hello world. + js> global.s + Hello world. + js> global.i = 42 + 42 + js> i + 42 + + +Global pollution +---------------- + +It's very easy to inadvertently pollute (change) the global object. +All it takes is an assignment to an undeclared variable in a function. + +.. code-block:: javascript + + js> pollute = function(n){ i = n; }; + function (n) { + i = n; + } + js> i + 42 + js> pollute(13) + js> i + 13 + +More global pollution +--------------------- + +There's a more subtle way to pollute the global object, which involves :doc:`JavaScript's this object <what-is-this>`. + +To begin with, note that we can push values onto an array. + +.. code-block:: javascript + + js> array = [] + + js> array.push(1, 2, 3) + 3 + js> array + 1,2,3 + +Let's try using *array.push* as a stand-alone function. + +.. code-block:: javascript + + js> p = array.push + function push() { [native code for Array.push, arity=1] } + + js> p(4, 5, 6) + 3 + +The push function returns the length of the object it has just pushed +to. So the value *3* above is a signal that all is not well. And +indeed it is not. The original array is unchanged, and the global +object has an entry at 0. + +.. code-block:: javascript + + js> array + 1,2,3 + js> + js> global[0] + 4 + +It's also got a length! + +.. code-block:: javascript + + js> global.length + 3 + + +Explanation +----------- + +This behaviour is a consequence of :doc:`what-is-this` and +:doc:`bind-is-transient`, together with JavaScript's no-exceptions +design.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/immutables.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,131 @@ +Immutables +========== + +First, a quick word about variables. A value is an indentifier that +holds a value. In JavaScript every value has a type, but variables +are not typed. In other words, any variable can hold any value. Most +dynamic language (such as Perl, Python and Ruby) are like this. + +Always declare variables with a var statement, because sometimes a +missing var can cause a hard-to-find bug. Experiments at the command +line are the only exception to this rule. + +We can change an array or a 'dictionary'. Immutable values are values +that can't be changed. We can't change, for example, the third +character in a string. + + +Strings +------- + +Strings literals are delimited by single or double quote marks, with +special characters escaped. There's no difference between the two +forms (except in single quote you don't have to escape double quote, +and vice versa.) Generally, I prefer the single quote form as it's +less busy on the eye and slightly easier to type. + +.. code-block:: javascript + + js> s = 'Hello world.' + Hello world. + +We can add two strings together to produce a third string. + +.. code-block:: javascript + + js> r = "I'm me. " + I'm me. + js> r + s + I'm me. Hello world. + + +typeof +------ + +There's a built-in operator called 'typeof' that returns a string +that, sort-of, gives the type of a value. + +.. code-block:: javascript + + js> typeof(s) + string + +Because typeof is an operator (just as '+' is an operator) the +parentheses are not needed, and many JavaScript programmers omit them. + +.. code-block:: javascript + + js> typeof s + string + +Here we see that typeof produces (returns) a string. + +.. code-block:: javascript + + js> typeof typeof s + string + + +Numbers +------- + +JavaScript numbers are platform and processor independent. It uses +IEE 754 to represent both integers and floats. + +.. code-block:: javascript + + js> i = 42 + 42 + js> typeof i + number + + +Booleans +-------- + +JavaScript has keywords 'true' and 'false' whose values are always +true and false respectively. + +.. code-block:: javascript + + js> t = true + true + js> typeof b + boolean + +Logical comparisions also produce Booleans. + +.. code-block:: javascript + + js> f = (1 > 2) + false + js> typeof f + boolean + + +undefined and null +------------------ + +JavaScript has *two* values that represent None. Later, we'll see +why, and which to use when. For now we'll simply note that they are +different, because their types are different. + +.. code-block:: javascript + + js> typeof undefined + undefined + js> typeof null + object + + +Gotchas +------- + +.. code-block:: javascript + + js> s = 'Hello world.' + Hello world. + js> s.lang = 'en' + en + js> s.lang === undefined + true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/index.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,56 @@ +Welcome to Core JavaScript training +=================================== + +This document has a :ref:`search`. + + +About this course +----------------- + +.. toctree:: + :maxdepth: 2 + + getting-started + counters-example + testtools + + +Objects +------- + +.. toctree:: + :maxdepth: 2 + + + immutables + equality + objects + object-tree + simple-classes + + +Functions +--------- + +.. toctree:: + :maxdepth: 2 + + global + closures + what-is-this + bind-is-transient + what-is-new + arguments + millers-device + + +Examples and exercises +---------------------- + +.. toctree:: + :maxdepth: 2 + + exercise-1 + solution-1 + exercise-2 + solution-2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/make.bat Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,163 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^<target^>` where ^<target^> is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\CoreJavaScript.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\CoreJavaScript.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %ALLSPHINXOPTS% %BUILDDIR%/locale + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/millers-device.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,22 @@ +Mark Miller's device +==================== + +This can be omitted at a first reading. Mark Miller is one of the +leading JavaScript experts at Google. He's been credited an ingenious +application of call that gives a simple and reliable test for whether +or not an object is an array. + +This may not sound very much, but it is something that had been +puzzling the other experts for some years. For example, Crockford in +his *Good Parts* (published 2008, page 61) gives a much more complex +and less reliable solution to this problem. + +The moral of this story is that even the experts in JavaScript can +have difficulty finding the best way to solve a simple problem. + +Test code +--------- +.. literalinclude:: /core-javascript-work/tutorial.js + :language: javascript + :start-after: start millers-device + :end-before: end millers-device
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/object-tree.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,168 @@ +The object tree +=============== + +Objects can have attributes. The statements + +.. code-block:: javascript + + value = obj.attr # Get the attr of 'a'. + obj.attr = value # Set the attr of 'a'. + +respectively **set** and **get** the *attr* attribute of the object +*obj*. Inheritance is where an object gets some its attributes from +one or other more objects. JavaScript uses an object tree for +inheritance. + + +.. _inheritance-tree: + +Tree +---- + +All JavaScript objects are part of an inheritance tree. Each object +in the tree has a parent object, which is also called the prototype +object (of the child). There is a single exception to this rule, +which is the **root** of the tree. The root of the tree does not have +a parent object. + +.. note:: + + **You can't get far in JavaScript without understanding the + object tree.** + + +Get +^^^ + +When JavaScript needs to get an attribute value of an object, it first +looks up the attr of the attribute in the object's dictionary. If the +attr is a key in the dictionary, the associated value is returned. + +If the attr is not a key, then the process is repeated using the +object's parent, grandparent, and so on until the key is found. If +the key is not found in this way then *undefined* is returned. + + +Set +^^^ + +When JavaScript needs to set an attribute value of an object it +ignores the inheritance tree. It simply sets that value in the +object's dictionary. + + +Root +---- + +When the interpreter starts up, the **root** of the tree is placed at +*Object.prototype*. (We'll find out later why that location is used.) + +Every object inherits from the **root**, although perhaps not +directly. Here's a simple example: + +.. code-block:: javascript + + js> root = Object.prototype + js> a = {} + js> a.attr === undefined + true + js> root.attr = 'gotcha' + js> a.attr + gotcha + +If we give *root* a *attr* attribute then every other object, +including those already created and those not yet created, also has a +*attr* attribute with the same value. (In practice it's better not to +change Object.prototpye.) + +This applies arrays: + +.. code-block:: javascript + + js> array = [0, 1, 2] + 0,1,2 + js> array.attr + gotcha + +And even to functions: + +.. code-block:: javascript + + js> f = function(){} + function () { + } + + js> f.attr + gotcha + +.. note:: + + A page might have many scripts, all of which would like to modify + the Object.prototype root object. This can cause bugs and + incompatibilities. So try not to do this. + + +However, this up-the-tree lookup does not apply if *attr* is found +earlier in the tree. We continue the previous example to show this, +and the behaviour of set. + +.. code-block:: javascript + + js> a.attr = 'fixed' + js> a.attr + fixed + js> root.attr + gotcha + + +Create +------ + +Any tree can be constructed from its root, together with a command +**create(parent)** that returns a new **child** of the given parent +node. + +In all but the most recent version of JavaScript the *create* function +is not built in. However, it's easy to write one, once you know +enough JavaScript. Here's how its done in the work folder's +library.js file. + +.. literalinclude:: /core-javascript-work/library.js + :language: javascript + :start-after: start create + :end-before: end create + + +Using create +------------ + +Here's an example of its use: + +.. code-block:: javascript + + js> a = {} + js> b = create(a) + js> a.attr = 'apple' + apple + js> b.attr + apple + + +And a continuation of the example: + +.. code-block:: javascript + + js> c = create(b) + js> c.attr + apple + js> b.attr = 'banana' + banana + js> c.attr + banana + + +.. note:: + + JavaScript uses an inheritance tree. By using *create*, we can + create any inheritance tree. All JavaScript objects are in this + tree.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/objects.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,177 @@ +Objects +======= + +JavaScript has objects and is object-oriented, but in an unusual way +(more on that later). + + +Simple objects +-------------- + +Simple objects are like dictionaries or hashes in other languages. +They support key-value storage and access to attributes. Here's how +to create a simple object. + +.. code-block:: javascript + + js> obj = {} + [object Object] + + js> typeof obj + object + +We can store attribute values in a simple object (or any object in the +doc:`object-tree`). + +.. code-block:: javascript + + js> obj.s = 'hi' + hi + js> obj.i = 10 + 10 + +We can get these values back again. + +.. code-block:: javascript + + js> obj.s === 'hi' + true + js> obj.i == 10 + true + + +Missing attributes +------------------ + +It's not an error to ask for something that's not there. We get +undefined. + +.. code-block:: javascript + + js> obj.dne === undefined + true + +We still get undefined if we set the value to undefined. + +.. code-block:: javascript + + js> obj.undef = undefined + undefined + js> obj.dne === undefined + true + +You can use null to signal that there is a value, but that it is None. + +.. code-block:: javascript + + js> obj.none = null + null + js> obj.none === null + true + + +hasOwnProperty +-------------- + +You can use hasOwnProperty to help figure out why an object has an +undefined attribute. It's also useful when inspecting the +doc::`object-tree`. Often, however, it's better to write your code so +you don't need to do this (for example by using null). + +.. code-block:: javascript + + js> obj.hasOwnProperty('dne') + false + js> obj.hasOwnProperty('undef') + true + js> obj.hasOwnProperty('none') + true + + +Object literals +--------------- + +You can create a simple object by placing key-value pairs in the curly +braces. + +.. code-block:: javascript + + js> someone = { + > 'name': 'Joe Doe', + > 'age': 43 + > } + [object Object] + + js> someone.age + 43 + +Take care *not* to put a trailing semicolon in the object literal. It +will work in Firefox but not in Internet Explorer. + + +Arrays +------ + +An array is a list of items. You can put anything is as a value for +the list. Use square brackets to create an array. + +.. code-block:: javascript + + js> array = [] + +Arrays expand to accomodate the data you store in them. You can even +leave gaps. + +.. code-block:: javascript + + js> array[0] = 'zero' + zero + js> array[3] = 'three' + three + +An array turned into a string consists of the string on its entries +joined by commas. + +.. code-block:: javascript + + js> array + zero,,,three + js> array[1] === undefined + true + + +Array literals +-------------- + +As with simple objects, simply place the values between the square +brackets, separated by commas. + +.. code-block:: javascript + + js> seasons = ['spring', 'summer', 'autumn', 'winter'] + spring,summer,autumn,winter + + +As with simple objects, beware of trailing commas and missing entries. +This will work in some browsers and not others. + + +JSON +---- + +`JSON <http://www.json.org/>`_,stands for JavaScript Simple Object +Notation. It is very much one of the best parts of JavaScript. It is +`the fat-free alternative to XML <http://www.json.org/xml.html>`_, and +is widely used in AJAX (instead of XML). JSON objects are object and +array literals constructed using only simple objects, arrays, strings, +true, false and null. + +If your code accepts JSON objects then it will be a lot easier to use +it with AJAX or otherwise integrate it with other systems. + +Many programming languages have JSON libraries. You don't have to use +JavaScript to use JSON. + +To provide standards, there are rules on how to write a JSON objects. +Many applications and programming languages will generate valid JSON +for you. I find YAML a convenient way of authoring JSON data.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/py/remove_answers.py Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,94 @@ +'''Remove answers from exercises file.''' + +import os +import sys + +NORMAL, SKIPPING, ERROR = range(3) +LINE, SOMETHING, START, STOP = range(4) + +lookup = {} + +# All other states are an error. +lookup[NORMAL, LINE] = NORMAL +lookup[NORMAL, SOMETHING] = NORMAL +lookup[NORMAL, START] = SKIPPING +lookup[SKIPPING, LINE] = SKIPPING +# lookup[NORMAL, STOP] = ERROR +# lookup[SKIPPING, SOMETHING] = ERROR +lookup[SKIPPING, STOP] = NORMAL + + +def doit(src, tgt): + + pending = [] + with open(src, 'rb') as lines: + + new_state = NORMAL + + for lineno, line in enumerate(lines): + + state = new_state + + + stripped_line = line.strip() + + if stripped_line.startswith('something'): + event = SOMETHING + + elif stripped_line == '// start': + event = START + + elif stripped_line == '// stop': + event = STOP + + else: + event = LINE + + new_state = lookup[state, event] + + if event == SOMETHING: + pass + + elif event == STOP: + pass + + elif event == START: + indent = len(line) - len(line.lstrip()) + spaces = line[0:indent] + pending.append(spaces + '// Please write your answer here.') + + elif state == SKIPPING: + pass + + elif event == LINE: + pending.append(line.rstrip()) + + + with open(tgt, 'wb') as f: + + for line in pending: + f.write(line + '\r\n') + + + +if __name__ == '__main__': + + + root = 'core-javascript-work' + exercises_dir = root + answers_dir = os.path.join(root, 'answers') + + + names = [ + name + for name in os.listdir(answers_dir) + if name.endswith('.js') + ] + + + for name in names: + + src = os.path.join(answers_dir, name) + tgt = os.path.join(exercises_dir, name) + + doit(src, tgt)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/simple-classes.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,47 @@ +Simple classes +============== + +JavaScript has objects, which through the object tree support +inheritance. Almost all object-oriented languages have classes and +instances, but JavaScript has neither. This will be explained later, +along with a discussion of the consequences. + +For now our focus is on getting going with object-oriented +programming. The easiest way to do this is via create. + + +Definition +---------- + +Here's a factory function that creates classes. It returns a function +called *cls*, which in turn when called returns instances. At it's +heart is *create*, and also *apply* and *arguments*, which we've not +seen before). + +.. literalinclude:: /core-javascript-work/library.js + :language: javascript + :start-after: start SimpleClass + :end-before: end SimpleClass + + +Why *cls*? +---------- + +We call the returned function *cls* because in JavaScript *class*, +like *function* is a reserved word in JavaScript. However, in +JavaScript the identifier *class* has no meaning, and cannot be used +in any valid JavaScript program. It seems that they intended to +provide a built-in class capability, but never got round to it. + + +Fruit example +------------- + +Here's an example of the use of *SimpleClass*. There's a bug in it, +and I've changed the test so it passes. Can you find the bug and fix +the example? + +.. literalinclude:: /core-javascript-work/test_library.js + :language: javascript + :start-after: start SimpleClass + :end-before: end SimpleClass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/solution-1.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,118 @@ +Solution one +============ + +Here's the code we're studying. + +.. literalinclude:: /core-javascript-work/exercise-1.js + :language: javascript + + +Verbal summary +-------------- + +We are making sure that there's an object AAA and giving it an +attribute AAA.fff, which is a function. We won't say more about the +function in this summary. + + +Hoisting once-only arguments +---------------------------- + +First, we'll hoist the argument to the anonymous function into its +body. We can always do this, without changing the meaning, provided the +function is called only once. + +.. literalinclude:: /core-javascript-work/solution-1-a.js + :language: javascript + +There, that's better. We no longer have to read to the end of the +function to find out what BBB is (and in particular its relationship +with AAA). + + +What is AAA? +------------ + +Next, we'll hide (or ignore) the assigment to BBB.fff. This +assignment sets an attribute on the object pointed to by the variable +BBB. In other words, it mutates the object pointed to by BBB, but the +variable BBB still points to the same object as it did before. + +.. literalinclude:: /core-javascript-work/solution-1-b.js + :language: javascript + + +If AAA starts out as an object then the expression + +.. code-block:: javascript + + AAA || {} + +evaluates to AAA, while if AAA is undefined then it evaluates to the +newly created object literal (the left-and-right curly braces). In +either case, this value is assigned to the local variable BBB, which +is then returned by the anonymous function and bound to AAA. + +Put another way, if AAA starts off as undefined then it is bound to a +new object, while if it starts off as an object then it finishes as +the same object. + +Therefore, our truncated version of the original exercise is equivalent to + +.. literalinclude:: /core-javascript-work/solution-1-c.js + :language: javascript + +This idiom is very common in JavaScript, and is (as here) a +consequence of using objects as namespaces (which is better than using +the global object) and defensiveness about the order in which files +are loaded. + +Here's a command line session that shows this equivalence. To begin +with AAA is undefined and the assignment binds AAA to a new object. +For the second assignment AAA is defined and the assignment binds AAA +to the object is already bound to. + +.. code-block:: javascript + + js> var AAA = AAA || {} + js> AAA + [object Object] + js> pointer = AAA + [object Object] + js> var AAA = AAA || {} + js> pointer === AAA + true + + +The assignment to BBB.fff +------------------------- + +Reading the code, it looks as if BBB is local to the anonymous +function. As a variable it is, but its value is not. The value of +BBB is bound to the global object AAA at the end of the function call. + +The following is almost equivalent to our original JavaScript code. +Note that BBB.hhh has been changed to AAA.hhh. + +.. literalinclude:: /core-javascript-work/solution-1-d.js + :language: javascript + + +Caveat +------ + +Why did I say *almost equivalent*? Because we might subsequently do +something like + + +.. code-block:: javascript + + var CCC = AAA; + AAA = undefined; + +For the original code the closure variable BBB still exists and points +to the same object as CCC, even though AAA is undefined. Thus, the +reference to BBB.hhh in the original code will continue to work. + +For the revised code, when the value of AAA is set to undefined the +reference to AAA.hhh will fail.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/solution-2.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,112 @@ +Solution two +============ + +Here's the code we are studying. The first task is make a loop that +does the assignments. + +.. literalinclude:: /core-javascript-work/exercise-2.js + :language: javascript + + +Making a loop +------------- + +As previously hinted, we use item access rather than attribute access. +The problem with attribute access is that the name of the key is +hard-coded and we want it to vary during the loop. + +Here's the solution. There's a bit of overhead in setting up the +loop, but the body of the loop is straightforward. + +.. literalinclude:: /core-javascript-work/solution-2-a.js + :language: javascript + + +Factory function +---------------- + +The second task is to write a factory function that takes the list of +keys as a parameter. + +This is a common refactoring pattern, and so is worth learning well. +First we give the code, and then the explanation. + +.. literalinclude:: /core-javascript-work/solution-2-b.js + :language: javascript + + +Here's what we've done. We created a wrapper function, with *keys* as +its only parameter. This will be our factory function. We've placed +the original function into the body of the wrapper, and return it (as +the value of the wrapper function). Finally, we clean up by removing +the *keys* constant from the inner function. + +There, it's done! When the factory function executes it returns an +instance of the inner function for which *keys* is the argument we +supplied to the factory. + + +A prototype class +----------------- + +The third task is to write a Fields class that has a copy_attributes +method. + +Here's a function prototype based solution. It relies on the user of +the class supplying the *new* operator to create the new instance (and +chaos results if the user forgets). + +.. literalinclude:: /core-javascript-work/solution-2-c.js + :language: javascript + +A minor point is the trailing sequence of right curly braces. Two are +followed by a semicolon, but the middle one is not. Why? What +happens if we put a semicolon there? + + +A SimpleClass solution +---------------------- + +Here's another solution to the third task, which does not require the +user to supply a *new* operator when creating instances. Instead it +uses a factory function, which we call *Class* , that creates an +instance constructor function from a prototype object. + +.. literalinclude:: /core-javascript-work/solution-2-d.js + :language: javascript + + +Exercise: write test +-------------------- + +Here's an exercise. Write a JavaScript file that tests this solution. + + +Exercise: bound method class +---------------------------- + + +Recall that this-based instance methods are somewhat fragile. In +other words, code like this will fail (and make unwanted changes to +the global object): + +.. code-block:: javascript + + var instance = MyClass(arg1, arg2); + + var fn = instance.method; + element.onclick(fn); + +Let's say that a class *has bound methods* if we can safely pass +around instance methods. In other words, we want *instance.method* to +have no dependence on the value of *this*. + +We can achieve this by using a different class factory function. + +.. code-block:: javascript + + var myclass = {} // The prototype object. + // Add methods to myclass. + var MyClass = BoundMethodClass(myclass); + +The exercise is to create (and test) a BoundMethodClass factory.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/testtools.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,76 @@ +Test tools +========== + +To make it easier to test code, and to ensure that example code is +correct, there is a small testtools.js file in the work folder. + + +How to write tests +------------------ + +To prepare a test create a file like this. It's also in the work +folder. (For real examples the file will give *assert* a more +interesting argument, whose truth or falsity is perhaps not obvious.) + +.. literalinclude:: /core-javascript-work/demo_testtools.js + :language: javascript + + +How to run tests +---------------- + +Here's how to run the tests at the command line, finishing with a +command prompt that allows you inspect the state and also run the test +again. + + +Rhino +^^^^^ + +For Rhino use *load* to run the test again. + +.. code-block:: sh + + core-javascript-work$ js -f testtools.js -f demo_testtools.js -f - + Testing: all-pass + Testing: 2-4-fail + ! assert 2 has failed + ! assert 4 has failed + Rhino 1.7 release 2 2010 01 20 + js> load('demo_testtools.js') // You type this. + Testing: all-pass + Testing: 2-4-fail + ! assert 2 has failed + ! assert 4 has failed + js> + + +JSDB +^^^^ + +For JSDB use *run* to run the test again. + +.. code-block:: sh + + C:core-javascript-work> jsdb -load testtools.js -load demo_testtools.js + Testing: all-pass + Testing: 2-4-fail + ! assert 2 has failed + ! assert 4 has failed + js> load('demo_testtools.js') // You type this. + Testing: all-pass + Testing: 2-4-fail + ! assert 2 has failed + ! assert 4 has failed + js> + + +testools.js +----------- + +(Optional on a first reading.) Here's the file testools.js. It's +short and simple (and has a not-nice dependency on global variables). +It also has a branch based on whether it's being run on JSDB or Rhino. + +.. literalinclude:: /core-javascript-work/testtools.js + :language: javascript
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/what-is-new.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,62 @@ +What is new? +============ + +This can be omitted at a first reading, and omitted altogether if you +never have to deal with code that uses the *new* operator. If you do, +then you **must** read about the missing *new* problem. + +JavaScript has an object tree. This is a fundamental language +feature. The *new* operator is a way of adding objects to the tree. +The *create* function, which is built-in to the latest version of +JavaScript, can be defined in terms of *new*, and vice versa. + + +Test code +--------- + +.. literalinclude:: /core-javascript-work/tutorial.js + :language: javascript + :start-after: start new-operator + :end-before: end new-operator + + +Discussion +---------- + +By changing the prototype object of *Fn* we start building a tree of +objects. + +There is a built-in relation between an object and its parent. There +is no relation between the object and its constructor, here called +*Fn*, except that at the time of construction the parent of the object +is Fn.prototype. + +The exact rules for the behaviour of *new* are complicated and not +given here. + + +No missing *new* warning +------------------------ + +We saw that when *Fn* is called without the *new* then during its +execution the value of *this* is the global object. If the +constructor *Fn* mutates *this* (and almost every constructor does), +then it is the global object that is mutated. + +This is very bad: + +#. You may clobber someone elses data. + +#. Someone else may clobber your data. + +#. If you create two instances, you will clobber your own data. + +#. Testing for this error involves extra work. + +#. Bugs that arise from this error can appear random and can be hard + to find. + +.. note:: + + Crockford writes **A much better alternative is to not use new at + all."**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core-javascript/what-is-this.rst Mon Feb 13 10:05:46 2012 +0000 @@ -0,0 +1,87 @@ +What is this? +============= + +JavaScript has a keyword *this*, whose value depends on the execution +context. It is always an object (and never an immutable, such as a +number). + + +Test code +--------- + +.. literalinclude:: /core-javascript-work/tutorial.js + :language: javascript + :start-after: start return_this + :end-before: end return_this + + +Discussion +---------- + +The function *return_this* is a convenient way of getting of getting +hold of the object that is the value of *this*. The code that follows +shows: + +#. The default value of *this* is the global object. + +#. When *return_this* is called as either a method (i.e. an attribute + that is a function) or item of *obj* then *this* is *obj* itself. + +#. That the *call* and *apply* methods of a function allow us to set + the value of *this* within a particular execution of the function. + +#. If we pass *undefined* to function *call* or *apply* then we get + the global object as *this*. + + +Explanation +----------- + +Throughout the execution of JavaScript code, the identifier *this* has +a value, which is an object (and not an immutable value). The value +of *this* depends on how the JavaScript interpreter got to be +executing the code. + +#. At the command line *this* is the global object. + +#. In a function *this* is the object from which the interpreter got + the function. + +#. Because global variables are attributes of the global object, the + rule for calling global variables as functions is a special case of + the previous rule. + +#. + + +Here's a helpful way to look at the situation. It is as if the +interpreter maintains a *this stack*, which starts containing a single +item, the global object. Each time an item access of an object is +immediately followed by a function call, the object is added to the +*this stack*, and removed at the end of the function call. For all +other function calls the global object is added to the *this stack*. + +During execution of a function the value of *this* is the element at +the top of the *this stack*. + +Finally, the function *call* and *apply* methods allow for a +particular object to be placed at the top of the *this stack* before +the function is called. + + +Exercises +--------- + +#. At the command-line try making an assignment to *this*. What + happens? + +#. Try loading a JavaScript program that makes an assignment to + *this*. What happens? + +#. For what values of *value* is the following true? + + .. code-block:: javascript + + return_this.apply(value) === value; + +#. Write a simple test that detects these values.
--- a/source/inheritance.rst Wed Oct 19 11:45:25 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -Inheritance -=========== - - -Objects have attributes. In both JavaScript and Python the statements - -.. code-block:: python - - value = a.name # Get the name of 'a'. - a.name = value # Set the name of 'a'. - -respectively **set** and **get** the *name* attribute of the object -*a*. Inheritance is where an object gets some its attributes from one -or other more objects. - -JavaScript and Python handle inheritance differently. Here we -describe what JavaScript does, and later [where] we compare with -Python. - - -.. _inheritance-tree: - -Tree ----- - -In JavaScript all objects are part of an inheritance tree. Each -object in the tree has a parent object, which is also called the -prototype object (of the child). There is a single exception to this -rule, which is the **root** of the tree. The root of the tree does -not have a parent object. - -**You can't get far in JavaScript without understanding the -inheritance tree.** - -Get -^^^ - -When JavaScript needs to get an attribute value of an object, it first -looks up the name of the attribute in the object's dictionary. If the -name is a key in the dictionary, the associated value is returned. - -If the name is not a key, then the process is repeated using the -object's parent, grandparent, and so on until the key is found. If -the key is not found in this way then (see :ref:`object-missing-keys`) -*undefined* is returned. - -Set -^^^ - -When JavaScript needs to set an attribute value of an object it -ignores the inheritance tree. It simply sets that value in the -object's dictionary. With Python its just the same. (However, -JavaScript's :doc:`pseudo-objects` are don't behave in this way.) - -Root ----- - -When the interpreter starts up, the **root** of the tree is placed at -*Object.prototype*. - -Every object inherits from the **root**, although perhaps not -directly. Here's an example: - -.. code-block:: javascript - - js> root = Object.prototype - js> a = {} - js> a.name === undefined - true - js> root.name = 'gotcha' - js> a.name - gotcha - -Once we give *root* a *name* attribute every other object, including -those already created and those not yet created, also has a *name* -attribute with the same value. - -However, this does not apply if *name* is found earlier in the tree. -We continue the previous example to show this, and the behaviour of -set. - -.. code-block:: javascript - - js> a.name = 'fixed' - js> a.name - fixed - js> root.name - gotcha - -Using create ------------- - -Any tree can be constructed from its root, together with a command -**create(parent)** that returns a new **child** of the given parent -node. - -In JavaScript the *create* function is not built in (although perhaps -it should be). However, it's easy to write one [link], once you know -enough JavaScript. - -Here's an example of its use: - -.. code-block:: javascript - - js> a = {} - js> b = create(a) - js> a.name = 'apple' - apple - js> b.name - apple - - -And a continuation of the example: - -.. code-block:: javascript - - js> c = create(b) - js> c.name - apple - js> b.name = 'banana' - banana - js> c.name - banana - - -.. note:: - - JavaScript uses an inheritance tree. By using *create*, we can - create any inheritance tree. All JavaScript objects are in this - tree.
