如何理解并学习javascript中的面向对象(OOP)【转载】

阅读() @2018-10-29 18:16:08

本文不适合javascript初学者看(javascript水平还停留在函数级别的朋友,看了会觉得很晕的)。如果你想让你的javascript代码变得更加优美,性能更加卓越。或者,你想像jQuery的作者一样,写出属于自己优秀的类库(哪怕是基于jquery的插件)。那么,你请务必要学习javascript面向对象,否则你无法更灵活的使用javascript这门语言。

什么是闭包?到底什么是原型?(知道闭包和原型的,就算得上是javascript的高手了。但真正能够理解,并且灵活运用的人并不多)到底该如何学习javascript中的面向对象呢?在javascript这么语言正如日中天,相信不少人正在为此而困惑。

本文中,我讲用代码+详细注释的方式,一行行一步步讲述javascript中的面向对象编程。当然有些只是我个人的理解,不足之处,敬请谅解!

1.下面部分的代码,将是从目前十分流行的JSON数据格式以及javascript数组,来一步步向大家阐述javascript中的面向对象思想。

    <!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>JSON数据格式</title>  
        <script src="Scripts/jquery-1.4.1-vsdoc.js" type="text/javascript"></script>  
         <script type="text/javascript">  
             function jsonTest() {  
                 //定义json数据格式 -- 以文本的形式存在,转换为javascript对象或者数组  
             //对象中可以包含数组,数组中也可以包含对象,可以存在相互嵌套的关系  
                 var json1 = "[1,2,{a:123,b:'str',c:[100,200]}]";//数组形式  
                 var parsejson1 = eval(json1);//将纯文本的内容转换为javascript原生支持的json  
                 var json2 = "{name:'dinglang',age:21,hobby:['武术','电影']}";//对象形式  
                 //var parsejson2 = eval(json2); //这样直接转换会报错   
                 //当被eval()转换的纯文本json数据为对象形式时,需要在前后加上"()"  
                 var parsejson2 = eval("(" + json2 + ")"); //这样转换就可以了  
                  
                 alert("");  
             }  
      
      
             //探索一下JSON这种数据格式的由来  
      
             //1.首先是回顾一下javascript数组的相关知识  
             function arrTest() {  
                // 1)数组的基本定义与赋值  
                 var arrOne = new Array(); //第一种创建方法  
                 var arrTwo =  new Array(0,1,2);//第二种创建方式(创建的时候就给数组赋初始值)  
                 var arrThree = []; //第三种方式 --定义一个空数组  
                 var arrFour = [1, 2, 3, 5]; //第四种方式--定义一个数组,并给数组赋初始值  
                 //创建多维数组  
                 var arrFive = new Array(1, new Array(2, 3), 4, 5); //创建一个多维数组(嵌套的)  
                 var arrSix = [1, [2, 3], 4];//创建一个多维数组  
                 // 2)数组的基本操作(数组是javascript语言中一种很重要的数据结构)  
                 alert(arrSix[1]);  //通过数组下标,来获取数组中对应的某个元素  
                 arrSix[0] = 100; //给数组中下标对应的元素赋值(如果该元素还未创建,就创建该元素并赋值)  
                 arrSix[99] = 888; //arrSix中会自动创建下标为99的元素,并给其赋值 --javascript中数组的长度是可以随时改变的  
                 // 3)javascript当中数组常用的一些方法  
                 //concat方法的使用 --可以做数组的连接或者合并,原数组的内容不变,将返回一个新的数组对象  
                 var arrFourarrFour1 = arrFour.concat(101, 102, 103);//第一种连接方式  
                 var arrFourarrFour2 = arrFour.concat([104, 105]);//第二种连接方式  
                 var arrFourarrFour3 = arrFour.concat(arrFour1); //将已经定义的数组进行连接  
                 //join方法--将数组中元素,按照指定的分隔符连接成字符串输出,原数组的内容不变  
                 //slice方法--返回当前数组中的子数组,原数组中的内容不会改变  
                 //push/pop  在数组的尾端追加(push)或弹出(pop),将会修改原数组的内容  
                 arrFive.push(107);//在数组尾部追加一个元素  
                 arrFive.pop(); //弹出数组中最后一个元素  
                 //在数组的开头追加(shift)和unshift(弹出)操作  
                 arrFive.reverse(); //反转数组中的元素  
                 arrFive.sort(); //按照字母是顺序,对数组中的元素进行升序排列  
                 arrFive.sort(function (a, b) {  
                     return a - b;  
                 }); //按照数值大小,进行升序排列。如果返回的是负值,那么a就会出现在b的前面  
                 arrFive.sort(function (a, b) {  
                     return b - a;  
                 }); //按照降序排列  
                 //splice  可以删除数组中一部分元素,并把部分元素进行返回。也可以在指定位置添加元素  
                 var arrSplice1 = arrSix.splice(3, 2); //从下标为3的元素开始删除,删除2个元素  
                 var arrSplice2 = arrSix.splice(4); //从下标为4的元素开始删除,一直删除到数组的末尾  
                 arrSix.splice(1, 0, 401, 402); //在下标为1的元素前面,插入401,402这两个元素  
                 arrSix.splice(1, 0[188, 189]);//在下标为1的元素前面,插入[188,199]  
             }  
               
             //2.javascript中的对象的定义、使用  
             var obj1 = new Object(); //定义一个对象  
             var obj2 = {};  //使用"{}"也可以定义一个对象  
             //给对象增加属性  
             obj1.num = 1;  
             obj1.str = "string";  
             obj1.sayHello = function () {  
                 alert("Hello");  
             }  
             obj2.srcObj = obj1; //将obj1对象作为obj2对象的属性  
      
             //属性的访问 --第一种访问方式  
             obj1.num; //也可以这么访问  obj2.srcObj.num;  
             obj1.str;           //obj2.srcObj.str;  
             obj1.sayHello();   //obj2.srcObj.sayHello();  
      
             //属性的访问 --第二种方式  
             obj1["num"];        //obj2["srcObj"]["num"];  
             obj1["str"];        //obj2["srcObj"]["str"];  
             obj1["sayHello"](); //obj2["srcObj"]["sayHello"]();  
      
             //通过对象直接量的方式,定义和调用对象、属性  
             var obj3 = {  
                 num: 1,  
                 str: "string",  
                 sayHello: function () {  
                     alert('Hello');  
                 }  
             }  
             //访问方式同上,例如  
             obj3.num; //obj3["num"];  
      
             //看清楚了吗?这就是javascript中JSON数据格式的原型  
      
      
      
             //下面来深入讲解javascript语言的面向对象特性  
             //javascript中定义类,需要用function来模拟  
      
    //         function Teacher(){  
    //           
             //         }   
             //建议使用下面这种方法来创建一个类,以便将类和函数区分开来(建议定义类时首字母大写)  
             var Teacher = function () {  
      
             }  
             //定义一个book类,这里的function还承担了构造函数的工作  
             //在使用new操作符创建Book对象时,这个funtion里面的代码将会被执行一次  
             //this关键字代表的是当前对象  
             function Book(name) {  
             //定义公有的属性  
                 this.name = name;  
                 //定义公有的函数  
                 this.getName = function () {  
                     return this.name;  
                 }  
                 this.setName = function (nname) {  
                     this.name = nname;  
                 }  
             }  
             function ooTest() {  
                 var tech = new Teacher();  
                 alert(tech instanceof Teacher); // instanceof函数,表示是否属于某对象类型  
                 var book1 = new Book("C#");//这里的new操作相当于先创建了一个简单对象,调用了类的构造函数  
                 var book2 = new Book("JAVA");  
                 alert(book1.name);//弹出C#  
                 alert(book2.name);//弹出JAVA  
                 book1.setName(".NET");  
                 alert(book1.name);//弹出.NET  
                 alert(book2.name); //弹出JAVA  
      
                 //function上面都有一个原型对象 --prototype  
                 var proto = Book.prototype;  
                 proto.str = "string";  
                 proto.hello = function () {  
                     alert("Hello");  
                 }  
                 //给原型定义了属性和方法后,拥有这个原型对象的function模拟出来的类,也具有该属性和方法  
                 alert(book1.str); //弹出string  
                 book1.hello(); //弹出hello  
      
             }  
               
         </script>  
    </head>  
    <body>  
    <input  type="button" value="测试json" onclick="jsonTest()"/>  
    </body>  
    </html>  

2.下面部分代码,是从另外一个角度讲解javascript中的面向对象编程。是借鉴EasyJF开源团队的讲解,我个人做了一些补充和说明。

    <!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面向对象编程</title>  
        <script src="Scripts/jquery-1.4.1-vsdoc.js" type="text/javascript"></script>  
        <script type="text/javascript">  
            $(function () {  
      
                //            function animal(name) {  
                //                this.name = name;  
                //                this.age = 0;  
                //            }  
                //            var a1 = animal;  
                //            alert(a1);//弹出整个函数体  
                //            var a2 = animal("dinglang");  
                //            alert(a2); //弹出undefined  
                //            var a3 = new animal();  
                //            alert(a3);//弹出object  
                //            var a4 = new animal;  
                //            alert(a4);//弹出object  
      
      
                //求值  
                //alert(sum(1, 3)); //要求弹出结果为4  
                // alert(sum(1, 3, 5, 4, 7)); //要求弹出结果为20  
                //根据java或者C#的编程经验,首先想到的是函数重载。  
                //            function sum(a, b) {  
                //                return a + b;  
                //            }  
                //            function sum(a, b, c, d, e) {  
                //                return a + b + c + d + e;  
                //            }  
                //不幸的是,javascript并不支持函数重载。如果照上面那么写,只有下面的那个函数才会生效  
      
                //javascript支持可变参数的函数  
                function sum() {  
      
                    var n = 0;  
                    for (var i = 0; i < arguments.length; i++) {  
                        n += arguments[i];  
                    }  
                    return n;  
                }  
      
                //javascript高级知识 -- 闭包  
                //函数里面嵌套函数,且在外部被引用了,所以这个对象i不会被垃圾回收机制清除,所以i递增  
                function f1() {  
                    var i = 0;  
                    var f2 = function () {  
                        i++;  
                        alert(i);  
                    }  
                    return f2; //f2对象指的是整个函数体  
                }  
                var f3 = f1(); // "f1()"就是指该函数的执行结果或者返回值 --f2  
                //           f3();//1  
                //            f3();//2  
                //            f3();//3  
      
      
                //作用域与this关键字  
                //            var obj = new Object();  
                //            obj.v = "v is a object";  
                //            //相当于这么写  
                //           var obj2 = { v: "v is a object" };  
                //作用域Scope  
                var b1 = { v: "this is b1" };  
                var b2 = { v: "this is b2" };  
                function b(x, y) {  
                    //  alert(this.v + "," + x + "," + y);  
                }  
                b("ding", "lang"); //undefined,"ding","lang"   
                //调用b函数时,b函数中的this关键字指的是window对象.而Window对象中没有v对象,所以undefined  
                //window.b("ding", "lang"); //undefined,"ding","lang"   --与 b("ding", "lang");意义相同  
                //b.call();//就等于b();  
                //call函数的第一个参数表示的是函数的上下文 --表示b函数中的this 所以this关键字=b1  
                // b.call(b1, "ding", "lang"); //this is b1,ding,lang  
                //注意apply函数的用法:第一个参数表示的也是函数中的上下文。不过后面的参数要以数组的形式传递  
                // b.apply(b2, ["ding", "lang"]); // //this is b1,ding,lang  
      
                //关于作用域,再补充一点  
                var b3 = { v: "this is b3",  
                    sayHello: function () {  
                        alert(this.v);  
                    }  
                }  
                // b3.sayHello(); //this is b3  
                //b3.sayHello.call(b1); //会调用b1对象中的sayHello函数  -- this is b1  
      
      
                // for ... in  
                //            var arr = new Array(); //new 一个js数组,与c#、java等编程语言不同,可以不指定长度  
                //            arr[0] = 1; //赋值  
                //            arr[1] = 2;  
                //            arr[2] = 3;  
                //javascript支持直接定义赋值  
                var arr = new Array(1, 2, 3);  
                for (var i = 0; i < arr.length; i++) {  
                    // alert(arr[i]); //弹出 1,2 ,3   
                }  
                //注意:javascript中的for...in ,看起来与C#或者java中的foreach类似,但是不同  
                for (var key in arr) {  
                    // alert(key);// 弹出0,1,2   key指的是键,而不是值。在C#的foreach中,“in”前的变量指的是值  
                    //alert(arr[key]);//可以使用这种方式取值 --key指的是键,也就是某个对象中包含的所有的对象,而不是值  
                }  
                //假如我没有firebug,想使用IE实现类似与firebug控制台中console.dir的功能,可以这样  
                for (var key in window) {  
                    // 这样就能将window对象中,包含的全部对象迭代显示出来。也就实现了firebug中console.dir的功能  
                    //document.getElementById("key").innerHTML += (key + ":" + window[key] + "</br>");  
                }  
      
                //对象的删除(释放内存-- 在extjs组件中常用)  
                //            delete b3.v; //删除b3对象中的v成员  
                //            alert(b3.v); // undefined --因为v这个成员已经被删除了  
      
                //类的修改,扩展(重点,难点)  
                //1.假如我要实现一个简单的加法计算  
                //       var numOne = 5;//如果直接这么定义,那么下面的numOne.add(8);执行会报错  
                //如果我这么写,下面调用就不会报错了(因为此时的numOne,是个类.相当于java或C#语言中原始的基本数据类型、包装类型)  
                var numOne = new Number(5);  
                numOne.add = function (numTwo) {  
                    return this + numTwo;  
                }  
                //alert(numOne.add); //undefined  
                // alert(numOne.add(8));//这样写看起来没错,但是会报错--numOne.add is not a function  
                var numThree = new Number(100);  
                //如果我现在想要给numThree对象中也加上add这么一个函数  
                //直接使用prototype这个特殊的属性来实现,给所有的Number类型实例都加入add函数  
                Number.prototype.add = function (numTwo) {  
                    return this + numTwo;  
                }  
      
                alert(numThree.add(200).add(300)); //弹出600   100+200+300=600    
                //说明所有的Number类型确实都具有了add这么一个函数   超级延时绑定--类的扩展  
      
                //小例子 --扩展String类,给所有的String类加上sayILoveYou  
                //            String.prototype.sayILoveYou = function () {  
                //                alert(this.toString() + ", I Love You");  
                //            }  
                //            var strOne = "dinglang";  
                //            strOne.sayILoveYou();  
      
                //javascript中的类的用法  
                //使用构造函数的方式,定义简单的Person类(javascript函数也是一个类)  
                function Person(name) {  
      
                    this.name = name;  
                    this.age = 20;  
                    var year = 2010; //定义一个私有的成员sex  
                    //                this.sayHello = function () {  
                    //                    alert(this.name + ":今年" + this.age + "岁,HelloWold");  
                    //可以直接在这里面定义sayHello成员函数(特权方法),但是每次实例化该类的时候都会重新去定义,所有还是选择用prototype属性的方式  
                    //如果在sayHello函数中要使用year这个私有对象,就可以直接在此定义这个特权方法。这就是典型的“闭包”。  
                }  
                //如果使用了new关键字,说明Person就是一个类。否则的话,只是一个普通的函数  
                var p1 = new Person("丁浪"); // new 一个Person类的实例   
                var p2 = new Person("蔡世友"); //注意:别按照java或者C#的习惯,写成了Person p =new Person("XXX");   
                //给Person这个自定义的类,添加一个sayHello函数  
                Person.prototype.sayHello = function () {  
                    alert(this.name + ":今年" + this.age + "岁,HelloWold");  
                }  
                p1.sayHello();  
                p2.sayHello();  
      
                //实现javascript中的继承  
      
                function classA(name) {  
                    this.name = name;  
                    this.showName = function () {  
                        alert(this.name);  
                    }  
                }  
                function classB(name) {  
                    //1)使用newMethod的方式实现继承  
                    //                this.newMethod = classA;  
                    //                this.newMethod(name);  
                    //                delete this.newMethod; //释放对象  
                    //2)调用claasA这个函数,并把他的上下文(作用域)指向this(也就是classB类的实例)  
                    //这样也能实现继承效果(使用call或者apply)  
                    classA.call(this, name);  
                    //classA.apply(this,[name]);  
                }  
                objA = new classA("操作系统");  
                objB = new classB("组成原理");  
                objA.showName(); //弹出“操作系统”  
                objB.showName(); //弹出“组成原理”  
      
            })  
        </script>  
    </head>  
    <body>  
    <div id="key"> </div>  
    </body>  
</html>  

本文转自博客园文章!

微信二维码