亚洲免费乱码视频,日韩 欧美 国产 动漫 一区,97在线观看免费视频播国产,中文字幕亚洲图片

      1. <legend id="ppnor"></legend>

      2. 
        
        <sup id="ppnor"><input id="ppnor"></input></sup>
        <s id="ppnor"></s>

        JS面向對象編程詳解

        字號:


            序言
            在JavaScript的大世界里討論面向對象,都要提到兩點:1.JavaScript是一門基于原型的面向對象語言 2.模擬類語言的面向對象方式。對于為什么要模擬類語言的面向對象,我個人認為:某些情況下,原型模式能夠提供一定的便利,但在復雜的應用中,基于原型的面向對象系統(tǒng)在抽象性與繼承性方面差強人意。由于JavaScript是唯一一個被各大瀏覽器支持的腳本語言,所以各路高手不得不使用各種方法來提高語言的便利性,優(yōu)化的結果就是其編寫的代碼越來越像類語言中的面向對象方式,從而也掩蓋了JavaScript原型系統(tǒng)的本質。
            基于原型的面向對象語言
            原型模式如類模式一樣,都是是一種編程泛型,即編程的方法論。另外最近大紅大紫的函數(shù)編程也是一種編程泛型。JavaScript之父Brendan Eich在設計JavaScript時,從一開始就沒打算為其加入類的概念,而是借鑒了另外兩門基于原型的的語言:Self和Smalltalk。
            既然同為面向對象語言,那就得有創(chuàng)建對象的方法。在類語言中,對象基于模板來創(chuàng)建,首先定義一個類作為對現(xiàn)實世界的抽象,然后由類來實例化對象;而在原型語言中,對象以克隆另一個對象的方式創(chuàng)建,被克隆的母體稱為原型對象。
            克隆的關鍵在于語言本身是否為我們提供了原生的克隆方法。在ECMAScript5中,Object.create可以用來克隆對象。
            var person = {
              name: "tree",
              age: 25,
              say: function(){
                console.log("I'm tree.")
              }
            };
            var cloneTree = Object.create(person);
            console.log(cloneTree);
            原型模式的目的并不在于得到一個一模一樣的對象,而提供了一種便捷的方式去創(chuàng)建對象(出自《JavaScript設計模式與開發(fā)實踐》)。但是由于語言設計的問題,JavaScript的原型存在著諸多矛盾,它的某些復雜的語法看起來就那些基于類的語言,這些語法問題掩蓋了它的原型機制(出自《JavaScript語言精粹》)。如:
            function Person(name, age){
              this.name = name;
              this.age = age;      
            }
            var p = new Person('tree', 25)
            實際上,當一個函數(shù)對象唄創(chuàng)建時,F(xiàn)unction構造器產(chǎn)生的函數(shù)對象會運行類似這樣的一些代碼:
            this.prototype = {constructor: this}
            新的函數(shù)對象被賦予一個prototype屬性,它的值是一個包含constructor屬性且屬性值為該新函數(shù)的對象。當對一個函數(shù)使用new運算符時,函數(shù)的prototype的屬性的值被作為原型對象來克隆出新對象。如果new運算符是一個方法,它的執(zhí)行過程如下:
            Function.prorotype.new = function() {
              //以prototype屬性值作為原型對象來克隆出一個新對象
              var that = Object.create(this.prorotype);
               
              //改變函數(shù)中this關鍵指向這個新克隆的對象
              var other = this.apply(that, arguments);
               
              //如果返回值不是一個對象,則返回這個新克隆對象
              return (other && typeof other === 'object') ? other : that;
            }
            從上面可以看出,雖然使用new運算符調用函數(shù)看起來像是使用模板實例化的方式來創(chuàng)建對象,但本質還是以原型對象來克隆出新對象。
            由于新克隆的對象能否訪問到原型對象的一切方法和屬性,加上new運算符的特性,這便成了利用原型模擬類式語言的基石。 
            利用原型模擬類式語言
            抽象
            用原型模式來模擬類,首先是抽象方式。根據(jù)JavaScript語言的特點,通常一個類(實際上是偽類)通常是將字段放置于構造函數(shù)(實際上是new 運算符調用的函數(shù),JavaScript本身并沒有構造函數(shù)的概念)中,而將方法放置于函數(shù)的prototype屬性里。
            function Person(name, age) {
              this.name = name;
              this.age = age;
            };
            Person.prototype.say = function(){
              console.log("Hello, I'm " + this.name);
            };
            繼承
            繼承是OO語言中的一個最為人津津樂道的概念。許多OO語言都支持兩種繼承方式:接口繼承和實現(xiàn)繼承。接口繼承之繼承方法簽名,而實現(xiàn)繼承則繼承實際的方法。但是ECMAScript中無法實現(xiàn)接口繼承,只支持實現(xiàn)繼承,而且其實現(xiàn)繼承主要是依靠原型鏈來實現(xiàn)的。(出自《JavaScript高級程序設計》 6.3節(jié)——繼承)在高三中作者探索了各種關于繼承的模擬,如:組合繼承、原型繼承、寄生繼承、寄生組合繼承,最終寄生組合式成為所有模擬類式繼承的基礎。
            function Person(name, age) {
              this.name = name;
              this.age = age;
            };
            Person.prototype.say = function(){
              console.log("Hello, I'm " + this.name);
            };
            function Employee(name, age, major) {
              Person.apply(this, arguments);
              this.major = major;
            };
            Employee.prototype = Object.create(Person.prototype);
            Employee.prorotype.constructor = Employee;
            Employee.prorotype.sayMajor = function(){
              console.log(this.major);
            }
            高三中只給出了單繼承的解決方案,關于多繼承的模擬我們還得自己想辦法。由于多繼承有其本身的困難:面向對象語言如果支持了多繼承的話,都會遇到著名的菱形問題(DiamondProblem)。假設存在一個如左圖所示的繼承關系,O中有一個方法foo,被A類和B類覆寫,但是沒有被C類覆寫。那么C在調用foo方法的時候,究竟是調用A中的foo,還是調用B中的foo?
            名單
            所以大多數(shù)語言并不支持多繼承,如Java支持單繼承+接口的形式。JavaScript并不支持接口,要在一個不支持接口的語言上去模擬接口怎么辦?答案是著名的鴨式辨型。放到實際代碼中就是混入(mixin)。原理很簡單:
            function mixin(t, s) {
               for (var p in s) {
                 t[p] = s[p];
               }
             }
            值得一提的是dojo利用MRO(方法解析順序(Method Resolution Order),即查找被調用的方法所在類時的搜索順序)方式解決了多繼承的問題。
            到此,我們已經(jīng)清楚了模擬類語言的基本原理。作為一個愛折騰的程序員,我希望擁有自己的方式來簡化類的創(chuàng)建:
            提供一種便利的方式去創(chuàng)建類,而不暴露函數(shù)的prototype屬性
            在子類中覆蓋父類方法時,能夠像Java一樣提供super函數(shù),來直接訪問父類同名方法
            以更方便的方式添加靜態(tài)變量和方法而不去關心prototype
            像C#那樣支持Attribute
            最終,在借鑒各位大牛的知識總結,我編寫了自己的類創(chuàng)建工具O.js:
            (function(global) {
              var define = global.define;
              if (define && define.amd) {
                define([], function(){
                  return O;
                });
              } else {
                global.O = O;
              }
              function O(){};
              O.derive = function(sub) {
                debugger;
                var parent = this;
                sub = sub ? sub : {};
                var o = create(parent);
                var ctor = sub.constructor || function(){};//如何調用父類的構造函數(shù)?
                var statics = sub.statics || {};
                var ms = sub.mixins || [];
                var attrs = sub.attributes || {};
                delete sub.constructor;
                delete sub.mixins;
                delete sub.statics;
                delete sub.attributes;
                //處理繼承關系
                ctor.prototype = o;
                ctor.prototype.constructor = ctor;
                ctor.superClass = parent;
                //利用DefineProperties方法處理Attributes
                //for (var p in attrs) {
                  Object.defineProperties(ctor.prototype, attrs);
                //}
                //靜態(tài)屬性
                mixin(ctor, statics);
                //混入其他屬性和方法,注意這里的屬性是所有實例對象都能夠訪問并且修改的
                mixin(ctor.prototype, sub);
                //以mixin的方式模擬多繼承
                for (var i = 0, len = ms.length; i < len; i++) {
                  mixin(ctor.prototype, ms[i] || {});
                }
                ctor.derive = parent.derive;
                //_super函數(shù)
                ctor.prototype._super = function(f) {
                  debugger;
                  return parent.prototype[f].apply(this, Array.prototype.slice.call(arguments, 1));
                }
                return ctor;
              }
              function create(clazz) {
                var F = function(){};
                F.prototype = clazz.prototype;
                //F.prototype.constructor = F; //不需要
                return new F();
              };
              function mixin(t, s) {
                for (var p in s) {
                  t[p] = s[p];
                }
              }
            })(window);
            類創(chuàng)建方式如下:
            var Person = O.derive({
              constructor: function(name) {//構造函數(shù)
                this.setInfo(name);
              },
              statics: {//靜態(tài)變量
                declaredClass: "Person"
              },
              attributes: {//模擬C#中的屬性
                Name: {
                  set: function(n) {
                    this.name = n;
                    console.log(this.name);
                  },
                  get: function() {
                    return this.name + "Attribute";
                  }
                }
              },
              share: "asdsaf",//變量位于原型對象上,對所有對象共享
              setInfo: function(name) {//方法
                this.name = name;
              }
            });
            var p = new Person('lzz');
            console.log(p.Name);//lzzAttribute
            console.log(Person);
            繼承:
            var Employee = Person.derive({//子類有父類派生
              constructor: function(name, age) {
                this.setInfo(name, age);
              },
              statics: {
                declaredClass: "Employee"
              },
              setInfo: function(name, age) {
                this._super('setInfo', name);//調用父類同名方法
                this.age = age;
              }
            });
            var e = new Employee('lll', 25);
            console.log(e.Name);//lllAttribute
            console.log(Employee);
            以上就是本文的全部內容,希望對大家的學習有所幫助。