JS函數對象


<!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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM