天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

JavaScript 繼承詳解(四)

Classical Inheritance in JavaScript。
Crockford是JavaScript開發社區最知名的權威,是JSON、JSLint、JSMin和ADSafe之父,是《JavaScript: The Good Parts》的作者。
現在是Yahoo的資深JavaScript架構師,參與YUI的設計開發。 這里有一篇文章詳細介紹了Crockford的生平和著作。
當然Crockford也是我等小輩崇拜的對象。

調用方式

首先讓我們看下使用Crockford式繼承的調用方式:
注意:代碼中的method、inherits、uber都是自定義的對象,我們會在后面的代碼分析中詳解。

    // 定義Person類    function Person(name) {      this.name = name;    }    // 定義Person的原型方法    Person.method("getName", function() {      return this.name;    });         // 定義Employee類    function Employee(name, employeeID) {      this.name = name;      this.employeeID = employeeID;    }    // 指定Employee類從Person類繼承    Employee.inherits(Person);    // 定義Employee的原型方法    Employee.method("getEmployeeID", function() {      return this.employeeID;    });    Employee.method("getName", function() {      // 注意,可以在子類中調用父類的原型方法      return "Employee name: " + this.uber("getName");    });    // 實例化子類    var zhang = new Employee("ZhangSan", "1234");    console.log(zhang.getName());  // "Employee name: ZhangSan"    

 

這里面有幾處不得不提的硬傷:

  • 子類從父類繼承的代碼必須在子類和父類都定義好之后進行,并且必須在子類原型方法定義之前進行。
  • 雖然子類方法體中可以調用父類的方法,但是子類的構造函數無法調用父類的構造函數。
  • 代碼的書寫不夠優雅,比如原型方法的定義以及調用父類的方法(不直觀)。

 

當然Crockford的實現還支持子類中的方法調用帶參數的父類方法,如下例子:

    function Person(name) {      this.name = name;    }    Person.method("getName", function(prefix) {      return prefix + this.name;    });    function Employee(name, employeeID) {      this.name = name;      this.employeeID = employeeID;    }    Employee.inherits(Person);    Employee.method("getName", function() {      // 注意,uber的第一個參數是要調用父類的函數名稱,后面的參數都是此函數的參數      // 個人覺得這樣方式不如這樣調用來的直觀:this.uber("Employee name: ")      return this.uber("getName", "Employee name: ");    });    var zhang = new Employee("ZhangSan", "1234");    console.log(zhang.getName());  // "Employee name: ZhangSan"    

 

代碼分析

首先method函數的定義就很簡單了:

    Function.prototype.method = function(name, func) {      // this指向當前函數,也即是typeof(this) === "function"      this.prototype[name] = func;      return this;    };    
要特別注意這里this的指向。當我們看到this時,不能僅僅關注于當前函數,而應該想到當前函數的調用方式。 比如這個例子中的method我們不會通過new的方式調用,所以method中的this指向的是當前函數。

 

inherits函數的定義有點復雜:

    Function.method('inherits', function (parent) {      // 關鍵是這一段:this.prototype = new parent(),這里實現了原型的引用      var d = {}, p = (this.prototype = new parent());            // 只為子類的原型增加uber方法,這里的Closure是為了在調用uber函數時知道當前類的父類的原型(也即是變量 - v)      this.method('uber', function uber(name) {        // 這里考慮到如果name是存在于Object.prototype中的函數名的情況        // 比如 "toString" in {} === true        if (!(name in d)) {          // 通過d[name]計數,不理解具體的含義          d[name] = 0;        }            var f, r, t = d[name], v = parent.prototype;        if (t) {          while (t) {            v = v.constructor.prototype;            t -= 1;          }          f = v[name];        } else {          // 個人覺得這段代碼有點繁瑣,既然uber的含義就是父類的函數,那么f直接指向v[name]就可以了          f = p[name];          if (f == this[name]) {            f = v[name];          }        }        d[name] += 1;        // 執行父類中的函數name,但是函數中this指向當前對象        // 同時注意使用Array.prototype.slice.apply的方式對arguments進行截斷(因為arguments不是標準的數組,沒有slice方法)        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));        d[name] -= 1;        return r;      });      return this;    });    
注意,在inherits函數中還有一個小小的BUG,那就是沒有重定義constructor的指向,所以會發生如下的錯誤:
    var zhang = new Employee("ZhangSan", "1234");    console.log(zhang.getName());  // "Employee name: ZhangSan"    console.log(zhang.constructor === Employee);  // false    console.log(zhang.constructor === Person);   // true    

 

改進建議

根據前面的分析,個人覺得method函數必要性不大,反而容易混淆視線。 而inherits方法可以做一些瘦身(因為Crockford可能考慮更多的情況,原文中介紹了好幾種使用inherits的方式,而我們只關注其中的一種), 并修正了constructor的指向錯誤。

    Function.prototype.inherits = function(parent) {      this.prototype = new parent();      this.prototype.constructor = this;      this.prototype.uber = function(name) {        f = parent.prototype[name];        return f.apply(this, Array.prototype.slice.call(arguments, 1));      };    };    
調用方式:
    function Person(name) {      this.name = name;    }    Person.prototype.getName = function(prefix) {      return prefix + this.name;    };    function Employee(name, employeeID) {      this.name = name;      this.employeeID = employeeID;    }    Employee.inherits(Person);    Employee.prototype.getName = function() {      return this.uber("getName", "Employee name: ");    };    var zhang = new Employee("ZhangSan", "1234");    console.log(zhang.getName());  // "Employee name: ZhangSan"    console.log(zhang.constructor === Employee);  // true    

 

有點意思

在文章的結尾,Crockford居然放出了這樣的話:

I have been writing JavaScript for 8 years now, and I have never once found need to use an uber function. The super idea is fairly important in the classical pattern, but it appears to be unnecessary in the prototypal and functional patterns. I now see my early attempts to support the classical model in JavaScript as a mistake.
可見Crockford對在JavaScript中實現面向對象的編程不贊成,并且聲稱JavaScript應該按照原型和函數的模式(the prototypal and functional patterns)進行編程。
不過就我個人而言,在復雜的場景中如果有面向對象的機制會方便的多。
但誰有能擔保呢,即使像jQuery UI這樣的項目也沒用到繼承,而另一方面,像Extjs、Qooxdoo則極力倡導一種面向對象的JavaScript。 甚至Cappuccino項目還發明一種Objective-J語言來實踐面向對象的JavaScript。

JavaScript技術JavaScript 繼承詳解(四),轉載需保留來源!

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

主站蜘蛛池模板: 俄罗斯6一9泑女网站 | gogogo视频在线观看 | 手机在线观看你懂的 | 国产福利高清在线视频 | 一区二区视频在线观看高清视频在线 | 亚洲中文字幕乱倫在线 | 亚洲精品在线不卡 | 国产欧美另类久久久品 | 日本色呦呦 | 久久偷拍国2017的 | 老阿姨才是最有味的一区二区 | 无码AV熟妇素人内射V在线 | 玩弄人妻少妇500系列网址 | 51无码人妻精品1国产 | CHINA学生白嫩| 果冻传媒2021一二三在线观看 | 免费观看美女的网站 | GOGOGO高清在线播放免费 | 8050午夜二级一片 | 日本欧美久久久久免费播放网 | jizzzz亚洲丰满xxxx | 国产AV电影区二区三区曰曰骚网 | 亚洲影院在线播放 | 暖暖视频大全免费观看 | 扒开双腿疯进出爽爽爽动态图 | 国产精品人妻午夜福利 | 成人小视频在线免费观看 | 亚洲精品无码葡京AV天堂 | 老熟女重囗味GRANNYBBW | 亚洲合集综合久久性色 | 日日摸天天添天天添无码蜜臀 | 日韩av无码在线直播 | 儿子操妈妈 | 厨房玩朋友娇妻中文字幕 | 国产99久久久国产精品成人 | 99国产热视频在线观看 | 中文免费视频 | 久久精品视频16 | 国产成人精品在视频 | 久久免费精品一区二区 | 外国三级片名 |