色尼玛亚洲综合影院,亚洲3atv精品一区二区三区,麻豆freexxxx性91精品,欧美在线91

JavaScript 繼承詳解(四)

Classical Inheritance in JavaScript。
Crockford是JavaScript開發(fā)社區(qū)最知名的權(quán)威,是JSON、JSLint、JSMin和ADSafe之父,是《JavaScript: The Good Parts》的作者。
現(xiàn)在是Yahoo的資深JavaScript架構(gòu)師,參與YUI的設(shè)計(jì)開發(fā)。 這里有一篇文章詳細(xì)介紹了Crockford的生平和著作。
當(dāng)然Crockford也是我等小輩崇拜的對(duì)象。

調(diào)用方式

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

    // 定義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() {      // 注意,可以在子類中調(diào)用父類的原型方法      return "Employee name: " + this.uber("getName");    });    // 實(shí)例化子類    var zhang = new Employee("ZhangSan", "1234");    console.log(zhang.getName());  // "Employee name: ZhangSan"    

 

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

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

 

當(dāng)然Crockford的實(shí)現(xiàn)還支持子類中的方法調(diào)用帶參數(shù)的父類方法,如下例子:

    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的第一個(gè)參數(shù)是要調(diào)用父類的函數(shù)名稱,后面的參數(shù)都是此函數(shù)的參數(shù)      // 個(gè)人覺得這樣方式不如這樣調(diào)用來的直觀:this.uber("Employee name: ")      return this.uber("getName", "Employee name: ");    });    var zhang = new Employee("ZhangSan", "1234");    console.log(zhang.getName());  // "Employee name: ZhangSan"    

 

代碼分析

首先method函數(shù)的定義就很簡單了:

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

 

inherits函數(shù)的定義有點(diǎn)復(fù)雜:

    Function.method('inherits', function (parent) {      // 關(guān)鍵是這一段:this.prototype = new parent(),這里實(shí)現(xiàn)了原型的引用      var d = {}, p = (this.prototype = new parent());            // 只為子類的原型增加uber方法,這里的Closure是為了在調(diào)用uber函數(shù)時(shí)知道當(dāng)前類的父類的原型(也即是變量 - v)      this.method('uber', function uber(name) {        // 這里考慮到如果name是存在于Object.prototype中的函數(shù)名的情況        // 比如 "toString" in {} === true        if (!(name in d)) {          // 通過d[name]計(jì)數(shù),不理解具體的含義          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 {          // 個(gè)人覺得這段代碼有點(diǎn)繁瑣,既然uber的含義就是父類的函數(shù),那么f直接指向v[name]就可以了          f = p[name];          if (f == this[name]) {            f = v[name];          }        }        d[name] += 1;        // 執(zhí)行父類中的函數(shù)name,但是函數(shù)中this指向當(dāng)前對(duì)象        // 同時(shí)注意使用Array.prototype.slice.apply的方式對(duì)arguments進(jìn)行截?cái)啵ㄒ驗(yàn)閍rguments不是標(biāo)準(zhǔn)的數(shù)組,沒有slice方法)        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));        d[name] -= 1;        return r;      });      return this;    });    
注意,在inherits函數(shù)中還有一個(gè)小小的BUG,那就是沒有重定義constructor的指向,所以會(huì)發(fā)生如下的錯(cuò)誤:
    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    

 

改進(jìn)建議

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

    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));      };    };    
調(diào)用方式:
    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    

 

有點(diǎn)意思

在文章的結(jié)尾,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對(duì)在JavaScript中實(shí)現(xiàn)面向?qū)ο蟮木幊滩毁澇桑⑶衣暦QJavaScript應(yīng)該按照原型和函數(shù)的模式(the prototypal and functional patterns)進(jìn)行編程。
不過就我個(gè)人而言,在復(fù)雜的場景中如果有面向?qū)ο蟮臋C(jī)制會(huì)方便的多。
但誰有能擔(dān)保呢,即使像jQuery UI這樣的項(xiàng)目也沒用到繼承,而另一方面,像Extjs、Qooxdoo則極力倡導(dǎo)一種面向?qū)ο蟮?a href=/itjie/Javajishu/ target=_blank class=infotextkey>JavaScript。 甚至Cappuccino項(xiàng)目還發(fā)明一種Objective-J語言來實(shí)踐面向?qū)ο蟮?a href=/itjie/Javajishu/ target=_blank class=infotextkey>JavaScript。

JavaScript技術(shù)JavaScript 繼承詳解(四),轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 浙江省| 会泽县| 三门县| 子洲县| 威信县| 应城市| 威信县| 郑州市| 谢通门县| 合川市| 云和县| 固阳县| 沅陵县| 海晏县| 长丰县| 平南县| 连城县| 西乌珠穆沁旗| 新化县| 越西县| 永泰县| 普宁市| 河池市| 丰县| 深水埗区| 嘉黎县| 苍南县| 正宁县| 博湖县| 阿城市| 朝阳区| 揭西县| 宁蒗| 色达县| 双柏县| 盈江县| 广州市| 清水县| 晋城| 司法| 韶山市|