JS函數與面向對象


本文摘自北風網CJ講的javascript+jquery+ajax教程。摘以自娛。

函數是一組可以隨時隨地運行的語句。

創建函數

function fnOne() {//具有名稱的函數,函數名必須符合變量名命名規范
        //可以沒有符何語句
    }
    var fnTwo = function () {//匿名函數
    };
    function () {//創建匿名函數而不立即創建其引用,那么之后就沒辦法調用此函數
    }
    (function fnThree() {
    })();//創建函數並立即執行一次
    (function () {})();//創建匿名函數並立即執行一次

匿名函數與命名函數的區別

   fnOne();//不會出錯,使用function創建的具有名稱的函數在任何與之相同作用域的地方都能調用
    fnTwo();//出錯
    var fnTwo =function () {};//因為只有執行了這個賦值語句后,fnTwo才會被創建
    function fnOne() {}

函數返回值

function fnTest() {
        return "value";//使用return來返回值
        alert("Hello!!!");//執行了return之后,函數就會退出 
    }

函數參數

function fnTest(arg1,arg2,arg3) {
        alert(arg1+"\n"+arg2+"\n"+arg3);
    }
fnTest(1,2,3);
fnTest(1,2);//沒有傳值過去時,就會是undefined

 arguments對象:在函數執行時函數內部就會有arguments對象,它包含了所有的參數,arguments的length屬性報告了傳入參數個數

function fnTest() {
        for (var i=0;i< arguments.length;i++) {
            alert(arguments[i]);
        }
    }
    fnTest(1,2,3);
    fnTest(45);

使用arguments對象模擬函數重載

function fnTest() {
        var args = arguments;
        switch (arguments.length) {
            case 0 :
                return "沒有輸入!!!";
            case 1 :
                return "一個參數:"+args[0];
            case 2 :
                return "二個參數:"+args[0]+" 和 "+ args[1];
        }
    }
    alert(fnTest());
    alert(fnTest(1));
    alert(fnTest(2));

arguments對象補充:arguments對象的callee屬性指向它所在的函數

function fnTest() {alert(arguments.callee);}

閉包

"閉包,指的是詞法表示包括不被計算的變量的函數,也就是說,函數可以使用函數之外定義的變量。" 請理解透並牢牢記住這句話,否則你就可能永遠不知道什么是閉包,理解好,其實閉包還是蠻簡單的。-----摘者注。

在 ECMAScript 中使用全局變量是一個簡單的閉包實例。請思考下面這段代碼:

var msg = "我是全局變量!!!";
    function say() {
      alert(msg);
    }
    say();

在ECMAScript中,在函數聲明處向函數外部看到的聲明的所有變量,在函數內部都能訪問到它們的最終值!

var g = "全局變量!!!";
    function fnA() {
        var a="A";
        function fnB() {
            var b="B";
            alert(a);//可以訪問到a
            alert(c);//但不以訪問c
            function fnC() {
                var c = "C";
                alert(a+"\n"+b);//只要遵循從里向外看的原則,看到的變量都可以訪問到
            }
        }
    }
    //更復雜的閉包
    function fnTest(num1,num2) {
        var num3 = num1+num2;
        return function () {
            alert("num1+num2結果為"+num3);
        };
    }
    var result = fnTest(23,56);
    result();

閉包函數只能訪問變量的最終值!!!

function fnTest(arr) {
        for (var i=0;i < arr.length;i++) {
            arr[i]=function () {
                alert(i+" | "+arr[i]);
            };
        }
    }
    var arr = [0,1,2,3,4,5];
    fnTest(arr);
    for (var i=0;i < arr.length;i++) {
        arr[i]();//始終輸出6還有一個undefined
        //因為函數退出后,i值為6,所以訪問到的值只有6
    }
//我開始一看感覺應該不怎么復雜,不過只細看過后,再運行過后,感覺還真不簡單呢!很意思的一段代碼。

不但在閉包中可以訪問閉包外的變量值,而且還可以設置它的值

function fnTest() {
        var a=123;
        return {
            set:function (param) {a = param},
            get:function () {return a}
        };
    }
    var obj = fnTest();
    alert(obj.get());//123
    obj.set(4);
    alert(obj.get());//4

對象,構造函數

創建一個對象

var obj = new Object();
    alert(obj);
    alert(Object);//一個函數
    Object();//可以直接執行
    //構造函數也是一個普通的函數
    function Demo() {
    }
    var d = new Demo();//不會出錯,使用new運算符來創建對象實例
    alert(d);//object

this關鍵字的用法

function Demo() {
        this.property = "屬性!!!";
    }
    d = new Demo();
    alert(d.property);//屬性!!!

不使用new而直接執行構造函數時,this指向window

function Demo() {
        this.property = "屬性!!!";
    }
    var d = Demo();
alert(d.property);
//undefined alert(window.property);//屬性!!!

可以給構造函數傳遞參數,然后可以將參數賦值給對象的屬性

function Person(name,age) {
        this.name = name;
        this.age = age;
    }
    var p1 = new Person("CJ",18);

instanceof 運算符,用來判斷對象是否是某個類(雖然ECMAScript中並不存在類,但我們在這里依然使用這一術語)的實例

var str = new String("string");
    alert(str instanceof String);//true
    var arr = new Array();
    alert(arr instanceof Array);//true
    function Demo() {}
    var d = new Demo();
    alert(d instanceof Demo);//true

面向對象術語

一種面向對象語言需要向開發者提供四種基本能力:

  • 封裝——把相關的信息(無論數據或方法)存儲在對象中的能力。
  • 聚集——把一個對象存儲在另一個對象內的能力。
  • 繼承——由另一個類(或多個類)得來類的屬性和方法的能力。
  • 多態——編寫能以多種方法運行的函數或方法的能力。

ECMAScript支持這些要求,因此可被看作面向對象的。

封裝與私有屬性:封裝並不要求私有!

function Person(name,age) {
        this.name = name;//將值存儲為對象的屬性即是封裝
        this.age = age;
    }
    var p1 = new Person("CJ",18);

ECMAScript目前版本並不支持私有屬性,但可以通過閉包來模擬

function Person(name,age) {
        this.getName = function () {return name};
        this.setName = function (param) {name=param};
        this.getAge = function () {return age};
        this.setAge = function (param) {age=param};
    }
    var p1 = new Person("CJ",18);
    alert(p1.name);//undefined
    alert(p1.getName());//CJ
    p1.setName("XXX");
    alert(p1.getName());//XXX

繼承:prototype屬性

ECMAScript中,繼承是通過構造函數的prototype屬性實現的

function Person(name,age) {
        this.name=name;
        this.age = name;
    }
    alert(Person.prototype);//object
    Person.prototype.sort = "人";
    var p1 = new Person("CJ",18);
    alert(p1.sort);//所有的Person對象的實例繼承了sort這個屬性

所有對象都有一個方法isPrototypeOf(),用來判斷它是不是另一個對象的原型

function Person() {}
    var p1 = new Person();
    alert(Person.prototype.isPrototypeOf(p1));//true    P1的原型是不是Person

在ECMAScript中讓一個類繼承另一個類的方式比較特殊 

function ClassA() {
        this.a = "A";
    }
    function ClassB() {
        this.b = "B";
    }
    ClassB.prototype = new ClassA(); //讓ClassB繼承ClassA
    var b = new ClassB();
    alert(b.a);//"A",繼承了屬性a
    alert(b instanceof ClassB);//true
    alert(b instanceof ClassA);//true,因為繼承,b也是ClassA的后代
    alert(ClassB.prototype.isPrototypeOf(b));//true
    alert(ClassA.prototype.isPrototypeOf(b));//true,ClassA.prototype也是b的原型

然而這樣的繼承有個注意點——

function ClassA() {
        this.a = "A";
    }
    function ClassB() {
        this.b = "B";
    }
    var b = new ClassB();//先實例化ClassB
    ClassB.prototype = new ClassA();//再去繼承ClassA,將prototype重置為另一個對象
    alert(b instanceof ClassB);//false
    alert(b instanceof ClassA);//false
    alert(ClassB.prototype.isPrototypeOf(b));//false
    alert(ClassA.prototype.isprototypeOf(b));//false

當構造函數需要參數時

function Person(name,age) {
        this.name = name;
        this.age = age;
    }
    function Teacher(name,age,lesson) {
        this.tempMethod = Person;//對象冒充
        this.tempMethod(name,age);
        //當執行Person時,由於是在Teacher某個實例上調用的,所以在Person函數中的this指向了Teacher的實例
        delete this.tempMethod;//刪除臨時方法
        this.lesson = lesson;
    }
    ClassB.prototype =new ClassA();//始終不應在繼承時放參數
    var t1 = new Teacher("HUXP",18,"C#");
    alert(t1.name+" | "+ this.age+ " | "+this.lesson);

事實上,對於對象冒充,ECMAScript提供了更簡潔的內置方法call,在每個函數上調用,第一個參數即為要冒充的對象,剩下的是函數需要的其它參數

 

function Demo(arg) {
        this.name = arg;
    }
    var obj = new Object();
    Demo.call(obj,"name");
    alert(obj.name);//"name"
    //使用call重寫上面繼承的例子
    function Person(name,age) {
        this.name = name;
        this.age = age;
    }
    function Teacher(name,age,lesson) {
        Person.call(this,name,age);//對象冒充
        this.lesson = lesson;
    }

靜態屬性與Function類

在ECMAScript里有個有趣的地方是,函數本身也是對象(和數組也一樣),也可使用new來創建.Function構造函數至少要傳兩個字符串參數,可以是空字符串。除了最后一個字符串會被當做函數體語句去執行,其它參數都會作為函數參數變量名! 

var fn = new Function();//一個空函數
    //創建有內容的函數
    fn = new Function("arg1","alert(arg1)");//最后一個參數為執行語句的字符串,前面參數全是函數要用到的參數
    //上面的代碼等效於
    fn = function (arg1) {alert(arg1)};//同樣,由於都是賦值語句,所以要注意出現次序
    fn.property ="既然是對象,那么就要以添加屬性"

事實上,在構造函數上添加的屬性就被稱之為靜態屬性,它不會被實例所繼承 

function ClassDemo() {
    }
    ClassDemo.property = new Array();
    var d =new ClassDemo();
    alert(d.property);//undefined
    alert(ClassDemo.property);//object

Function類的實例還有其它一些方法和屬性(當然,使用function關鍵字聲明的函數也是一樣的) 

function outer() {
        inner();
        function inner() {
            alert(inner.caller);
            //函數的caller屬性指向調用自身的上下文函數,這里指向outer
        }
    }
    function applyTest() {
        var args = arguments;
        this.name = args[0];
        this.age=args[1];
    }
    var obj = new Object();
    applyTest.apply(obj,["name",18]);
    alert(obj.name);
    alert(obj.age);
    //apply方法與call方法類似,也用於對象冒充,只是apply方法只有兩個參數
    //第一個是要冒充的對象,第二個是所有參數組成的數組

 感謝你的閱讀。


免責聲明!

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



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