<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript"> 1、函數簡介 函數也是一個對象,也具有普通對象的功能,除了基本數據類型外,一切都是對象 - 函數中可以封裝一些代碼,在需要的時候可以去調用函數來執行這些代碼 - 使用typeof檢查一個函數時會返回function 2、創建函數對象 : 方式1: 開發中很少使用構造函數創建函數對象 可以把封裝的代碼以字符串的形式傳遞給構造函數 var fun = new function ("console.log('Hello')"); 封裝的函數不會立即執行,調用的時候才按照順序執行 調用函數: fun(); 方式2: 使用函數聲明創建一個函數 - 函數聲明 function 函數名([形參1,形參2...形參N]){ 語句... } function f() { console.log("這是一個函數聲明創建函數f()"); } 調用函數: f(); 方式3: 使用函數表達式創建函數 var 函數名 = function([形參1,形參2...形參N]){ 語句... }; var f = function () { console.log("使用匿名函數創建函數賦值給f變量"); }; //本質上此句是一個賦值語句 3、函數形參 function sum(a, b) { // 實參可以是任何類型,也可以是對象() return (a+b); return ; //返回undefined } 調用函數時,不檢查實參的類型和實參的數量 sum('helll',12) var res = sum(2); //2+ undefined = NaN 4、對象做函數實際參數 /* * 創建一個函數,可以在控制台中輸出一個人的信息 * 可以輸出人的 name age gender address * * 實參可以是任意的數據類型,也可以是一個對象 * 當我們的參數過多時,可以將參數封裝到一個對象中,然后通過對象傳遞 */ function sayHello(o){ //console.log("o = "+o); console.log("我是"+o.name+",今年我"+o.age+"歲了,"+"我是一個"+o.gender+"人"+",我住在"+o.address); } //sayHello("豬八戒",28,"高老庄","男"); //創建一個對象 var obj = { name:"孫悟空", age:18, address:"花果山", gender:"男" }; //sayHello(obj); /* * 實參可以是一個對象,也可以是一個函數 */ function fun(a){ console.log("a = "+a); //a(obj); } //fun(sayHello); 等價於 sayHello(obj); //fun(function(){alert("hello")}); 函數對象做函數參數 * sayHello() * - 調用函數 * - 相當於使用的函數的返回值 * * sayHello 函數名 * - 函數對象 * - 相當於直接使用函數對象 5、函數的返回值、 返回值可以是任意類型,可以是對象,也可以是函數 * 如果return語句后不跟任何值就相當於返回一個undefined, * 如果函數中不寫return,則也會返回undefined * * return后可以跟任意類型的值 function f1() { function f2() { alert(“函數內部還可以再聲明函數”); } return f2(); } a =f1() a(); <=> f1()() <=> f2(); 6、立即執行函數 函數定義完后,立即執行,只能執行一次 調用函數方法是:函數(); ( function () { alert("我是一個匿名函數,因為沒有名字,所以只能被調用一次"); })(); //前面的括號是把匿名函數看成一個整體,后面的括號是為了調用該函數 7、對象屬性可以是函數 /* * 創建一個對象 */ var obj = new Object(); //向對象中添加屬性 obj.name = "孫悟空"; obj.age = 18; //對象的屬性值可以是任何的數據類型,也可以是個函數 obj.sayName = function(){ console.log(obj.name); }; function fun(){ console.log(obj.name); }; //console.log(obj.sayName); //調方法 obj.sayName(); //調函數 //fun(); * 函數也可以稱為對象的屬性, * 如果一個函數作為一個對象的屬性保存, * 那么我們稱這個函數時這個對象的方法 * 調用這個函數就說調用對象的方法(method) var obj2 = { name:"豬八戒", age:18, sayName:function(){ console.log(obj2.name); } }; obj2.sayName(); 8、列舉某一對象的屬性和方法 var obj = { name:"孫悟空", age:18, gender:"男", address:"花果山" }; //枚舉對象中的屬性 //使用for ... in 語句 /* * 語法: * for(var 變量 in 對象){ * * } * for...in語句 對象中有幾個屬性,循環體就會執行幾次 * 每次執行時,會將對象中的一個屬性的名字賦值給變量 */ for(var n in obj){ console.log("屬性名:"+n); console.log("屬性值:"+obj[n]); 中括號的方式可以把n作為變量替換其屬性值 console.log("屬性值:"+obj.n); 錯誤,obj中沒有n的屬性 } 9、全局作用域的問題 /* * 作用域 * - 作用域指一個變量的作用的范圍 * - 在JS中一共有兩種作用域: * 1.全局作用域 * - 直接編寫在script標簽中的JS代碼,都在全局作用域 * - 全局作用域在頁面打開時創建,在頁面關閉時銷毀 * - 在全局作用域中有一個全局對象window, * 它代表的是一個瀏覽器的窗口,它由瀏覽器創建我們可以直接使用 * - 在全局作用域中: * 創建的變量都會作為window對象的屬性保存 * 創建的函數都會作為window對象的方法保存 * 一切皆對象 * - 全局作用域中的變量都是全局變量, * 在頁面的任意的部分都可以訪問的到 * */ var a = 10; // 實際上是 window.a var b = 20; //var c = "hello"; //console.log(window.c); function fun(){ console.log("我是fun函數"); } //window.fun(); //window.alert("hello"); console.log(d); //會報錯,d沒有用var 提前聲明,只是賦值的時候,給window對象動態添加了一個屬性d d =10; 10、變量函數提前聲明 /* * 變量的聲明提前 * - 使用var關鍵字聲明的變量,會在所有的代碼執行之前被聲明(但是不會賦值), * 但是如果聲明變量時不使用var關鍵字,則變量不會被聲明提前 * * 函數的聲明提前 * - 使用函數聲明形式創建的函數 function 函數(){} * 它會在所有的代碼執行之前就被創建,所以我們可以在函數聲明前來調用函數 * 使用函數表達式創建的函數,不會被聲明提前,所以不能在聲明前調用 */ /*console.log("a = "+a); 情況1:使用 var a = 123; a是undefined,聲明變量在此句之后,但var a 會使變量提前聲明, 情況2: 使用 a = 123; a未找到,會報錯,因為a未提前聲明 a = 123; 等價於 window.a = 123; var a = 123;*/ 到了此句才賦值,等價於在所有代碼之前 聲明a var a; 執行到此句再賦值 a=123; //fun(); //函數聲明,會被提前創建 function fun(){ console.log("我是一個fun函數"); } //函數表達式,不會被提前創建,此句只是提前聲明了fun2,不知道是不是函數,可能是變量, undefined而在此次才開始賦值一個函數表達式 var fun2 = function(){ console.log("我是fun2函數"); }; fun2(); 11、函數作用域 /* * 函數作用域 * - 調用函數時創建函數作用域,函數執行完畢以后,函數作用域銷毀 * - 每調用一次函數就會創建一個新的函數作用域,他們之間是互相獨立的 * - 在函數作用域中可以訪問到全局作用域的變量 * 在全局作用域中無法訪問到函數作用域的變量 * - 當在函數作用域操作一個變量時,它會先在自身作用域中尋找,如果有就直接使用 * 如果沒有則向上一級作用域中尋找,直到找到全局作用域, * 如果全局作用域中依然沒有找到,則會報錯ReferenceError * - 在函數中要訪問全局變量可以使用window對象 */ //創建一個變量 var a = 10; function fun(){ var a = "我是fun函數中的變量a"; var b = 20; //console.log("a = "+a); function fun2(){ console.log("a = "+window.a); //訪問全局變量 } fun2(); } //fun(); //console.log("b = "+b); /* * 在函數作用域也有聲明提前的特性, * 使用var關鍵字聲明的變量,會在函數中所有的代碼執行之前被聲明 * 函數聲明也會在函數中所有的代碼執行之前執行 */ function fun3(){ fun4(); //console.log(a); //先找局部的a,后面找到了用var 提前聲明的a,就不用函數外的a了 // 因為后面用var 已經聲明了變量a,只是未賦值而已,因此是undefined var a = 35; // 此處的a是局部變,// 和函數外的a是兩個不同的變量 function fun4(){ alert("I'm fun4"); } } //fun3(); var c = 33; /* * 在函數中,不適用var聲明的變量都會成為全局變量 */ function fun5(){ //console.log("c = "+c); 先找局部的c,未找到,就找全局的c //c = 10; c是全局變量 和函數外的c是同一變量,等價於window.c= 10; 因為沒有使用var, var是創建一個新變量 //d沒有使用var關鍵字,則會設置為全局變量 ,等價於 window.d = 100 d = 100; } fun5(); //在全局輸出c //console.log("d = "+d); var e = 23; /* * 定義形參就相當於在函數作用域中聲明了變量 */ function fun6(e){// 聲明了形參,就相當於提前聲明了變量e, 即var e alert(e); //已經提前聲明了變量,但未賦值 e = 45; //此處賦值的是函數內的e,因為在形參處已經提前聲明了e } fun6(); 12、this /* * 解析器在調用函數每次都會向函數內部傳遞進一個隱含的參數, * 這個隱含的參數就是this,this指向的是一個對象, * 這個對象我們稱為函數執行的 上下文對象, * 根據函數的調用方式的不同,this會指向不同的對象 * 1.以函數的形式調用時,this永遠都是window * 2.以方法的形式調用時,this就是調用方法的那個對象 */ function fun(){ //console.log("a = "+a+", b = "+b); console.log(this.name); } //fun(); //創建一個對象 var obj = { name:"孫悟空", sayName:fun }; var obj2 = { name:"沙和尚", sayName:fun }; //console.log(obj.sayName == fun); var name = "全局的name屬性"; //obj.sayName(); //以函數形式調用,this是window //fun(); 等價於 window.fun(); //以方法的形式調用,this是調用方法的對象 //obj.sayName(); obj2.sayName(); 13、使用工廠方法創建大量對象 /* * 創建一個對象 */ var obj = { name:"孫悟空", age:18, gender:"男", sayName:function(){ alert(this.name); } }; /* * 使用工廠方法創建對象 * 通過該方法可以大批量的創建對象 */ function createPerson(name , age ,gender){ //創建一個新的對象 var obj = new Object(); //向對象中添加屬性 obj.name = name; // 不需要寫this.name 因為name已經非常明確,在形參中已經指定了 obj.age = age; obj.gender = gender; obj.sayName = function(){ alert(this.name); }; //將新的對象返回 return obj; //return this; 不能這樣傳this對象,因為調用的時候不是以構造函數的方式調用,而是以普通函數調用創建對象 // var obj2 = createPerson("豬八戒",28,"男"); } /* * 用來創建狗的對象 */ function createDog(name , age){ var obj = new Object(); obj.name = name; obj.age = age; obj.sayHello = function(){ alert("汪汪~~"); }; return obj; //return this; 不能這樣傳this對象,,,因為調用的時候不是以構造函數的方式調用(在構造函數中也不用顯示返回this),而是以普通函數調用創建對象 // var dog = createDog("旺財",3); } var obj2 = createPerson("豬八戒",28,"男"); var obj3 = createPerson("白骨精",16,"女"); var obj4 = createPerson("蜘蛛精",18,"女"); /* * 使用工廠方法創建的對象,使用的構造函數都是Object * 所以創建的對象都是Object這個類型, * 就導致我們無法區分出多種不同類型的對象 */ //創建一個狗的對象 var dog = createDog("旺財",3); console.log(dog); console.log(obj2); console.log(obj3); console.log(obj4); 14、使用構造函數創建對象 /* * 創建一個構造函數,專門用來創建Person對象的 * 構造函數就是一個普通的函數,創建方式和普通函數沒有區別, * 不同的是構造函數習慣上首字母大寫 * * 構造函數和普通函數的區別就是調用方式的不同 * 普通函數是直接調用,而構造函數需要使用new關鍵字來調用 * * 構造函數的執行流程: * 1.立刻創建一個新的對象 * 2.將新建的對象設置為函數中this,在構造函數中可以使用this來引用新建的對象 * 3.逐行執行函數中的代碼 * 4.將新建的對象作為返回值返回,隱式返回this * * 使用同一個構造函數創建的對象,我們稱為一類對象,也將一個構造函數稱為一個類。 * 我們將通過一個構造函數創建的對象,稱為是該類的實例 * * this的情況: * 1.當以函數的形式調用時,this是window * 2.當以方法的形式調用時,誰調用方法this就是誰 * 3.當以構造函數的形式調用時,this就是新創建的那個對象 * */ function Person(name , age , gender){ this.name = name; this.age = age; this.gender = gender; this.sayName = function(){ alert(this.name); }; } function Dog(){ } var per = new Person("孫悟空",18,"男"); var per2 = new Person("玉兔精",16,"女"); var per3 = new Person("奔波霸",38,"男"); var dog = new Dog(); /*console.log(per); console.log(dog);*/ /* * 使用instanceof可以檢查一個對象是否是一個類的實例 * 語法: * 對象 instanceof 構造函數 * 如果是,則返回true,否則返回false */ //console.log(per instanceof Person); //console.log(dog instanceof Person); /* * 所有的對象都是Object的后代, * 所以任何對象和Object左instanceof檢查時都會返回true */ //console.log(dog instanceof Object); 15、構造函數創建對象存在的問題 /* * 創建一個Person構造函數 * - 在Person構造函數中,為每一個對象都添加了一個sayName方法, * 目前我們的方法是在構造函數內部創建的, * 也就是構造函數每執行一次就會創建一個新的sayName方法 * 也是所有實例的sayName都是唯一的。 * 這樣就導致了構造函數執行一次就會創建一個新的方法, * 執行10000次就會創建10000個新的方法,而10000個方法都是一摸一樣的 * 這是完全沒有必要,完全可以使所有的對象共享同一個方法 */ function Person(name , age , gender){ this.name = name; this.age = age; this.gender = gender; //向對象中添加一個方法 //this.sayName = fun; } //將sayName方法在全局作用域中定義 /* * 將函數定義在全局作用域,污染了全局作用域的命名空間 * 而且定義在全局作用域中也很不安全 * 得使用原型對象 */ /*function fun(){ alert("Hello大家好,我是:"+this.name); };*/ //向原型中添加sayName方法 Person.prototype.sayName = function(){ alert("Hello大家好,我是:"+this.name); }; //創建一個Person的實例 var per = new Person("孫悟空",18,"男"); var per2 = new Person("豬八戒",28,"男"); per.sayName(); per2.sayName(); //console.log(per.sayName == per2.sayName); 16、函數的原型對象 /* * 原型 prototype * * 我們所創建的每一個函數,解析器都會向函數中添加一個屬性prototype,無論是構造函數還是普通函數 * 這個屬性對應着一個對象,這個對象就是我們所謂的原型對象 * * 1、如果函數作為普通函數調用prototype沒有任何作用 * * 2、 當函數以構造函數的形式調用時,它所創建的對象中都會有一個隱含的屬性, * 指向該構造函數的原型對象,我們可以通過__proto__來訪問該屬性 * * 原型對象就相當於一個公共的區域,所有同一個類的實例都可以訪問到這個原型對象, * 我們可以將對象中共有的內容,統一設置到原型對象中。 * * 當我們訪問對象的一個屬性或方法時,它會先在對象自身中尋找,如果有則直接使用, * 如果沒有則會去原型對象中尋找,如果找到則直接使用 * * 以后我們創建構造函數時,可以將這些對象共有的屬性和方法,統一添加到構造函數的原型對象中, * 這樣不用分別為每一個對象添加,也不會影響到全局作用域,就可以使每個對象都具有這些屬性和方法了 */ function MyClass(){ } //向MyClass的原型中添加屬性a MyClass.prototype.a = 123; //向MyClass的原型中添加一個方法 MyClass.prototype.sayHello = function(){ alert("hello"); }; var mc = new MyClass(); var mc2 = new MyClass(); //console.log(MyClass.prototype); //console.log(mc2.__proto__ == MyClass.prototype); //向mc中添加a屬性 mc.a = "我是mc中的a"; //console.log(mc2.a); 此處訪問的是原型對象中的a,因為mc2中沒有a的屬性 mc.sayHello(); 15、原型鏈 /* * 創建一個構造函數 */ function MyClass(){ } //向MyClass的原型中添加一個name屬性 MyClass.prototype.name = "我是原型中的名字"; var mc = new MyClass(); mc.age = 18; //console.log(mc.name); //使用in檢查對象中是否含有某個屬性時,如果對象中沒有但是原型中有,也會返回true //console.log("name" in mc); //可以使用對象的hasOwnProperty()來檢查對象自身中是否含有該屬性 //使用該方法只有當對象自身中含有屬性時,才會返回true //console.log(mc.hasOwnProperty("age")); //true //console.log(mc.hasOwnProperty("name")); //false //console.log(mc.hasOwnProperty("hasOwnProperty")); //false /* * 原型對象也是對象,所以它也有原型, * 當我們使用一個對象的屬性或方法時,會現在自身中尋找, * 自身中如果有,則直接使用, * 如果沒有則去原型對象中尋找,如果原型對象中有,則使用, * 如果沒有則去原型對象的原型中尋找,直到找到Object對象的原型, * Object對象的原型沒有原型,如果在Object原型中依然沒有找到,則返回undefined */ //console.log(mc.__proto__.hasOwnProperty("hasOwnProperty")); //false //console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); //true hasOwnProperty在函數原型對象中原型對象中 //console.log(mc.hello); //undefined //console.log(mc.__proto__) //函數的原型對象 //console.log(mc.__proto__.__proto__) //系統object的對象 //console.log(mc.__proto__.__proto__.__proto__) //null 16、垃圾回收 /* * 垃圾回收(GC) * - 就像人生活的時間長了會產生垃圾一樣,程序運行過程中也會產生垃圾 * 這些垃圾積攢過多以后,會導致程序運行的速度過慢, * 所以我們需要一個垃圾回收的機制,來處理程序運行過程中產生垃圾 * - 當一個對象沒有任何的變量或屬性對它進行引用,此時我們將永遠無法操作該對象, * 此時這種對象就是一個垃圾,這種對象過多會占用大量的內存空間,導致程序運行變慢, * 所以這種垃圾必須進行清理。 * - 在JS中擁有自動的垃圾回收機制,會自動將這些垃圾對象從內存中銷毀, * 我們不需要也不能進行垃圾回收的操作 * - 我們需要做的只是要將不再使用的對象設置null即可 * */ var obj = new Object(); //對對象進行各種操作。。。。 obj = null; </script> </head> <body> </body> </html>