搜尋
首頁web前端js教程JavaScript程式中實作繼承特性的方式(圖文教學)

JavaScript是一門強行聲稱物件導向的語言,而繼承是物件導向的一大主要特性,以下是我給大家整理的,有興趣的同學可以去看看。

概述JavaScript的所有對象,都有自己的繼承鏈。也就是說,每個物件都繼承另一個對象,該物件稱為「原型」(prototype)物件。只有null除外,它沒有自己的原型物件。

原型物件的重要性在於,如果A物件是B物件的原型,那麼B物件可以拿到A物件的所有屬性和方法。 Object.getPrototypof方法用於取得目前物件的原型物件。

var p = Object.getPrototypeOf(obj);

上面程式碼中,物件p就是物件obj的原型物件。

Object.create方法用來產生一個新的對象,繼承指定對象。

var obj = Object.create(p);

上面程式碼中,新產生的obj物件的原型就是物件p。

非標準的__proto__屬性(前後各兩個底線),可以改寫某個物件的原型物件。但是,應該盡量少用這個屬性,而是用Object.getPrototypeof()和Object.setPrototypeOf(),進行原型物件的讀寫操作。

var obj = {};
var p = {};

obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true

上面程式碼透過__proto__屬性,將p物件設為obj物件的原型。

下面是一個實際的例子。

var a = {x: 1};
var b = {__proto__: a};
b.x // 1

上面程式碼中,b物件透過__proto__屬性,將自己的原型物件設為a對象,因此b物件可以拿到a物件的所有屬性和方法。 b物件本身並沒有x屬性,但是JavaScript引擎透過__proto__屬性,找到它的原型物件a,然後讀取a的x屬性。

new指令透過建構函式新建實例對象,實質就是將實例物件的原型綁定建構函式的prototype屬性,然後在實例物件上執行建構函式。

var o = new Foo();

// 等同于
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);

原型物件自己的__proto__屬性,也可以指向其他對象,從而一級一級地形成「原型鏈」(prototype chain)。

var a = { x: 1 };
var b = { __proto__: a };
var c = { __proto__: b };

c.x // 1

要注意的是,一級級向上,在原型鏈尋找某個屬性,對性能是有影響的。所尋找的屬性在越上層的原型對象,對效能的影響越大。如果尋找某個不存在的屬性,將會遍歷整個原型鏈。

this的動作指向不管this在哪裡定義,使用的時候,它總是指向當前對象,而不是原型對象。

var o = {
 a: 2,
 m: function(b) {
  return this.a + 1;
 }
};

var p = Object.create(o);
p.a = 12;

p.m() // 13

上面程式碼中,p物件的m方法來自它的原型物件o。這時,m方法內部的this對象,不指向o,而是指向p。

建構函數的繼承這個小節介紹,如何讓一個建構函數,繼承另一個建構子。

假定有一個Shape建構子。

function Shape() {
 this.x = 0;
 this.y = 0;
}

Shape.prototype.move = function (x, y) {
 this.x += x;
 this.y += y;
 console.info('Shape moved.');
};
Rectangle构造函数继承Shape。

function Rectangle() {
 Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
 this.base = Shape;
 this.base();
}

// 子类继承父类的方法
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

rect instanceof Rectangle // true
rect instanceof Shape // true

rect.move(1, 1) // 'Shape moved.'

上面程式碼表示,建構函式的繼承分成兩部分,一部分是子類別呼叫父類別的建構方法,另一部分是子類別的原型指向父類別的原型。

上面程式碼中,子類別是整體繼承父類別。有時,只需要單一方法的繼承,這時可以採用下面的寫法。

ClassB.prototype.print = function() {
 ClassA.prototype.print.call(this);
 // some code
}

上面程式碼中,子類別B的print方法先呼叫父類別A的print方法,再部署自己的程式碼。這就等於繼承了父類別A的print方法。

__proto__屬性__proto__屬性指向目前物件的原型對象,即建構函數的prototype屬性。

var obj = new Object();

obj.__proto__ === Object.prototype
// true
obj.__proto__ === obj.constructor.prototype
// true

上面程式碼首先新建了一個物件obj,它的__proto__屬性,指向建構函式(Object或obj.constructor)的prototype屬性。所以,兩者比較以後,回傳true。

因此,取得實例物件obj的原型對象,有三種方法。

  • obj.__proto__

  • #obj.constructor.prototype

  • Object.getPrototypeOf(obj)

上面三種方法之中,前兩種都不是很可靠。最新的ES6標準規定,__proto__屬性只有瀏覽器才需要部署,其他環境可以不部署。而obj.constructor.prototype在手動改變原型物件時,可能會失效。

var P = function () {};
var p = new P();

var C = function () {};
C.prototype = p;
var c = new C();

c.constructor.prototype === p // false

上面程式碼中,C建構子的原型物件被改成了p,結果c.constructor.prototype就失真了。所以,改變原型物件時,一般要同時設定constructor屬性。

C.prototype = p;
C.prototype.constructor = C;

c.constructor.prototype === p // true

所以,建議使用第三種Object.getPrototypeOf方法,取得原型物件。該方法的用法如下。

var o = new Object();

Object.getPrototypeOf(o) === Object.prototype
// true

可以使用Object.getPrototypeOf方法,檢查瀏覽器是否支援__proto__屬性,老式瀏覽器不支援這個屬性。

Object.getPrototypeOf({ __proto__: null }) === null

上面程式碼將一個物件的__proto__屬性設為null,然後使用Object.getPrototypeOf方法取得這個物件的原型,判斷是否等於null。如果目前環境支援__proto__屬性,兩者的比較結果應該是true。

有了__proto__屬性,就可以很方便得設定實例物件的原型了。假設有三個物件machine、vehicle和car,其中machine是vehicle的原型,vehicle又是car的原型,只要兩行程式碼就可以設定。

vehicle.__proto__ = machine;
car.__proto__ = vehicle;

下面是一個實例,透過__proto__屬性與constructor.prototype屬性兩種方法,分別讀取定義在原型物件上的屬性。

Array.prototype.p = 'abc';
var a = new Array();

a.__proto__.p // abc
a.constructor.prototype.p // abc

顯然,__proto__看起來更簡潔一些。

通过构造函数生成实例对象时,实例对象的__proto__属性自动指向构造函数的prototype对象。

var f = function (){};
var a = {};

f.prototype = a;
var o = new f();

o.__proto__ === a
// true

属性的继承属性分成两种。一种是对象自身的原生属性,另一种是继承自原型的继承属性。

对象的原生属性对象本身的所有属性,可以用Object.getOwnPropertyNames方法获得。

Object.getOwnPropertyNames(Date)
// ["parse", "arguments", "UTC", "caller", "name", "prototype", "now", "length"]

对象本身的属性之中,有的是可以枚举的(enumerable),有的是不可以枚举的。只获取那些可以枚举的属性,使用Object.keys方法。

Object.keys(Date) // []
hasOwnProperty()

hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。

Date.hasOwnProperty('length')
// true

Date.hasOwnProperty('toString')
// false

hasOwnProperty方法是JavaScript之中唯一一个处理对象属性时,不会遍历原型链的方法。

对象的继承属性用Object.create方法创造的对象,会继承所有原型对象的属性。

var proto = { p1: 123 };
var o = Object.create(proto);

o.p1 // 123
o.hasOwnProperty("p1") // false

获取所有属性判断一个对象是否具有某个属性(不管是自身的还是继承的),使用in运算符。

"length" in Date // true
"toString" in Date // true

获得对象的所有可枚举属性(不管是自身的还是继承的),可以使用for-in循环。

var o1 = {p1: 123};

var o2 = Object.create(o1,{
 p2: { value: "abc", enumerable: true }
});

for (p in o2) {console.info(p);}
// p2
// p1

为了在for...in循环中获得对象自身的属性,可以采用hasOwnProperty方法判断一下。

for ( var name in object ) {
 if ( object.hasOwnProperty(name) ) {
  /* loop code */
 }
}

获得对象的所有属性(不管是自身的还是继承的,以及是否可枚举),可以使用下面的函数。

function inheritedPropertyNames(obj) {
 var props = {};
 while(obj) {
  Object.getOwnPropertyNames(obj).forEach(function(p) {
   props[p] = true;
  });
  obj = Object.getPrototypeOf(obj);
 }
 return Object.getOwnPropertyNames(props);
}

用法如下:

inheritedPropertyNames(Date)
// ["caller", "constructor", "toString", "UTC", "call", "parse", "prototype", "__defineSetter__", "__lookupSetter__", "length", "arguments", "bind", "__lookupGetter__", "isPrototypeOf", "toLocaleString", "propertyIsEnumerable", "valueOf", "apply", "__defineGetter__", "name", "now", "hasOwnProperty"]

对象的拷贝如果要拷贝一个对象,需要做到下面两件事情。

确保拷贝后的对象,与原对象具有同样的prototype原型对象。
确保拷贝后的对象,与原对象具有同样的属性。
下面就是根据上面两点,编写的对象拷贝的函数。

function copyObject(orig) {
 var copy = Object.create(Object.getPrototypeOf(orig));
 copyOwnPropertiesFrom(copy, orig);
 return copy;
}

function copyOwnPropertiesFrom(target, source) {
 Object
 .getOwnPropertyNames(source)
 .forEach(function(propKey) {
  var desc = Object.getOwnPropertyDescriptor(source, propKey);
  Object.defineProperty(target, propKey, desc);
 });
 return target;
}

多重继承JavaScript不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。

function M1(prop) {
 this.hello = prop;
}

function M2(prop) {
 this.world = prop;
}

function S(p1, p2) {
 this.base1 = M1;
 this.base1(p1);
 this.base2 = M2;
 this.base2(p2);
}
S.prototype = new M1();

var s = new S(111, 222);
s.hello // 111
s.world // 222

上面代码中,子类S同时继承了父类M1和M2。当然,从继承链来看,S只有一个父类M1,但是由于在S的实例上,同时执行M1和M2的构造函数,所以它同时继承了这两个类的方法。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

详细介绍在JS中Map和ForEach的区别

js装饰设计模式学习心得(详细解答)

详解解读JS数值Number类型(图文教程)

以上是JavaScript程式中實作繼承特性的方式(圖文教學)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
es6数组怎么去掉重复并且重新排序es6数组怎么去掉重复并且重新排序May 05, 2022 pm 07:08 PM

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

JavaScript的Symbol类型、隐藏属性及全局注册表详解JavaScript的Symbol类型、隐藏属性及全局注册表详解Jun 02, 2022 am 11:50 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

原来利用纯CSS也能实现文字轮播与图片轮播!原来利用纯CSS也能实现文字轮播与图片轮播!Jun 10, 2022 pm 01:00 PM

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

JavaScript对象的构造函数和new操作符(实例详解)JavaScript对象的构造函数和new操作符(实例详解)May 10, 2022 pm 06:16 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

JavaScript面向对象详细解析之属性描述符JavaScript面向对象详细解析之属性描述符May 27, 2022 pm 05:29 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

javascript怎么移除元素点击事件javascript怎么移除元素点击事件Apr 11, 2022 pm 04:51 PM

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

整理总结JavaScript常见的BOM操作整理总结JavaScript常见的BOM操作Jun 01, 2022 am 11:43 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。

foreach是es6里的吗foreach是es6里的吗May 05, 2022 pm 05:59 PM

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。