<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>JavaScript Study 2015.11.9--</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style type="text/css"> </style> <script type="text/javascript"> //JavaScript = ECMAScript(function,closure,OO) + DOM + BOM /* var sColor = "red"; //alert(sColor.length); //輸出 "3" var bFound = false; //alert(bFound.toString()); //輸出 "false" var iNum1 = parseInt("12345red"); //返回 12345 var iNum1 = parseInt("red12345"); //返回 NaN //alert(iNum1); //alert(inum1); // error: inum1 is not defined var iNum1 = parseInt("0xA"); //返回 10 var iNum1 = parseInt("56.9"); //返回 56 var iNum1 = parseInt("red"); //返回 NaN //解析16進制的值, 第二個參數 var iNum1 = parseInt("AF", 16); //返回 175 //alert(iNum1); //2進制 var iNum1 = parseInt("10", 2); //返回 2 //8進制 var iNum2 = parseInt("10", 8); //返回 8 //10進制 var iNum3 = parseInt("10", 10); //返回 10 //若是10進制數,包含0做前導,最好使用第二個參數爲10 var iNum1 = parseInt("010"); //返回 8 var iNum2 = parseInt("010", 8); //返回 8 var iNum3 = parseInt("010", 10); //返回 10 var b1 = Boolean(""); //false - 空字符串 var b2 = Boolean("hello"); //true - 非空字符串 var b1 = Boolean(50); //true - 非零數字 var b1 = Boolean(null); //false - null var b1 = Boolean(0); //false - 零 var b1 = Boolean(new Object()); //true - 對象 ECMAScript 中可用的 3 種強制類型轉換以下: Boolean(value) - 把給定的值轉換成 Boolean 型; Number(value) - 把給定的值轉換成數字(能夠是整數或浮點數); String(value) - 把給定的值轉換成字符串; var b1 = Boolean(""); //false - 空字符串 var b2 = Boolean("hello"); //true - 非空字符串 var b3 = Boolean(50); //true - 非零數字 var b4 = Boolean(null); //false - null var b5 = Boolean(0); //false - 零 var b6 = Boolean(new Object()); //true - 對象 document.write(b1+"<br>"); document.write(b2+"<br>"); document.write(b3+"<br>"); document.write(b4+"<br>"); document.write(b5+"<br>"); document.write(b6+"<br>"); //特別注意 document.write(Number(false)+"<br>");// 0 document.write(Number(true)+"<br>");// 1 document.write(Number(undefined)+"<br>");// NaN document.write(Number(null) +"<br>");// 0 document.write(Number("1.2")+"<br>");// 1.2 document.write(Number("12")+"<br>");// 12 document.write(Number("1.2.3")+"<br>");// NaN document.write(Number(new Object())+"<br>");// NaN document.write(Number(50) +"<br>");// 50 var s1 = String(null); //"null" var oNull = null; var s2 = oNull.toString(); //會引起錯誤 //兩種方式均可以 var obj = new Object; var obj1 = new Object(); Object 對象具備下列屬性: constructor 對建立對象的函數的引用(指針)。對於 Object 對象,該指針指向原始的 Object() 函數。 Prototype 對該對象的對象原型的引用。對於全部的對象,它默認返回 Object 對象的一個實例。 Object 對象還具備幾個方法: hasOwnProperty(property) 判斷對象是否有某個特定的屬性。必須用字符串指定該屬性。(例如,o.hasOwnProperty("name")) IsPrototypeOf(object) 判斷該對象是否爲另外一個對象的原型。 PropertyIsEnumerable 判斷給定的屬性是否能夠用 for...in 語句進行枚舉。 ToString() 返回對象的原始字符串表示。對於 Object 對象,ECMA-262 沒有定義這個值,因此不一樣的 ECMAScript 實現具備不一樣的值。 ValueOf() 返回最適合該對象的原始值。對於許多對象,該方法返回的值都與 ToString() 的返回值相同。 */ /* //函數 function sayHi() { if (arguments[0] == "bye") { return; } alert(arguments[0]); } //下面的調用方式都不報錯 sayHi(); sayHi("hehe"); function howManyArgs() { alert(arguments.length); } howManyArgs("string", 45); howManyArgs(); howManyArgs(12); //模擬函數重載 function doAdd() { if(arguments.length == 1) { alert(arguments[0] + 5); } else if(arguments.length == 2) { alert(arguments[0] + arguments[1]); } } doAdd(10); //輸出 "15" doAdd(40, 20); //輸出 "60" function doAdd(){ var j =0; for(var i=0;i<arguments.length;i++){ j= j+ arguments[i]; } alert(j); } doAdd(20); doAdd(20,40); //函數的覆蓋 function doAdd(iNum) { alert(iNum + 20); } function doAdd(iNum) { alert(iNum + 10); } doAdd(10); //輸出 "20" var doAdd = new Function("iNum", "alert(iNum + 20)"); var doAdd = new Function("iNum", "alert(iNum + 10)"); doAdd(10); //畫出內存結構 function fn(){ return 100; } //x 指向 fn 所指向的內存區域 var x = fn; var y = fn(); var z = x(); //定義對象 function Person(name,age,address){ this.name = name; this.age = age; this.address = address; this.say = function(){ alert(this.name+","+this.age+","+this.address); } } var p1 = new Person("p1",23,"bj"); var p2 = new Person("p2",24,"nj"); //存在的問題: //每建立一個對象p,都會在內存中有一份Person的say function拷貝,會致使內存空間的浪費。 //而實際的對象的建立,應該是:每建立一個對象p,只會建立一個變量,指向內存中的say function的同一區域。 //p1.say(); // p2.say(); //定義對象的方式1: function Person(name,age){ this.name = name; this.age = age; } Person.prototype.say = function(){ alert(this.name+","+this.age); } var p1 = new Person("p1",20); p1.say(); var p2 = new Person("p2",21); p2.say(); //定義對象的方式2: person = new Object(); person.name = "li"; person.age = 34; person.say= function(){alert(person.name);} person.say(); //定義對象的方式3: person={ name:"li", age:34, say:function(){ alert(this.name); } } person.say(); var person ={ name:"li", age:34, say:function(){ alert(this.name); } } for(x in person){ alert(x); // name,age,say } */ /* //http://www.jb51.net/article/24101.htm //閉包 閉包,指的是詞法表示包括不被計算的變量的函數,也就是說,函數可使用函數以外定義的變量。 //做用: 一個是前面提到的能夠讀取函數內部的變量, 另外一個就是讓這些變量的值始終保持在內存中。 //何時使用: 一、保護函數內的變量安全。以最開始的例子爲例,函數a中i只有函數b才能訪問,而沒法經過其餘途徑訪問到,所以保護了i的安全性。 二、在內存中維持一個變量。依然如前例,因爲閉包,函數a中i的一直存在於內存中,所以每次執行c(),都會給i自加1。 三、經過保護變量的安全實現JS私有屬性和私有方法(不能被外部訪問) 私有屬性和方法在Constructor外是沒法被訪問的 function Constructor(...) { var that = this; var membername = value; function membername(...) {...} } 以上3點是閉包最基本的應用場景,不少經典案例都源於此。 //這是一個最簡單的閉包 var sMessage = "hello closure"; function sayHelloClosure(){ alert(sMessage); } sayHelloClosure(); //在上面這段代碼中,腳本被載入內存後,並無爲函數 sayHelloWorld() 計算變量 sMessage 的值。 //該函數捕獲 sMessage 的值只是爲了之後的使用,也就是說,解釋程序知道在調用該函數時要檢查 sMessage 的值。 //sMessage 將在函數調用 sayHelloWorld() 時(最後一行)被賦值,顯示消息 "hello world"。 var iBaseNum = 10; function addNum(inum1,inum2){ //定義一個閉包 function doAdd(){ return inum1+iNum2+iBaseNum; } //調用 return doAdd(); } //這裏,函數 addNum() 包括函數 doAdd() (閉包)。 //內部函數是一個閉包,由於它將獲取外部函數的參數 iNum1 和 iNum2 以及全局變量 iBaseNum 的值。 //addNum() 的最後一步調用了 doAdd(),把兩個參數和全局變量相加,並返回它們的和。 //這裏要掌握的重要概念是,doAdd() 函數根本不接受參數,它使用的值是從執行環境中獲取的。 //能夠看到,閉包是 ECMAScript 中很是強大多用的一部分,可用於執行復雜的計算。 //提示:就像使用任何高級函數同樣,使用閉包要當心,由於它們可能會變得很是複雜。 //這裏很關鍵,也很難理解,須要畫內存圖。 function foo(x){ var tmp = 3; return function(y){ alert(x + y + (++tmp) ); } } //這裏纔是閉包 var bar = foo(2); // 至關於 var bar = function(y){alert(2+y+(++tmp));} 但仍是有所不一樣。 bar(10);//16 bar(10);//17 因爲tmp仍存在於bar閉包的內部,因此它仍是會自加1,並且你每次調用bar時它都會自加1. //閉包的理解: //http://kb.cnblogs.com/page/105708/ //Javascript的垃圾回收機制 在Javascript中,若是一個對象再也不被引用,那麼這個對象就會被GC回收。 若是兩個對象互相引用,而再也不被第3者所引用,那麼這兩個互相引用的對象也會被回收。 由於函數a被b引用,b又被a外的c引用,這就是爲何函數a執行後不會被回收的緣由。 var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; var func = object.getNameFunc(); //返回匿名的閉包函數 var returnName = func(); //alert(returnName); //alert(object.getNameFunc()()); //The Window 爲何不是: My Object function outerFun() { var a=0; function innerFun() { a++; //a 的值不會被GC回收。 alert(a); } return innerFun; //注意這裏 } var obj = outerFun(); obj(); //結果爲1 obj(); //結果爲2 obj(); //3 .... var obj2 = outerFun(); obj2(); //結果爲1 obj2(); //結果爲2 function outerFun() { var a =0; alert(a); } var a=4; outerFun();//0 alert(a);//40 function outerFun() { //沒有var a =0; alert(a); } var a=4; outerFun();//0 alert(a);//0 //做用域鏈是描述一種路徑的術語,沿着該路徑能夠肯定變量的值 .當執行a=0時,由於沒有使用var關鍵字,所以賦值操做會沿着做用域鏈到var a=4; 並改變其值. */ //做用域鏈 // http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html //OO面向對象 /* function Cat(name,age){ this.name = name; this.age = age; this.type = "動物"; this.say = function(){ alert(this.name); } } var cat1 = new Cat("haha",3); var cat2 = new Cat("wwww",4); 上面的定義方式,存在的問題: cat1, cat2 ...的 type, say 都是相同的內容,在new的時候,會給每一個cat1,cat2,都建立一塊內存,用來存儲type,say. 這就形成了內存空間的浪費。 可使用下面的改良的方式定義: function Cat(name,age){ this.name = name; this.age = age; } Cat.prototype.type = "動物"; Cat.prototype.say = function(){ alert(this.name); } */ /* 上面的定義方式,存在的問題: 雖然解決了內存空間浪費的問題,但在形式上看起來,並不像java裏的Class的定義方式。 可使用下面的改良的方式定義: function Cat(name,age){ this.name = name; this.age = age; if(!this.type) Cat.prototype.type = "動物"; if(!this.say){ Cat.prototype.say = function(){ alert(this.name); } } } var cat1 = new Cat("haha",3); var cat2 = new Cat("wwww",4); cat1.say(); cat2.say(); */ //繼承 //http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html /* // 1、 構造函數綁定 //第一種方法也是最簡單的方法,使用call或apply方法,將父對象的構造函數綁定在子對象上,即在子對象構造函數中加一行: //父類 function Animal(){ this.species = "動物"; } //子類 function Cat(name,color){ Animal.apply(this, arguments);// this.name = name; this.color = color; } var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物 // 2、 prototype模式 //第二種方法更常見,使用prototype屬性。 //若是"貓"的prototype對象,指向一個Animal的實例,那麼全部"貓"的實例,就能繼承Animal了。 //父類 function Animal(){ this.species = "動物"; } //子類 function Cat(name,color){ this.name = name; this.color = color; } //實現繼承 Cat.prototype = new Animal();//重寫了Cat.prototype Cat.prototype.constructor = Cat;//將新的Cat.prototype.constructor再指向Cat var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物 // 3、 直接繼承prototype (有問題) //第三種方法是對第二種方法的改進。因爲Animal對象中,不變的屬性均可以直接寫入Animal.prototype。因此,咱們也可讓Cat()跳過 Animal(),直接繼承Animal.prototype。 //如今,咱們先將Animal對象改寫: function Animal(){} Animal.prototype.species = "動物"; //而後,將Cat的prototype對象,而後指向Animal的prototype對象,這樣就完成了繼承。 function Cat(name,color){ this.name = name; this.color = color; } Cat.prototype = Animal.prototype; Cat.prototype.constructor = Cat;//這一句實際上把Animal.prototype對象的constructor屬性也改掉了! var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物 //與前一種方法相比,這樣作的優勢是效率比較高(不用執行和創建Animal的實例了),比較省內存。缺點是 Cat.prototype和Animal.prototype如今指向了同一個對象,那麼任何對Cat.prototype的修改,都會反映到Animal.prototype。 //因此,上面這一段代碼實際上是有問題的。 //4、 利用空對象做爲中介 //因爲"直接繼承prototype"存在上述的缺點,因此就有第四種方法,利用一個空對象做爲中介。 /* function Animal(){} Animal.prototype.species = "動物"; function Cat(name,color){ this.name = name; this.color = color; } var F = function(){}; F.prototype = Animal.prototype; Cat.prototype = new F(); Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物 //F是空對象,因此幾乎不佔內存。這時,修改Cat的prototype對象,就不會影響到Animal的prototype對象。 // function Animal(){} Animal.prototype.species = "動物"; function Cat(name,color){ this.name = name; this.color = color; } function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; } extend(Cat,Animal);//YUI提供的實現繼承的方式 var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物 //5、 拷貝繼承 //上面是採用prototype對象,實現繼承。咱們也能夠換一種思路,純粹採用"拷貝"方法實現繼承。簡單說,若是把父對象的全部屬性和方法,拷貝進子對象,不也可以實現繼承嗎?這樣咱們就有了第五種方法。 function Animal(){} Animal.prototype.species = "動物"; function Cat(name,color){ this.name = name; this.color = color; } function extend2(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } c.uber = p; } extend2(Cat, Animal); var cat1 = new Cat("大毛","黃色"); alert(cat1.species); // 動物 */ /* //1、什麼是"非構造函數"的繼承? //好比,如今有一個對象,叫作"中國人"。 var Chinese = { nation:"中國" } var Doctor ={ career:"醫生" } //1, object方法 function object(o) { function F() {} //空對象 F.prototype = o; //父對象 return new F(); } //這個object()函數,其實只作一件事,就是把子對象的prototype屬性,指向父對象,從而使得子對象與父對象連在一塊兒。 //2, 淺拷貝方法 //除了使用"prototype鏈"之外,還有另外一種思路:把父對象的屬性,所有拷貝給子對象,也能實現繼承。 function extendCopy(p){ var c = {}; for(var i in p){ c[i] = p[i]; } return c; } //var Doctor = extendCopy(Chinese); //Doctor.career = '醫生'; //alert(Doctor.nation); // 中國 //可是,這樣的拷貝有一個問題。那就是,若是父對象的屬性等於數組或另外一個對象,那麼實際上,子對象得到的只是一個內存地址,而不是真正拷貝,所以存在父對象被篡改的可能。 Chinese.birthPlaces = ['北京','上海','香港']; var Doctor = extendCopy(Chinese); Doctor.birthPlaces.push('廈門'); alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門 alert(Chinese.birthPlaces); //北京, 上海, 香港, 廈門 //3, 深拷貝方法 (JQuery使用的實現繼承的方式) function deepCopy(p,c){ var c = c || {}; for(var i in p){ if(typeof p[i] === 'object'){ c[i] = (p[i].constructor === Array)? []:{};//? 什麼意思 deepCopy(p[i],c[i]);//遞歸 }else{ c[i] = p[i]; } } return c; } Chinese.birthPlaces = ['北京','上海','香港']; var Doctor = deepCopy(Chinese); Doctor.birthPlaces.push('廈門'); alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門 alert(Chinese.birthPlaces); //北京, 上海, 香港, 廈門 */ </script> </head> <body> <div id="wrap"> </div> </body> </html>