国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

JavaScript 繼承詳解(五)

2019-11-20 08:46:39
字體:
來源:轉載
供稿:網友

在本章中,我們將分析John Resig關于JavaScript繼承的一個實現 - Simple JavaScript Inheritance。 
John Resig作為jQuery的創始人而聲名在外。是《Pro JavaScript Techniques》的作者,而且Resig將會在今年秋天推出一本書《JavaScript Secrets》,非常期待。

調用方式
調用方式非常優雅:
注意:代碼中的Class、extend、_super都是自定義的對象,我們會在后面的代碼分析中詳解。

var Person = Class.extend({  // init是構造函數  init: function(name) {this.name = name;  },  getName: function() {return this.name;  }});// Employee類從Person類繼承var Employee = Person.extend({  // init是構造函數  init: function(name, employeeID) {// 在構造函數中調用父類的構造函數this._super(name);this.employeeID = employeeID;  },  getEmployeeID: function() {return this.employeeID;  },  getName: function() {// 調用父類的方法return "Employee name: " + this._super();  }});var zhang = new Employee("ZhangSan", "1234");console.log(zhang.getName());  // "Employee name: ZhangSan"

說實話,對于完成本系列文章的目標-繼承-而言,真找不到什么缺點。方法一如jQuery一樣簡潔明了。

代碼分析
為了一個漂亮的調用方式,內部實現的確復雜了很多,不過這些也是值得的 - 一個人的思考帶給了無數程序員快樂的微笑 - 嘿嘿,有點肉麻。
不過其中的一段代碼的確迷惑我一段時間:

fnTest = /xyz/.test(function(){xyz;}) ? //b_super/b/ : /.*/; 

我曾在幾天前的博客中寫過一篇文章專門闡述這個問題,有興趣可以向前翻一翻。

// 自執行的匿名函數創建一個上下文,避免引入全局變量(function() {  // initializing變量用來標示當前是否處于類的創建階段,  // - 在類的創建階段是不能調用原型方法init的  // - 我們曾在本系列的第三篇文章中詳細闡述了這個問題  // fnTest是一個正則表達式,可能的取值為(//b_super/b/ 或 /.*/)  // - 對 /xyz/.test(function() { xyz; }) 的測試是為了檢測瀏覽器是否支持test參數為函數的情況  // - 不過我對IE7.0,Chrome2.0,FF3.5進行了測試,此測試都返回true。  // - 所以我想這樣對fnTest賦值大部分情況下也是對的:fnTest = //b_super/b/;  var initializing = false, fnTest = /xyz/.test(function() { xyz; }) ? //b_super/b/ : /.*/;  // 基類構造函數  // 這里的this是window,所以這整段代碼就向外界開辟了一扇窗戶 - window.Class  this.Class = function() { };  // 繼承方法定義  Class.extend = function(prop) {// 這個地方很是迷惑人,還記得我在本系列的第二篇文章中提到的么// - this具體指向什么不是定義時能決定的,而是要看此函數是怎么被調用的// - 我們已經知道extend肯定是作為方法調用的,而不是作為構造函數// - 所以這里this指向的不是Object,而是Function(即是Class),那么this.prototype就是父類的原型對象// - 注意:_super指向父類的原型對象,我們會在后面的代碼中多次碰見這個變量var _super = this.prototype;// 通過將子類的原型指向父類的一個實例對象來完成繼承// - 注意:this是基類構造函數(即是Class)initializing = true;var prototype = new this();initializing = false;// 我覺得這段代碼是經過作者優化過的,所以讀起來非常生硬,我會在后面詳解for (var name in prop) {  prototype[name] = typeof prop[name] == "function" &&typeof _super[name] == "function" && fnTest.test(prop[name]) ?(function(name, fn) {  return function() {var tmp = this._super;this._super = _super[name];var ret = fn.apply(this, arguments);this._super = tmp;return ret;  };})(name, prop[name]) :prop[name];}// 這個地方可以看出,Resig很會偽裝哦// - 使用一個同名的局部變量來覆蓋全局變量,很是迷惑人// - 如果你覺得拗口的話,完全可以使用另外一個名字,比如function F()來代替function Class()// - 注意:這里的Class不是在最外層定義的那個基類構造函數function Class() {  // 在類的實例化時,調用原型方法init  if (!initializing && this.init)this.init.apply(this, arguments);}// 子類的prototype指向父類的實例(完成繼承的關鍵)Class.prototype = prototype;// 修正constructor指向錯誤Class.constructor = Class;// 子類自動獲取extend方法,arguments.callee指向當前正在執行的函數Class.extend = arguments.callee;return Class;  };})();

下面我會對其中的for-in循環進行解讀,把自執行的匿名方法用一個局部函數來替換, 這樣有利于我們看清真相:

(function() {      var initializing = false, fnTest = /xyz/.test(function() { xyz; }) ? //b_super/b/ : /.*/;      this.Class = function() { };      Class.extend = function(prop) {        var _super = this.prototype;        initializing = true;        var prototype = new this();        initializing = false;        // 如果父類和子類有同名方法,并且子類中此方法(name)通過_super調用了父類方法        // - 則重新定義此方法        function fn(name, fn) {          return function() {            // 將實例方法_super保護起來。            // 個人覺得這個地方沒有必要,因為每次調用這樣的函數時都會對this._super重新定義。            var tmp = this._super;            // 在執行子類的實例方法name時,添加另外一個實例方法_super,此方法指向父類的同名方法            this._super = _super[name];            // 執行子類的方法name,注意在方法體內this._super可以調用父類的同名方法            var ret = fn.apply(this, arguments);            this._super = tmp;                        // 返回執行結果            return ret;          };        }        // 拷貝prop中的所有屬性到子類原型中        for (var name in prop) {          // 如果prop和父類中存在同名的函數,并且此函數中使用了_super方法,則對此方法進行特殊處理 - fn          // 否則將此方法prop[name]直接賦值給子類的原型          if (typeof prop[name] === "function" &&              typeof _super[name] === "function" && fnTest.test(prop[name])) {            prototype[name] = fn(name, prop[name]);          } else {            prototype[name] = prop[name];          }        }        function Class() {          if (!initializing && this.init) {            this.init.apply(this, arguments);          }        }        Class.prototype = prototype;        Class.constructor = Class;        Class.extend = arguments.callee;        return Class;      };    })();

寫到這里,大家是否覺得Resig的實現和我們在第三章一步一步實現的jClass很類似。 其實在寫這一系列的文章之前,我已經對prototype、mootools、extjs、 jQuery-Simple-Inheritance、Crockford-Classical-Inheritance這些實現有一定的了解,并且大部分都在實際項目中使用過。 在第三章中實現jClass也參考了Resig的實現,在此向Resig表示感謝。
下來我們就把jClass改造成和這里的Class具有相同的行為。

我們的實現
將我們在第三章實現的jClass改造成目前John Resig所寫的形式相當簡單,只需要修改其中的兩三行就行了:

 (function() {      // 當前是否處于創建類的階段      var initializing = false;      jClass = function() { };      jClass.extend = function(prop) {        // 如果調用當前函數的對象(這里是函數)不是Class,則是父類        var baseClass = null;        if (this !== jClass) {          baseClass = this;        }        // 本次調用所創建的類(構造函數)        function F() {          // 如果當前處于實例化類的階段,則調用init原型函數          if (!initializing) {            // 如果父類存在,則實例對象的baseprototype指向父類的原型            // 這就提供了在實例對象中調用父類方法的途徑            if (baseClass) {              this._superprototype = baseClass.prototype;            }            this.init.apply(this, arguments);          }        }        // 如果此類需要從其它類擴展        if (baseClass) {          initializing = true;          F.prototype = new baseClass();          F.prototype.constructor = F;          initializing = false;        }        // 新創建的類自動附加extend函數        F.extend = arguments.callee;        // 覆蓋父類的同名函數        for (var name in prop) {          if (prop.hasOwnProperty(name)) {            // 如果此類繼承自父類baseClass并且父類原型中存在同名函數name            if (baseClass &&            typeof (prop[name]) === "function" &&            typeof (F.prototype[name]) === "function" &&            //b_super/b/.test(prop[name])) {              // 重定義函數name -               // 首先在函數上下文設置this._super指向父類原型中的同名函數              // 然后調用函數prop[name],返回函數結果              // 注意:這里的自執行函數創建了一個上下文,這個上下文返回另一個函數,              // 此函數中可以應用此上下文中的變量,這就是閉包(Closure)。              // 這是JavaScript框架開發中常用的技巧。              F.prototype[name] = (function(name, fn) {                return function() {                  this._super = baseClass.prototype[name];                  return fn.apply(this, arguments);                };              })(name, prop[name]);            } else {              F.prototype[name] = prop[name];            }          }        }        return F;      };    })();    // 經過改造的jClass    var Person = jClass.extend({      init: function(name) {        this.name = name;      },      getName: function(prefix) {        return prefix + this.name;      }    });    var Employee = Person.extend({      init: function(name, employeeID) {        // 調用父類的方法        this._super(name);        this.employeeID = employeeID;      },      getEmployeeIDName: function() {        // 注意:我們還可以通過這種方式調用父類中的其他函數        var name = this._superprototype.getName.call(this, "Employee name: ");        return name + ", Employee ID: " + this.employeeID;      },      getName: function() {        // 調用父類的方法        return this._super("Employee name: ");      }    });    var zhang = new Employee("ZhangSan", "1234");    console.log(zhang.getName());  // "Employee name: ZhangSan"    console.log(zhang.getEmployeeIDName()); // "Employee name: ZhangSan, Employee ID: 1234"

這篇文章就接受到這了,下面還有一個系列的文章,大家都可以看下

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 彭泽县| 辽阳市| 江门市| 泰安市| 广河县| 萨嘎县| 读书| 准格尔旗| 图木舒克市| 龙海市| 浠水县| 上虞市| 苗栗县| 山西省| 那坡县| 武安市| 贡觉县| 大理市| 三都| 九江市| 兴化市| 桐乡市| 军事| 库车县| 惠水县| 河南省| 商都县| 类乌齐县| 新乡市| 和顺县| 凭祥市| 旅游| 白山市| 黑水县| 敖汉旗| 涿州市| 元谋县| 浦北县| 长沙市| 南通市| 沽源县|