四虎精品视频-四虎精品成人免费网站-四虎黄色网-四虎国产视频-国产免费91-国产蜜臀97一区二区三区

JavaScript 繼承詳解(四)

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

調(diào)用方式

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

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

 

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

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

 

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

    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)用父類(lèi)的函數(shù)名稱(chēng),后面的參數(shù)都是此函數(shù)的參數(shù)      // 個(gè)人覺(jué)得這樣方式不如這樣調(diào)用來(lái)的直觀: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ù)的定義就很簡(jiǎn)單了:

    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ì)通過(guò)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());            // 只為子類(lèi)的原型增加uber方法,這里的Closure是為了在調(diào)用uber函數(shù)時(shí)知道當(dāng)前類(lèi)的父類(lèi)的原型(也即是變量 - v)      this.method('uber', function uber(name) {        // 這里考慮到如果name是存在于Object.prototype中的函數(shù)名的情況        // 比如 "toString" in {} === true        if (!(name in d)) {          // 通過(guò)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è)人覺(jué)得這段代碼有點(diǎn)繁瑣,既然uber的含義就是父類(lèi)的函數(shù),那么f直接指向v[name]就可以了          f = p[name];          if (f == this[name]) {            f = v[name];          }        }        d[name] += 1;        // 執(zhí)行父類(lèi)中的函數(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ù)組,沒(méi)有slice方法)        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));        d[name] -= 1;        return r;      });      return this;    });    
注意,在inherits函數(shù)中還有一個(gè)小小的BUG,那就是沒(méi)有重定義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è)人覺(jué)得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居然放出了這樣的話(huà):

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

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

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

主站蜘蛛池模板: 玉林电视台| 复仇者联盟4在线完整版观看| 电视剧暗夜与黎明剧情介绍| 第一财经直播电视直播今日股市| 美丽的坏女人中文字幕| 乱世枭雄评书485集免费| 安息2| 郑楚一| 假男假女| nhk| 秋天 课文| 丘淑贞| 楚门的世界演员表| 春闺梦里人演员表| 迎宾进行曲| 即日启程演员表| 大秦帝国第一部免费版| 韩国电影闵度允主演电影| 老男人电影完整版高清在线观看| 意 电影| 好快…好快的| 宋小莹| 二年级合并综合算式题| 林正英全部电影| 胡金铨最好的十部电影| 浙江卫视今晚电视节目表| 电影《48天》免费观看全集| 远景山谷1981免费版| 杨颖电影| 变形金刚6免费完整版在线观看| 陆海涛| 林景云李海海| 红灯停绿灯行电影观看| 妻子的电影| 饶俊| 潜龙轰天 电影| 女同视频在线| 乔什布洛林| 掐脖子的视频| 艳妇乳肉豪妇荡乳xxx| cctv6 节目表|