日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

JavaScript大杂烩4 - 理解JavaScript对象的继承机制

發(fā)布時間:2025/3/15 javascript 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JavaScript大杂烩4 - 理解JavaScript对象的继承机制 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

JavaScript是單根的完全面向?qū)ο蟮恼Z言

  JavaScript是單根的面向?qū)ο笳Z言,它只有單一的根Object,所有的其他對象都是直接或者間接的從Object對象繼承。而在JavaScript的眾多討論中,JavaScript的繼承機(jī)制也是最讓人津津樂道的,在了解它的機(jī)制之前,先讓我們溫習(xí)一下繼承的本質(zhì)。

?

繼承的本質(zhì)

  繼承的本質(zhì)是重用,就是這么簡單,重用是軟件編程技術(shù)向前發(fā)展最原始的動力。從語法上來講,繼承就是"D是B"的描述,其中B是基類,描述共性,D是子類,描述特性。例如"貓是動物",動物就是基類,貓是子類,動物的共性,比如會呼吸,貓也會,但是貓的特性,如打呼嚕,別的動物就不一定有了。

?

JavaScript的原型繼承機(jī)制

  前面我們已經(jīng)總結(jié)過了原型的作用,原型鏈繼承是JavaScript實現(xiàn)繼承的主要方式。這種繼承的典型實現(xiàn)方式如下:

function Base(name) {// 基對象構(gòu)造函數(shù)實例成員this.name = name; }; // 基對象原型中的實例成員 Base.prototype.showName = function() {alert(this.name); }; // 子對象的構(gòu)造函數(shù) function Derived(name, age) {// 關(guān)鍵點1:獲取基對象構(gòu)造函數(shù)中的成員Base.call(this, name);// 定義子對象自己的成員this.age = age; }; // 關(guān)鍵點2:獲取基對象原型上的成員 Derived.prototype = new Base(); // 關(guān)鍵點3:將子對象的原型的構(gòu)造函數(shù)屬性設(shè)回正確的值 // 這樣可以防止new對象的掛接對象出錯 Derived.prototype.constructor = Derived; // 擴(kuò)展子類自己的原型成員 Derived.prototype.showAge = function() {alert(this.age); };var d = new Derived('Frank', 10); d.showName(); d.showAge(); // 驗證繼承關(guān)系 alert(d instanceof Base);

上面的實現(xiàn)關(guān)鍵點已經(jīng)標(biāo)了出來:

關(guān)鍵點1:使用call方式來調(diào)用基對象的構(gòu)造函數(shù),這樣子對象就能按照基對象構(gòu)造函數(shù)的邏輯來構(gòu)造一份基對象構(gòu)造函數(shù)中的成員。

關(guān)鍵點2:使用new方式來創(chuàng)建一個新的Base對象,然后把它掛到Derived對象的原型上,這樣從"Derived對象->Derived原型->Base對象->Base原型->Object對象->Object原型"就形成了鏈條了。

關(guān)鍵點3:new操作符要求構(gòu)造函數(shù)對象的原型的constructor屬性要指向構(gòu)造函數(shù),而在關(guān)鍵點2中把Derived對象的原型完全換成Base對象了,這樣有問題了。修復(fù)這個問題也很簡單,直接把這個屬性重新設(shè)一下就行了。

  上面的實現(xiàn)近乎完美,只有一個問題:Base對象的構(gòu)造函數(shù)被調(diào)用了2次,一次在構(gòu)造函數(shù)中,一次在構(gòu)造原型鏈中,這樣的效率并不高。而根據(jù)上面的幾點說明我們知道第一次調(diào)用是為了復(fù)制基對象構(gòu)造函數(shù)中的成員,必不可少;而第二次的調(diào)用純粹是為了把原型鏈接上,構(gòu)造函數(shù)中的成員并沒有使用上,于是這里就存在一個優(yōu)化的契機(jī):既然構(gòu)造函數(shù)中的成員沒有使用到,那我就用一個空對象來輔助創(chuàng)建原型鏈不就就可以了,看下面的代碼:

// 利用空對象來掛接原型鏈 function extend(Child, Parent) {var F = function(){};F.prototype = Parent.prototype;Child.prototype = new F();Child.prototype.constructor = Child; }function Base(name) {this.name = name; };Base.prototype.showName = function() {alert(this.name); };function Derived(name, age) {Base.call(this, name);this.age = age; }; // 掛接原型鏈 extend(Derived, Base); Derived.prototype.showAge = function() {alert(this.age); };var d = new Derived('Frank', 10); d.showName(); d.showAge(); alert(d instanceof Base);

  這個版本的實現(xiàn)據(jù)說是YUI的實現(xiàn)繼承的方式,個人并沒參看其源代碼,有這個愛好的同學(xué)可以自行研究一下。

?

復(fù)制繼承

  既然繼承的本質(zhì)是復(fù)用,那么最直接的想法應(yīng)該是復(fù)制基對象的所有成員,在JavaScript中確實可以這么做,看下面的代碼:

// 淺拷貝實現(xiàn) function extendCopy(p) {var c = {};for (var i in p) { c[i] = p[i];}return c; } // 深拷貝實現(xiàn),最為安全 function deepCopy(p, c) {var c = c || {};for (var i in p) {if (typeof p[i] === 'object') {c[i] = (p[i].constructor === Array) ? [] : {};deepCopy(p[i], c[i]);} else {c[i] = p[i];}}return c;} // 基對象 var Base = {name: 'Frank',showName: function() {alert(this.name);} } // 拷貝基對象的成員 var Derived = extendCopy(Base); // 擴(kuò)展子對象自己的成員 Derived.age = 10; Derived.showAge = function() {alert(this.age); }; // 測試基對象的成員 Derived.showName();

在上面的代碼中,我們需要注意兩個問題:

1. 我們從前面已經(jīng)了解過,JavaScript有簡單的“值類型”和復(fù)雜的“引用類型”,這樣復(fù)制成員的時候就也有所謂的“淺拷貝”和“深拷貝”的說法。上面的代碼實現(xiàn)了兩種算法,深拷貝本質(zhì)上就是為引用類型創(chuàng)建新的對象,這樣修改的時候就不會誤操作,修改了其他對象的成員。

2. 這種對象的擴(kuò)展方式不能使用instanceof來檢查繼承關(guān)系,例如下面的代碼是無效的:

alert(Derived instanceof Base);

運行這段代碼會返回異常:Uncaught TypeError: Expecting a function in instanceof check, but got #<Object> 。這是因為instanceof只能用于檢查函數(shù)實現(xiàn)的那種繼承關(guān)系。

?

JavaScript的多態(tài)性

  談完了JavaScript的繼承機(jī)制,那就不能不說說與之密切相關(guān)的多態(tài)性。繼承與多態(tài)從來都是面向?qū)ο笳Z言中不可分割的兩個概念。

  由于JavaScript是腳本語言,動態(tài)語言,所以靜態(tài)的類型約束關(guān)系被壓縮到了極致。這一方面體現(xiàn)最為明顯的一點就是我們可以隨意的給對象添加和刪除成員,而另一個方面,很多語言都遵循“針對接口”的編程,這一點在動態(tài)語言中的表現(xiàn)也大為不同。在JavaScript這些動態(tài)語言中,我們不需要事先定義好一些接口,例如下面的例子:

var tank = {run : function () {alert('tank run');} };var person = {run : function () {alert('person run');} }; // 針對接口(run方法)的對象編程 function trigger(target) {target.run(); }trigger(tank); trigger(person);

很多人對于這種使用方式不以為然,但是個人覺得這正是動態(tài)語言快捷編程的特點,很多時候還是很方便的。

轉(zhuǎn)載于:https://www.cnblogs.com/dxy1982/p/2688525.html

總結(jié)

以上是生活随笔為你收集整理的JavaScript大杂烩4 - 理解JavaScript对象的继承机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。