diff options
Diffstat (limited to 'files/pl/orphaned/web/javascript/na_początek/index.html')
-rw-r--r-- | files/pl/orphaned/web/javascript/na_początek/index.html | 903 |
1 files changed, 903 insertions, 0 deletions
diff --git a/files/pl/orphaned/web/javascript/na_początek/index.html b/files/pl/orphaned/web/javascript/na_początek/index.html new file mode 100644 index 0000000000..44f95f6c62 --- /dev/null +++ b/files/pl/orphaned/web/javascript/na_początek/index.html @@ -0,0 +1,903 @@ +--- +title: Na początek +slug: Web/JavaScript/Na_początek +tags: + - JavaScript + - Strony_wymagające_dopracowania + - Wszystkie_kategorie +--- +<div class="note"><strong>UWAGA, UWAGA!</strong>: Pomimo tego, że artykuł jest wywieszony jako artykuł do dopracowania, to mimo to, PROSZĘ go na razie <strong>NIE TŁUMACZYĆ</strong>. Powodem jest to, że tekst w sporej mierze jest już przetłumaczony. Więc po prostu szkoda zapału i zużywania energii na coś co już jest w wersji PL. W ramach tego samego czasu możecie przetłumaczyć coś, czego nie ma na 100% w dopracowanych. <a href="pl/Wikipedysta/Ptak82">Ptak82</a> 17:18, 13 mar 2007 (PDT)</div> + +<h3 id="Wprowadzenie" name="Wprowadzenie">Wprowadzenie</h3> + +<p>Dlaczego ponowne wprowadzenie? Bo <a href="pl/JavaScript">JavaScript</a> można śmiało określić jako <a class="external" href="http://javascript.crockford.com/javascript.html">najbardziej błędnie rozumiany język programowania (en)</a>. Często wyszydzany, nazywany zabawką, pod swoją kuszącą prostotą chowa on szerokie możliwości. W roku 2005 pojawił się szereg wysokiej jakości aplikacji w JavaScripcie, dowodzących, że pogłębiona znajomość tej technologii to to, czym powinien móc się pochwalić każdy twórca serwisów internetowych.</p> + +<p>Wygodnie będzie zacząć od kilku słów na temat historii języka. JavaScript stowrzył w roku 1995 Brendan Eich, programista firmy Netscape. Pierwszą przeglądarką z obsługą JavaScriptu była Netscape 2, wydana na początku 1996 r. Pierwotnie język ten miał się nazywać LiveScript, ale nazwę nieszczęśliwie zmieniono z przyczyn marketingowych - chciano skorzystać z popularności Javy, języka firmy Sun Microsystems, mimo że oba te języki niewiele ze sobą łączy. Nazwa "JavaScript" do dziś jest przyczyną wielu nieporozumień.</p> + +<p>Microsoft wydał prawie kompatybilną wersję tego języka pod nazwą JScript razem z przeglądarką Internet Explorer 3 kilka miesięcy później. Firma Netscape zgłosiła język do europejskiej organizacji standaryzacyjnej <a class="external" href="http://www.ecma-international.org/">Ecma International</a>, skutkiem czego było pierwsze wydanie standardu <a href="pl/ECMAScript">ECMAScript</a> w roku 1997. Specyfikację tę szeroko uzupełniono i rozbudowano w roku 1999 i opublikowano jako <a class="external" href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript wydanie 3</a> i od tej pory język ten był w miarę stabilny, nie przechodził większych rewolucji, choć obecnie trwają prace nad wydaniem czwartym.</p> + +<p>Stabilność ta to dobra wiadomość dla programistów, jako że wszystkie implementacje miały wiele czasu, by w pełni dojść do w miarę zgodnego stanu. W tym artykule skupiam się wyłącznie na dialekcie z wydania trzeciego. Dla uproszczenia pozostanę w tym artykule przy terminie "JavaScript".</p> + +<p>W przeciwieństwie do większości języków, JavaScript nie posiada koncepcji wejścia/wyjścia. Zaprojektowany został jako język skryptowy dla maszyny wirtualnej i to ta maszyna wirtualna ma za zadanie zapewnić mechanizmy komunikacji ze światem zewnętrznym. Najbardziej popularną maszyną wirtualną jest przeglądarka, ale interpretery JavaScriptu można znaleźć np. w programach Adobe Acrobat, Photoshop, silniku Yahoo! Widget i innych.</p> + +<h3 id="Przegl.C4.85d" name="Przegl.C4.85d">Przegląd</h3> + +<p>Zacznijmy od fundamentów każdego języka programowania: typów danych. Programy w JavaScripcie manipulują wartościami, z których każda należy do konkretnego typu. Typy w JavaScripcie to:</p> + +<ul> + <li><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/Number">Number</a> - liczby</li> + <li><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/String">String</a> - ciągi znaków (teksty)</li> + <li><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/Boolean">Boolean</a> - wartości logiczne</li> + <li><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/Function">Function</a> - funkcje</li> + <li><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/Object">Object</a> - obiekty</li> +</ul> + +<p>a także Undefined (wartość niezdefiniowana) i Null, które można uważać za nieco dziwne. Ponadto mamy do dyspozycji tablice - <a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/Array">Array</a> - które są szczególnym rodzajem obiektu. Oprócz tego daty - <a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/Date">Date</a> i <a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/RegExp">wyrażenia regularne</a>, które także są obiektami. Gwoli ścisłości, funkcje także są szczególnymi obiektami.</p> + +<p>Diagram typów wygląda zatem następująco:</p> + +<ul> + <li>Number</li> + <li>String</li> + <li>Boolean</li> + <li>Object + <ul> + <li>Function</li> + <li>Array</li> + <li>Date</li> + <li>RegExp</li> + </ul> + </li> + <li>Null</li> + <li>Undefined</li> +</ul> + +<p>Istnieją także podtypy typu <a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/Error">Error</a>, ale pomińmy je, żeby za bardzo nie mieszać i pozostańmy przy pierwszym diagramie.</p> + +<h3 id="Liczby" name="Liczby">Liczby</h3> + +<p>Zgodnie ze specyfikacją, liczby w JavaScripcie są "64-bitowymi wartościami podwójnej precyzji w formacie IEEE 754". Ma to ciekawe skutki. Na przykład, w JavaScripcie nie ma czegoś takiego jak liczba całkowita, dlatego trzeba zachować nieco ostrożności przy operacjach arytmetycznych, zwłaszcza jeśli przyzwyczajeni jesteśmy do C lub Javy. Należy uważać na takie sytuacje:</p> + +<pre class="eval">0.1 + 0.2 = 0.30000000000000004 +</pre> + +<p>Dostępne są standardowe <a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Operatory/Operatory_arytmetyczne">operatory arytmetyczne</a>, w tym oczywiście dodawanie, odejmowanie, modulo (reszta z dzielenia) itd. Jest także specjalny obiekt <a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/Math">Math</a> pozwalający na stosowanie bardziej zaawansowanych funkcji i stałych matematycznych:</p> + +<pre class="eval">Math.sin(3.5); +d = Math.PI * r * r; +</pre> + +<p>Ciąg znaków można skonwertować do liczby korzystając z wbudowanej funkcji <code><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Funkcje/parseInt">parseInt()</a></code>. Funkcja ta może posiadać dodatkowy, drugi argument, który określa podstawę systemu dla tej konwersji. Ten argument warto jednak zawsze podawać:</p> + +<pre class="eval">> parseInt("123", 10) +123 +> parseInt("010", 10) +10 +</pre> + +<p>...żeby nie dostać nieoczekiwanych wyników:</p> + +<pre class="eval">> parseInt("010") +8 +</pre> + +<p>Taki wynik otrzymaliśmy dlatego, że funkcja <code>parseInt</code> uznała liczbę w naszym ciągu znaków za ósemkową, bo rozpoczęliśmy ją od znaku "0".</p> + +<p>Jeśli chcesz skonwertować liczbę binarną do dziesiętnej, wystarczy zmienić podstawę:</p> + +<pre class="eval">> parseInt("11", 2) +3 +</pre> + +<p>Funkcja zwraca specjalną wartość <code><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/W%c5%82asno%c5%9bci/NaN">NaN</a></code> (skrót ang. "Not a Number" - "To nie jest liczba"), jeśli dany ciąg znaków nie jest liczbą:</p> + +<pre class="eval">> parseInt("witaj", 10) +NaN +</pre> + +<p><code>NaN</code> jest toksyczna: wprowadzenie jej do jakiejkolwiek operacji matematycznej spowoduje, że jej wynikiem również będzie <code>NaN</code>:</p> + +<pre class="eval">> NaN + 5 +NaN +</pre> + +<p>Aby sprawdzić, czy dana wartość jest <code>NaN</code>, można użyć wbudowanej funkcji <code><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Funkcje/isNaN">isNaN()</a></code>:</p> + +<pre class="eval">> isNaN(NaN) +true +</pre> + +<p>(NaN nie jest bowiem równe nawet samo sobie, <code>NaN!=NaN</code> - przyp. tłum.)</p> + +<p>W JavaScripcie istnieją także specjalne wartości reprezentujące plus i minus nieskończoność: <code><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/W%c5%82asno%c5%9bci/Infinity">Infinity</a></code> and <code>-Infinity</code>:</p> + +<pre class="eval">> 1 / 0 +Infinity +> -1 / 0 +-Infinity +</pre> + +<h3 id="Ci.C4.85gi_znak.C3.B3w" name="Ci.C4.85gi_znak.C3.B3w">Ciągi znaków</h3> + +<p>Teksty w JavaScripcie to sekwencje znaków. Dokładniej rzecz biorąc, są to sekwencje <a href="pl/Przewodnik_po_j%c4%99zyku_JavaScript_1.5/Unicode">znaków Unicode</a>, w których każdy znak reprezentowany jest przez liczbę 16-bitową. To dobra wiadomość dla wszystkich osób zajmujących się internacjonalizacją.</p> + +<p>Jeśli zajdzie potrzeba reprezentowania pojedynczego znaku, stosuje się po prostu ciąg o długości równej 1.</p> + +<p>Aby odnaleźć długość ciągu, należy skorzystać z jego własności <code><a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/String/length">length</a></code>:</p> + +<pre class="eval">> "witaj".length +5 +</pre> + +<p>To nasze pierwsze spotkanie z obiektami JavaScriptu! Czy wspomniałem, że ciągi są też obiektami? Naturalnie, mają też <a href="pl/Dokumentacja_j%c4%99zyka_JavaScript_1.5/Obiekty/String#Metody">metody</a>:</p> + +<pre class="eval">> "witaj".charAt(0) +w +> "witaj, świecie".replace("witaj", "do zobaczenia") +do zobaczenia, świecie +> "witaj".toUpperCase() +WITAJ +</pre> + +<h3 id="Other_types" name="Other_types">Other types</h3> + +<p>JavaScript distinguishes between <code>null</code>, which is an object of type 'object' that indicates a deliberate non-value, and <code>undefined</code>, which is an object of type 'undefined' that indicates an uninitialized value — that is, a value hasn't even been assigned yet. We'll talk about variables later, but in JavaScript it is possible to declare a variable without assigning a value to it. If you do this, the variable's type is <code>undefined</code>.</p> + +<p>JavaScript has a boolean type, with possible values <code>true</code> and <code>false</code> (both of which are keywords). Any value can be converted to a boolean according to the following rules:</p> + +<ol> + <li><code>false</code>, <code>0</code>, the empty string (<code>""</code>), <code>NaN</code>, <code>null</code>, and <code>undefined</code> all become <code>false</code></li> + <li>all other values become <code>true</code></li> +</ol> + +<p>You can perform this conversion explicitly using the <code>Boolean()</code> function:</p> + +<pre class="eval">> Boolean("") +false +> Boolean(234) +true +</pre> + +<p>However, this is rarely necessary, as JavaScript will silently perform this conversion when it expects a boolean, such as in an <code>if</code> statement (see below). For this reason, we sometimes speak simply of "true values" and "false values," meaning values that become <code>true</code> and <code>false</code>, respectively, when converted to booleans. Alternatively, such values can be called "truthy" and "falsy," respectively.</p> + +<p>Boolean operations such as <code>&&</code> (logical<em>and</em>), <code>||</code> (logical<em>or</em>), and <code>!</code> (logical<em>not</em>) are supported; see below.</p> + +<h3 id="Variables" name="Variables">Variables</h3> + +<p>New variables in JavaScript are declared using the <code><a href="pl/Core_JavaScript_1.5_Reference/Statements/var">var</a></code> keyword:</p> + +<pre class="eval">var a; +var name = "simon"; +</pre> + +<p>If you declare a variable without assigning any value to it, its type is <code>undefined</code>. <span class="comment">should note the absence of block-scoping in JS</span></p> + +<h3 id="Operators" name="Operators">Operators</h3> + +<p>JavaScript's numeric operators are <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code> and <code>%</code> - which is the remainder operator. Values are assigned using <code>=</code>, and there are also compound assignment statements such as <code>+=</code> and <code>-=</code>. These extend out to <code>x = x<em>operator</em> y</code>.</p> + +<pre class="eval">x += 5 +x = x + 5 +</pre> + +<p>You can use <code>++</code> and <code>--</code> to increment and decrement respectively. These can be used as prefix or postfix operators.</p> + +<p>The <a href="pl/Core_JavaScript_1.5_Reference/Operators/String_Operators"><code>+</code> operator</a> also does string concatenation:</p> + +<pre class="eval">> "hello" + " world" +hello world +</pre> + +<p>If you add a string to a number (or other value) everything is converted in to a string first. This might catch you out:</p> + +<pre class="eval">> "3" + 4 + 5 +345 +> 3 + 4 + "5" +75 +</pre> + +<p>Adding an empty string to something is a useful way of converting it.</p> + +<p><a href="pl/Core_JavaScript_1.5_Reference/Operators/Comparison_Operators">Comparisons</a> in JavaScript can be made using <code><</code>, <code>></code>, <code><=</code> and <code>>=</code>. These work for both strings and numbers. Equality is a little less straightforward. The double-equals operator performs type coercion if you give it different types, with sometimes interesting results:</p> + +<pre class="eval">> "dog" == "dog" +true +> 1 == true +true +</pre> + +<p>To avoid type coercion, use the triple-equals operator:</p> + +<pre class="eval">> 1 === true +false +> true === true +true +</pre> + +<p>There are also <code>!=</code> and <code>!==</code> operators.</p> + +<p>JavaScript also has <a href="pl/Core_JavaScript_1.5_Reference/Operators/Bitwise_Operators">bitwise operations</a>. If you want to use them, they're there.</p> + +<h3 id="Control_structures" name="Control_structures">Control structures</h3> + +<p>JavaScript has a similar set of control structures to other languages in the C family. Conditional statements are supported by <code>if</code> and <code>else</code>; you can chain them together if you like:</p> + +<pre class="eval">var name = "kittens"; +if (name == "puppies") { + name += "!"; +} else if (name == "kittens") { + name += "!!"; +} else { + name = "!" + name; +} +name == "kittens!!" +</pre> + +<p>JavaScript has <code>while</code> loops and <code>do-while</code> loops. The first is good for basic looping; the second for loops where you wish to ensure that the body of the loop is executed at least once:</p> + +<pre class="eval">while (true) { + // an infinite loop! +} + +do { + var input = get_input(); +} while (inputIsNotValid(input)) +</pre> + +<p>JavaScript's <code>for</code> loop is the same as that in C and Java: it lets you provide the control information for your loop on a single line.</p> + +<pre class="eval">for (var i = 0; i < 5; i++) { + // Will execute 5 times +} +</pre> + +<p>The <code>&&</code> and <code>||</code> operators use short-circuit logic, which means whether they will execute their second operand is dependent on the first. This is useful for checking for null objects before accessing their attributes:</p> + +<pre class="eval">var name = o && o.getName(); +</pre> + +<p>Or for setting default values:</p> + +<pre class="eval">var name = otherName || "default"; +</pre> + +<p>JavaScript has a tertiary operator for one-line conditional statements:</p> + +<pre class="eval">var allowed = (age > 18) ? "yes" : "no"; +</pre> + +<p>The switch statement can be used for multiple branches based on a number or string:</p> + +<pre class="eval">switch(action) { + case 'draw': + drawit(); + break; + case 'eat': + eatit(); + break; + default: + donothing(); +} +</pre> + +<p>If you don't add a <code>break</code> statement, execution will "fall through" to the next level. This is very rarely what you want - in fact it's worth specifically labelling deliberate fallthrough with a comment if you really meant it to aid debugging:</p> + +<pre class="eval">switch(a) { + case 1: // fallthrough + case 2: + eatit(); + break; + default: + donothing(); +} +</pre> + +<p>The default clause is optional. You can have expressions in both the switch part and the cases if you like; comparisons take place between the two using the <code>===</code> operator:</p> + +<pre class="eval">switch(1 + 3): + case 2 + 2: + yay(); + break; + default: + neverhappens(); +} +</pre> + +<h3 id="Objects" name="Objects">Objects</h3> + +<p>JavaScript objects are simply collections of name-value pairs. As such, they are similar to:</p> + +<ul> + <li>Dictionaries in Python</li> + <li>Hashes in Perl and Ruby</li> + <li>Hash tables in C and C++</li> + <li>HashMaps in Java</li> + <li>Associative arrays in PHP</li> +</ul> + +<p>The fact that this data structure is so widely used is a testament to its versatility. Since everything (bar core types) in JavaScript is an object, any JavaScript program naturally involves a great deal of hash table lookups. It's a good thing they're so fast!</p> + +<p>The "name" part is a JavaScript string, while the value can be any JavaScript value - including more objects. This allows you to build data structures of arbitrary complexity.</p> + +<p>There are two basic ways to create an empty object:</p> + +<pre class="eval">var obj = new Object(); +</pre> + +<p>And:</p> + +<pre class="eval">var obj = {}; +</pre> + +<p>These are semantically equivalent; the second is called object literal syntax, and is more convenient. Object literal syntax was not present in very early versions of the language which is why you see so much code using the old method.</p> + +<p>Once created, an object's properties can again be accessed in one of two ways:</p> + +<pre class="eval">obj.name = "Simon" +var name = obj.name; +</pre> + +<p>And...</p> + +<pre class="eval">obj["name"] = "Simon"; +var name = obj["name"]; +</pre> + +<p>These are also semantically equivalent. The second method has the advantage that the name of the property is provided as a string, which means it can be calculated at run-time. It can also be used to set and get properties with names that are <a href="pl/Core_JavaScript_1.5_Reference/Reserved_Words">reserved words</a>:</p> + +<pre class="eval">obj.for = "Simon"; // Syntax error, because 'for' is a reserved word +obj["for"] = "Simon"; // works fine +</pre> + +<p>Object literal syntax can be used to initialise an object in its entirety:</p> + +<pre class="eval">var obj = { + name: "Carrot", + "for": "Max", + details: { + color: "orange", + size: 12 + } +} +</pre> + +<p>Attribute access can be chained together:</p> + +<pre class="eval">> obj.details.color +orange +> obj["details"]["size"] +12 +</pre> + +<h3 id="Arrays" name="Arrays">Arrays</h3> + +<p>Arrays in JavaScript are actually a special type of object. They work very much like regular objects (numerical properties can naturally be accessed only using [] syntax) but they have one magic property called '<code>length</code>'. This is always one more than the highest index in the array.</p> + +<p>The old way of creating arrays is as follows:</p> + +<pre class="eval">> var a = new Array(); +> a[0] = "dog"; +> a[1] = "cat"; +> a[2] = "hen"; +> a.length +3 +</pre> + +<p>A more convenient notation is to use an array literal:</p> + +<pre class="eval">> var a = ["dog", "cat", "hen"]; +> a.length +3 +</pre> + +<p>Leaving a trailing comma at the end of an array literal is inconsistent across browsers, so don't do it.</p> + +<p>Note that <code>array.length</code> isn't necessarily the number of items in the array. Consider the following:</p> + +<pre class="eval">> var a = ["dog", "cat", "hen"]; +> a[100] = "fox"; +> a.length +101 +</pre> + +<p>Remember - the length of the array is one more than the highest index.</p> + +<p>If you query a non-existent array index, you get <code>undefined</code>:</p> + +<pre class="eval">> typeof(a[90]) +undefined +</pre> + +<p>If you take the above into account, you can iterate over an array using the following:</p> + +<pre class="eval">for (var i = 0; i < a.length; i++) { + // Do something with a[i] +} +</pre> + +<p>This is slightly inefficient as you are looking up the length property once every loop. An improvement is this:</p> + +<pre class="eval">for (var i = 0, len = a.length; i < len; i++) { + // Do something with a[i] +} +</pre> + +<p>An even nicer idiom is:</p> + +<pre class="eval">for (var i = 0, item; item = a[i]; i++) { + // Do something with item +} +</pre> + +<p>Here we are setting up two variables. The assignment in the middle part of the <code>for</code> loop is also tested for truthfulness - if it succeeds, the loop continues. Since <code>i</code> is incremented each time, items from the array will be assigned to item in sequential order. The loop stops when a "falsy" item is found (such as <code>undefined</code>).</p> + +<p>Note that this trick should only be used for arrays which you know do not contain "falsy" values (arrays of objects or <a href="pl/DOM">DOM</a> nodes for example). If you are iterating over numeric data that might include a 0 or string data that might include the empty string you should use the <code>i, j</code> idiom instead.</p> + +<p>Another way to iterate is to use the <code><a href="pl/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a></code> loop. Note that if someone added new properties to <code>Array.prototype</code>, they will also be iterated over by this loop:</p> + +<pre class="eval">for (var i in a) { + // Do something with a[i] +} +</pre> + +<p>If you want to append an item to an array, the safest way to do it is like this:</p> + +<pre class="eval">a[a.length] = item; // same as a.push(item); +</pre> + +<p>Since <code>a.length</code> is one more than the highest index, you can be assured that you are assigning to an empty position at the end of the array.</p> + +<p>Arrays come with a number of methods:</p> + +<pre class="eval">a.toString(), a.toLocaleString(), a.concat(item, ..), a.join(sep), +a.pop(), a.push(item, ..), a.reverse(), a.shift(), a.slice(start, end), +a.sort(cmpfn), a.splice(start, delcount, [item]..), a.unshift([item]..) +</pre> + +<ul> + <li><code>concat</code> returns a new array with the items added on to it.</li> + <li><code>pop</code> removes and returns the last item</li> + <li><code>push</code> adds one or more items to the end (like our <code>ar{{ mediawiki.external('ar.length') }}</code> idiom)</li> + <li><code>slice</code> returns a sub-array</li> + <li><code>sort</code> takes an optional comparison function</li> + <li><code>splice</code> lets you modify an array by deleting a section and replacing it with more items</li> + <li><code>unshift</code> prepends items to the start of the array</li> +</ul> + +<h3 id="Functions" name="Functions">Functions</h3> + +<p>Along with objects, functions are the core component in understanding JavaScript. The most basic function couldn't be much simpler:</p> + +<pre class="eval">function add(x, y) { + var total = x + y; + return total; +} +</pre> + +<p>This demonstrates everything there is to know about basic functions. A JavaScript function can take 0 or more named parameters. The function body can contain as many statements as you like, and can declare its own variables which are local to that function. The <code>return</code> statement can be used to return a value at any time, terminating the function. If no return statement is used (or an empty return with no value), JavaScript returns <code>undefined</code>.</p> + +<p>The named parameters turn out to be more like guidelines than anything else. You can call a function without passing the parameters it expects, in which case they will be set to <code>undefined</code>.</p> + +<pre class="eval">> add() +NaN // You can't perform addition on undefined +</pre> + +<p>You can also pass in more arguments than the function is expecting:</p> + +<pre class="eval">> add(2, 3, 4) +5 // added the first two; 4 was ignored +</pre> + +<p>That may seem a little silly, but functions have access to an additional variable inside their body called <a href="pl/Core_JavaScript_1.5_Reference/Functions/arguments"><code>arguments</code></a>, which is an array-like object holding all of the values passed to the function. Let's re-write the add function to take as many values as we want:</p> + +<pre class="eval">function add() { + var sum = 0; + for (var i = 0, j = arguments.length; i < j; i++) { + sum += arguments[i]; + } + return sum; +} + +> add(2, 3, 4, 5) +14 +</pre> + +<p>That's really not any more useful than writing <code>2 + 3 + 4 + 5</code> though. Let's create an averaging function:</p> + +<pre class="eval">function avg() { + var sum = 0; + for (var i = 0, j = arguments.length; i < j; i++) { + sum += arguments[i]; + } + return sum / arguments.length; +} +> avg(2, 3, 4, 5) +3.5 +</pre> + +<p>This is pretty useful, but introduces a new problem. The <code>avg()</code> function takes a comma separated list of arguments - but what if you want to find the average of an array? You could just rewrite the function as follows:</p> + +<pre class="eval">function avgArray(arr) { + var sum = 0; + for (var i = 0, j = arr.length; i < j; i++) { + sum += arr[i]; + } + return sum / arr.length; +} +> avgArray([2, 3, 4, 5]) +3.5 +</pre> + +<p>But it would be nice to be able to reuse the function that we've already created. Luckily, JavaScript lets you call a function and call it with an arbitrary array of arguments, using the <a href="pl/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply"><code>apply()</code></a> method of any function object.</p> + +<pre class="eval">> avg.apply(null, [2, 3, 4, 5]) +3.5 +</pre> + +<p>The second argument to <code>apply()</code> is the array to use as arguments; the first will be discussed later on. This emphasizes the fact that functions are objects too.</p> + +<p>JavaScript lets you create anonymous functions.</p> + +<pre class="eval">var avg = function() { + var sum = 0; + for (var i = 0, j = arguments.length; i < j; i++) { + sum += arguments[i]; + } + return sum / arguments.length; +} +</pre> + +<p>This is semantically equivalent to the <code>function avg()</code> form. It's extremely powerful, as it lets you put a full function definition anywhere that you would normally put an expression. This enables all sorts of clever tricks. Here's a way of "hiding" some local variables - like block scope in C:</p> + +<pre class="eval">> var a = 1; +> var b = 2; +> (function() { + var b = 3; + a += b; +})(); +> a +4 +> b +2 +</pre> + +<p>JavaScript allows you to call functions recursively. This is particularly useful for dealing with tree structures, such as you get in the browser <a href="pl/DOM">DOM</a>.</p> + +<pre class="eval">function countChars(elm) { + if (elm.nodeType == 3) { // TEXT_NODE + return elm.nodeValue.length; + } + var count = 0; + for (var i = 0, child; child = elm.childNodes[i]; i++) { + count += countChars(child); + } + return count; +} +</pre> + +<p>This highlights a potential problem with anonymous functions: how do you call them recursively if they don't have a name? The answer lies with the <code>arguments</code> object, which in addition to acting as a list of arguments also provides a property called <code>arguments.callee</code>. This always refers to the current function, and hence can be used to make recursive calls:</p> + +<pre class="eval">var charsInBody = (function(elm) { + if (elm.nodeType == 3) { // TEXT_NODE + return elm.nodeValue.length; + } + var count = 0; + for (var i = 0, child; child = elm.childNodes[i]; i++) { + count += arguments.callee(child); + } + return count; +})(document.body); +</pre> + +<p>Since <code>arguments.callee</code> is the current function, and all functions are objects, you can use <code>arguments.callee</code> to save information across multiple calls to the same function. Here's a function that remembers how many times it has been called:</p> + +<pre class="eval">function counter() { + if (!arguments.callee.count) { + arguments.callee.count = 0; + } + return arguments.callee.count++; +} + +> counter() +0 +> counter() +1 +> counter() +2 +</pre> + +<h3 id="Custom_objects" name="Custom_objects">Custom objects</h3> + +<p>In classic Object Oriented Programming, objects are collections of data and methods that operate on that data. Let's consider a person object with first and last name fields. There are two ways in which their name might be displayed: as "first last" or as "last, first". Using the functions and objects that we've discussed previously, here's one way of doing it:</p> + +<pre class="eval">function makePerson(first, last) { + return { + first: first, + last: last + } +} +function personFullName(person) { + return person.first + ' ' + person.last; +} +function personFullNameReversed(person) { + return person.last + ', ' + person.first +} +> s = makePerson("Simon", "Willison"); +> personFullName(s) +Simon Willison +> personFullNameReversed(s) +Willison, Simon +</pre> + +<p>This works, but it's pretty ugly. You end up with dozens of functions in your global namespace. What we really need is a way to attach a function to an object. Since functions are objects, this is easy:</p> + +<pre class="eval">function makePerson(first, last) { + return { + first: first, + last: last, + fullName: function() { + return this.first + ' ' + this.last; + }, + fullNameReversed: function() { + return this.last + ', ' + this.first; + } + } +} +> s = makePerson("Simon", "Willison") +> s.fullName() +Simon Willison +> s.fullNameReversed() +Willison, Simon +</pre> + +<p>There's something here we haven't seen before: the '<code><a href="pl/Core_JavaScript_1.5_Reference/Operators/Special_Operators/this_Operator">this</a></code>' keyword. Used inside a function, '<code>this</code>' refers to the current object. What that actually means is specified by the way in which you called that function. If you called it using <a href="pl/Core_JavaScript_1.5_Reference/Operators/Member_Operators">dot notation or bracket notation</a> on an object, that object becomes '<code>this</code>'. If dot notation wasn't used for the call, '<code>this</code>' refers to the global object. This is a frequent cause of mistakes. For example:</p> + +<pre class="eval">> s = makePerson("Simon", "Willison") +> var fullName = s.fullName; +> fullName() +undefined undefined +</pre> + +<p>When we call <code>fullName()</code>, '<code>this</code>' is bound to the global object. Since there are no global variables called <code>first</code> or <code>last</code> we get <code>undefined</code> for each one.</p> + +<p>We can take advantage of the '<code>this</code>' keyword to improve our <code>makePerson</code> function:</p> + +<pre class="eval">function Person(first, last) { + this.first = first; + this.last = last; + this.fullName = function() { + return this.first + ' ' + this.last; + } + this.fullNameReversed = function() { + return this.last + ', ' + this.first; + } +} +var s = new Person("Simon", "Willison"); +</pre> + +<p>We've introduced another keyword: '<code><a href="pl/Core_JavaScript_1.5_Reference/Operators/Special_Operators/new_Operator">new</a></code>'. <code>new</code> is strongly related to '<code>this</code>'. What it does is it creates a brand new empty object, and then calls the function specified, with '<code>this</code>' set to that new object. Functions that are designed to be called by '<code>new</code>' are called constructor functions. Common practise is to capitalise these functions as a reminder to call them with <code>new</code>.</p> + +<p>Our person objects are getting better, but there are still some ugly edges to them. Every time we create a person object we are creating two brand new function objects within it - wouldn't it be better if this code was shared?</p> + +<pre class="eval">function personFullName() { + return this.first + ' ' + this.last; +} +function personFullNameReversed() { + return this.last + ', ' + this.first; +} +function Person(first, last) { + this.first = first; + this.last = last; + this.fullName = personFullName; + this.fullNameReversed = personFullNameReversed; +} +</pre> + +<p>That's better: we are creating the method functions only once, and assigning references to them inside the constructor. Can we do any better than that? The answer is yes:</p> + +<pre class="eval">function Person(first, last) { + this.first = first; + this.last = last; +} +Person.prototype.fullName = function() { + return this.first + ' ' + this.last; +} +Person.prototype.fullNameReversed = function() { + return this.last + ', ' + this.first; +} +</pre> + +<p><code>Person.prototype</code> is an object shared by all instances of <code>Person</code>. It forms part of a lookup chain (that has a special name, "prototype chain"): any time you attempt to access a property of <code>Person</code> that isn't set, JavaScript will check <code>Person.prototype</code> to see if that property exists there instead. As a result, anything assigned to <code>Person.prototype</code> becomes available to all instances of that constructor via the <code>this</code> object.</p> + +<p>This is an incredibly powerful tool. JavaScript lets you modify something's prototype at any time in your program, which means you can add extra methods to existing objects at runtime:</p> + +<pre class="eval">> s = new Person("Simon", "Willison"); +> s.firstNameCaps(); +TypeError on line 1: s.firstNameCaps is not a function +> Person.prototype.firstNameCaps = function() { + return this.first.toUpperCase() +} +> s.firstNameCaps() +SIMON +</pre> + +<p>Interestingly, you can also add things to the prototype of built-in JavaScript objects. Let's add a method to <code>String</code> that returns that string in reverse:</p> + +<pre class="eval">> var s = "Simon"; +> s.reversed() +TypeError on line 1: s.reversed is not a function +> String.prototype.reversed = function() { + var r = ""; + for (var i = this.length - 1; i >= 0; i--) { + r += this[i]; + } + return r; +} +> s.reversed() +nomiS +</pre> + +<p>Our new method even works on string literals!</p> + +<pre class="eval">> "This can now be reversed".reversed() +desrever eb won nac sihT +</pre> + +<p>As I mentioned before, the prototype forms part of a chain. The root of that chain is <code>Object.prototype</code>, whose methods include <code>toString()</code> - it is this method that is called when you try to represent an object as a string. This is useful for debugging our <code>Person</code> objects:</p> + +<pre class="eval">> var s = new Person("Simon", "Willison"); +> s +[object Object] +> Person.prototype.toString = function() { + return '<Person: ' + this.fullName() + '>'; +} +> s +<Person: Simon Willison> +</pre> + +<p>Remember how <code>avg.apply()</code> had a null first argument? We can revisit that now. The first argument to <code>apply()</code> is the object that should be treated as '<code>this</code>'. For example, here's a trivial implementation of '<code>new</code>':</p> + +<pre class="eval">function trivialNew(constructor) { + var o = {}; // Create an object + constructor.apply(o, arguments); + return o; +} +</pre> + +<p>This isn't an exact replica of <code>new</code> as it doesn't set up the prototype chain. <code>apply()</code> is difficult to illustrate - it's not something you use very often, but it's useful to know about.</p> + +<p><code>apply()</code> has a sister function named <a href="pl/Core_JavaScript_1.5_Reference/Global_Objects/Function/call"><code>call</code></a>, which again lets you set '<code>this</code>' but takes an expanded argument list as opposed to an array.</p> + +<pre class="eval">function lastNameCaps() { + return this.last.toUpperCase(); +} +var s = new Person("Simon", "Willison"); +lastNameCaps.call(s); +// Is the same as: +s.lastNameCaps = lastNameCaps; +s.lastNameCaps(); +</pre> + +<h3 id="Inner_functions" name="Inner_functions">Inner functions</h3> + +<p>JavaScript function declarations are allowed inside other functions. We've seen this once before, with an earlier <code>makePerson()</code> function. An important detail of nested functions in JavaScript is that they can access variables in their parent function's scope:</p> + +<pre class="eval">function betterExampleNeeded() { + var a = 1; + function oneMoreThanA() { + return a + 1; + } + return oneMoreThanA(); +} +</pre> + +<p>This provides a great deal of utility in writing more maintainable code. If a function relies on one or two other functions that are not useful to any other part of your code, you can nest those utility functions inside the function that will be called from elsewhere. This keeps the number of functions that are in the global scope down, which is always a good thing.</p> + +<p>This is also a great counter to the lure of global variables. When writing complex code it is often tempting to use global variables to share values between multiple functions - which leads to code that is hard to maintain. Nested functions can share variables in their parent, so you can use that mechanism to couple functions together when it makes sense without polluting your global namespace - 'local globals' if you like. This technique should be used with caution, but it's a useful ability to have.</p> + +<h3 id="Closures" name="Closures">Closures</h3> + +<p>This leads us to one of the most powerful abstractions that JavaScript has to offer - but also the most potentially confusing. What does this do?</p> + +<pre class="eval">function makeAdder(a) { + return function(b) { + return a + b; + } +} +x = makeAdder(5); +y = makeAdder(20); +x(6) +? +y(7) +? +</pre> + +<p>The name of the <code>makeAdder</code> function should give it away: it creates new 'adder' functions, which when called with one argument add it to the argument that they were created with.</p> + +<p>What's happening here is pretty much the same as was happening with the inner functions earlier on: a function defined inside another function has access to the outer function's variables. The only difference here is that the outer function has returned, and hence common sense would seem to dictate that its local variables no longer exist. But they<em>do</em> still exist - otherwise the adder functions would be unable to work. What's more, there are two different "copies" of <code>makeAdder</code>'s local variables - one in which <code>a</code> is 5 and one in which <code>a</code> is 20.</p> + +<p>Here's what's actually happening. Whenever JavaScript executes a function, a 'scope' object is created to hold the local variables created within that function. It is initialised with any variables passed in as function parameters. This is similar to the global object that all global variables and functions live in, but with a couple of important differences: firstly, a brand new scope object is created every time a function starts executing, and secondly, unlike the global object (which in browsers is accessible as window) these scope objects cannot be directly accessed from your JavaScript code. There is no mechanism for iterating over the properties of the current scope object for example.</p> + +<p>So when <code>makeAdder</code> is called, a scope object is created with one property: <code>a</code>, which is the argument passed to the <code>makeAdder</code> function. <code>makeAdder</code> then returns a newly created function. Normally JavaScript's garbage collector would clean up the scope object created for <code>makeAdder</code> at this point, but the returned function maintains a reference back to that scope object. As a result, the scope object will not be garbage collected until there are no more references to the function object that <code>makeAdder</code> returned.</p> + +<p>Scope objects form a chain called the scope chain, similar to the prototype chain used by JavaScript's object system.</p> + +<p>A closure is the combination of a function and the scope object in which it was created.</p> + +<p>Closures let you save state - as such, they can often be used in place of objects.</p> + +<h3 id="Memory_leaks" name="Memory_leaks">Memory leaks</h3> + +<p>An unfortunate side effect of closures is that they make it trivially easy to leak memory in Internet Explorer. JavaScript is a garbage collected language - objects are allocated memory upon their creation and that memory is reclaimed by the browser when no references to an object remain. Objects provided by the host environment are handled by that environment.</p> + +<p>Browser hosts need to manage a large number of objects representing the HTML page being presented - the objects of the <a href="pl/DOM">DOM</a>. It is up to the browser to manage the allocation and recovery of these.</p> + +<p>Internet Explorer uses its own garbage collection scheme for this, separate from the mechanism used by JavaScript. It is the interaction between the two that can cause memory leaks.</p> + +<p>A memory leak in IE occurs any time a circular reference is formed between a JavaScript object and a native object. Consider the following:</p> + +<pre class="eval">function leakMemory() { + var el = document.getElementById('el'); + var o = { 'el': el }; + el.o = o; +} +</pre> + +<p>The circular reference formed above creates a memory leak; IE will not free the memory used by <code>el</code> and <code>o</code> until the browser is completely restarted.</p> + +<p>The above case is likely to go unnoticed; memory leaks only become a real concern in long running applications or applications that leak large amounts of memory due to large data structures or leak patterns within loops.</p> + +<p>Leaks are rarely this obvious - often the leaked data structure can have many layers of references, obscuring the circular reference.</p> + +<p>Closures make it easy to create a memory leak without meaning to. Consider this:</p> + +<pre class="eval">function addHandler() { + var el = document.getElementById('el'); + el.onclick = function() { + this.style.backgroundColor = 'red'; + } +} +</pre> + +<p>The above code sets up the element to turn red when it is clicked. It also creates a memory leak. Why? Because the reference to <code>el</code> is inadvertently caught in the closure created for the anonymous inner function. This creates a circular reference between a JavaScript object (the function) and a native object (<code>el</code>).</p> + +<p>There are a number of workarounds for this problem. The simplest is this:</p> + +<pre class="eval">function addHandler() { + var el = document.getElementById('el'); + el.onclick = function() { + this.style.backgroundColor = 'red'; + } + el = null; +} +</pre> + +<p>This works by breaking the circular reference.</p> + +<p>Surprisingly, one trick for breaking circular references introduced by a closure is to add another closure:</p> + +<pre class="eval">function addHandler() { + var clickHandler = function() { + this.style.backgroundColor = 'red'; + } + (function() { + var el = document.getElementById('el'); + el.onclick = clickHandler; + })(); +} +</pre> + +<p>The inner function is executed straight away, and hides its contents from the closure created with <code>clickHandler</code>.</p> + +<p>Another good trick for avoiding closures is breaking circular references during the <code>window.onunload</code> event. Many event libraries will do this for you. Note that doing so disables <a href="pl/Using_Firefox_1.5_caching">bfcache in Firefox 1.5</a>, so you should not register an <code>unload</code> listener in Firefox, unless you have other reasons to do so.</p> + +<div class="originaldocinfo"> +<h2 id="Original_Document_Information" name="Original_Document_Information">Original Document Information</h2> + +<ul> + <li>Author: <a class="external" href="http://simon.incutio.com/">Simon Willison</a></li> + <li>Last Updated Date: March 7, 2006</li> + <li>Copyright: © 2006 Simon Willison, contributed under the Creative Commons: Attribute-Sharealike 2.0 license.</li> + <li>More information: For more information about this tutorial (and for links to the original talk's slides), see Simon's <a class="external" href="http://simon.incutio.com/archive/2006/03/07/etech">Etech weblog post</a>.</li> +</ul> +</div> |