Jadual Kandungan
修正constructor的指向错误
创建Employee类时实例化Person是不合适的
如何自动调用init函数?
如何避免引入全局变量initializing?
如何调用父类的同名方法?
优化jClass函数
Rumah hujung hadapan web tutorial js JavaScript 继承详解(三)_js面向对象

JavaScript 继承详解(三)_js面向对象

May 16, 2016 pm 06:50 PM
javascript mewarisi

注:本章中的jClass的实现参考了Simple JavaScript Inheritance的做法。

首先让我们来回顾一下第一章中介绍的例子:

 function Person(name) {<BR>
this.name = name;<BR>
}<BR>
Person.prototype = {<BR>
getName: function() {<BR>
return this.name;<BR>
}<BR>
}<br><br>
function Employee(name, employeeID) {<BR>
this.name = name;<BR>
this.employeeID = employeeID;<BR>
}<BR>
Employee.prototype = new Person();<BR>
Employee.prototype.getEmployeeID = function() {<BR>
return this.employeeID;<BR>
};<BR>
var zhang = new Employee("ZhangSan", "1234");<BR>
console.log(zhang.getName()); // "ZhangSan" <BR>
Salin selepas log masuk

修正constructor的指向错误

从上一篇文章中关于constructor的描述,我们知道Employee实例的constructor会有一个指向错误,如下所示:

 var zhang = new Employee("ZhangSan", "1234");<BR>
console.log(zhang.constructor === Employee); // false<BR>
console.log(zhang.constructor === Object); // true <BR>
Salin selepas log masuk
我们需要简单的修正:
 function Employee(name, employeeID) {<BR>
this.name = name;<BR>
this.employeeID = employeeID;<BR>
}<BR>
Employee.prototype = new Person();<BR>
Employee.prototype.constructor = Employee;<BR>
Employee.prototype.getEmployeeID = function() {<BR>
return this.employeeID;<BR>
};<BR>
var zhang = new Employee("ZhangSan", "1234");<BR>
console.log(zhang.constructor === Employee); // true<BR>
console.log(zhang.constructor === Object); // false<BR>
Salin selepas log masuk

创建Employee类时实例化Person是不合适的

但另一方面,我们又必须依赖于这种机制来实现继承。 解决办法是不在构造函数中初始化数据,而是提供一个原型方法(比如init)来初始化数据。

 // 空的构造函数<BR>
function Person() {<BR>
}<BR>
Person.prototype = {<BR>
init: function(name) {<BR>
this.name = name;<BR>
},<BR>
getName: function() {<BR>
return this.name;<BR>
}<BR>
}<BR>
// 空的构造函数<BR>
function Employee() {<BR>
}<BR>
// 创建类的阶段不会初始化父类的数据,因为Person是一个空的构造函数<BR>
Employee.prototype = new Person();<BR>
Employee.prototype.constructor = Employee;<BR>
Employee.prototype.init = function(name, employeeID) {<BR>
this.name = name;<BR>
this.employeeID = employeeID;<BR>
};<BR>
Employee.prototype.getEmployeeID = function() {<BR>
return this.employeeID;<BR>
};<BR>
Salin selepas log masuk
这种方式下,必须在实例化一个对象后手工调用init函数,如下:
 var zhang = new Employee();<BR>
zhang.init("ZhangSan", "1234");<BR>
console.log(zhang.getName()); // "ZhangSan"<BR>
Salin selepas log masuk

如何自动调用init函数?

必须达到两个效果,构造类时不要调用init函数和实例化对象时自动调用init函数。看来我们需要在调用空的构造函数时有一个状态标示。

 // 创建一个全局的状态标示 - 当前是否处于类的构造阶段<BR>
var initializing = false;<BR>
function Person() {<BR>
if (!initializing) {<BR>
this.init.apply(this, arguments);<BR>
}<BR>
}<BR>
Person.prototype = {<BR>
init: function(name) {<BR>
this.name = name;<BR>
},<BR>
getName: function() {<BR>
return this.name;<BR>
}<BR>
}<BR>
function Employee() {<BR>
if (!initializing) {<BR>
this.init.apply(this, arguments);<BR>
}<BR>
}<BR>
// 标示当前进入类的创建阶段,不会调用init函数<BR>
initializing = true;<BR>
Employee.prototype = new Person();<BR>
Employee.prototype.constructor = Employee;<BR>
initializing = false;<BR>
Employee.prototype.init = function(name, employeeID) {<BR>
this.name = name;<BR>
this.employeeID = employeeID;<BR>
};<BR>
Employee.prototype.getEmployeeID = function() {<BR>
return this.employeeID;<BR>
};<br><br>
// 初始化类实例时,自动调用类的原型函数init,并向init中传递参数<BR>
var zhang = new Employee("ZhangSan", "1234");<BR>
console.log(zhang.getName()); // "ZhangSan"<BR>
Salin selepas log masuk
但是这样就必须引入全局变量,这是一个不好的信号。

如何避免引入全局变量initializing?

我们需要引入一个全局的函数来简化类的创建过程,同时封装内部细节避免引入全局变量。

 // 当前是否处于创建类的阶段<BR>
var initializing = false;<BR>
function jClass(baseClass, prop) {<BR>
// 只接受一个参数的情况 - jClass(prop)<BR>
if (typeof (baseClass) === "object") {<BR>
prop = baseClass;<BR>
baseClass = null;<BR>
}<BR>
// 本次调用所创建的类(构造函数)<BR>
function F() {<BR>
// 如果当前处于实例化类的阶段,则调用init原型函数<BR>
if (!initializing) {<BR>
this.init.apply(this, arguments);<BR>
}<BR>
}<BR>
// 如果此类需要从其它类扩展<BR>
if (baseClass) {<BR>
initializing = true;<BR>
F.prototype = new baseClass();<BR>
F.prototype.constructor = F;<BR>
initializing = false;<BR>
}<BR>
// 覆盖父类的同名函数<BR>
for (var name in prop) {<BR>
if (prop.hasOwnProperty(name)) {<BR>
F.prototype[name] = prop[name];<BR>
}<BR>
}<BR>
return F;<BR>
};<BR>
Salin selepas log masuk
使用jClass函数来创建类和继承类的方法:
 var Person = jClass({<BR>
init: function(name) {<BR>
this.name = name;<BR>
},<BR>
getName: function() {<BR>
return this.name;<BR>
}<BR>
});<BR>
var Employee = jClass(Person, {<BR>
init: function(name, employeeID) {<BR>
this.name = name;<BR>
this.employeeID = employeeID;<BR>
},<BR>
getEmployeeID: function() {<BR>
return this.employeeID;<BR>
}<BR>
});<br><br>
var zhang = new Employee("ZhangSan", "1234");<BR>
console.log(zhang.getName()); // "ZhangSan"<BR>
Salin selepas log masuk
OK,现在创建类和实例化类的方式看起来优雅多了。 但是这里面还存在明显的瑕疵,Employee的初始化函数init无法调用父类的同名方法。

如何调用父类的同名方法?

我们可以通过为实例化对象提供一个base的属性,来指向父类(构造函数)的原型,如下:

 // 当前是否处于创建类的阶段<BR>
var initializing = false;<BR>
function jClass(baseClass, prop) {<BR>
// 只接受一个参数的情况 - jClass(prop)<BR>
if (typeof (baseClass) === "object") {<BR>
prop = baseClass;<BR>
baseClass = null;<BR>
}<BR>
// 本次调用所创建的类(构造函数)<BR>
function F() {<BR>
// 如果当前处于实例化类的阶段,则调用init原型函数<BR>
if (!initializing) {<BR>
// 如果父类存在,则实例对象的base指向父类的原型<BR>
// 这就提供了在实例对象中调用父类方法的途径<BR>
if (baseClass) {<BR>
this.base = baseClass.prototype;<BR>
}<BR>
this.init.apply(this, arguments);<BR>
}<BR>
}<BR>
// 如果此类需要从其它类扩展<BR>
if (baseClass) {<BR>
initializing = true;<BR>
F.prototype = new baseClass();<BR>
F.prototype.constructor = F;<BR>
initializing = false;<BR>
}<BR>
// 覆盖父类的同名函数<BR>
for (var name in prop) {<BR>
if (prop.hasOwnProperty(name)) {<BR>
F.prototype[name] = prop[name];<BR>
}<BR>
}<BR>
return F;<BR>
};<BR>
Salin selepas log masuk
调用方式:
 var Person = jClass({<BR>
init: function(name) {<BR>
this.name = name;<BR>
},<BR>
getName: function() {<BR>
return this.name;<BR>
}<BR>
});<BR>
var Employee = jClass(Person, {<BR>
init: function(name, employeeID) {<BR>
// 调用父类的原型函数init,注意使用apply函数修改init的this指向<BR>
this.base.init.apply(this, [name]);<BR>
this.employeeID = employeeID;<BR>
},<BR>
getEmployeeID: function() {<BR>
return this.employeeID;<BR>
},<BR>
getName: function() {<BR>
// 调用父类的原型函数getName<BR>
return "Employee name: " + this.base.getName.apply(this);<BR>
}<BR>
});<br><br>
var zhang = new Employee("ZhangSan", "1234");<BR>
console.log(zhang.getName()); // "Employee name: ZhangSan"<BR>
Salin selepas log masuk

目前为止,我们已经修正了在第一章手工实现继承的种种弊端。 通过我们自定义的jClass函数来创建类和子类,通过原型方法init初始化数据, 通过实例属性base来调用父类的原型函数。

唯一的缺憾是调用父类的代码太长,并且不好理解, 如果能够按照如下的方式调用岂不是更妙:

 var Employee = jClass(Person, {<BR>
init: function(name, employeeID) {<BR>
// 如果能够这样调用,就再好不过了<BR>
this.base(name);<BR>
this.employeeID = employeeID;<BR>
}<BR>
});<BR>
Salin selepas log masuk

优化jClass函数

 // 当前是否处于创建类的阶段<BR>
var initializing = false;<BR>
function jClass(baseClass, prop) {<BR>
// 只接受一个参数的情况 - jClass(prop)<BR>
if (typeof (baseClass) === "object") {<BR>
prop = baseClass;<BR>
baseClass = null;<BR>
}<BR>
// 本次调用所创建的类(构造函数)<BR>
function F() {<BR>
// 如果当前处于实例化类的阶段,则调用init原型函数<BR>
if (!initializing) {<BR>
// 如果父类存在,则实例对象的baseprototype指向父类的原型<BR>
// 这就提供了在实例对象中调用父类方法的途径<BR>
if (baseClass) {<BR>
this.baseprototype = baseClass.prototype;<BR>
}<BR>
this.init.apply(this, arguments);<BR>
}<BR>
}<BR>
// 如果此类需要从其它类扩展<BR>
if (baseClass) {<BR>
initializing = true;<BR>
F.prototype = new baseClass();<BR>
F.prototype.constructor = F;<BR>
initializing = false;<BR>
}<BR>
// 覆盖父类的同名函数<BR>
for (var name in prop) {<BR>
if (prop.hasOwnProperty(name)) {<BR>
// 如果此类继承自父类baseClass并且父类原型中存在同名函数name<BR>
if (baseClass &&<BR>
typeof (prop[name]) === "function" &&<BR>
typeof (F.prototype[name]) === "function") {<br><br>
// 重定义函数name - <BR>
// 首先在函数上下文设置this.base指向父类原型中的同名函数<BR>
// 然后调用函数prop[name],返回函数结果<br><br>
// 注意:这里的自执行函数创建了一个上下文,这个上下文返回另一个函数,<BR>
// 此函数中可以应用此上下文中的变量,这就是闭包(Closure)。<BR>
// 这是JavaScript框架开发中常用的技巧。<BR>
F.prototype[name] = (function(name, fn) {<BR>
return function() {<BR>
this.base = baseClass.prototype[name];<BR>
return fn.apply(this, arguments);<BR>
};<BR>
})(name, prop[name]);<br><br>
} else {<BR>
F.prototype[name] = prop[name];<BR>
}<BR>
}<BR>
}<BR>
return F;<BR>
};<BR>
Salin selepas log masuk
此时,创建类与子类以及调用方式都显得非常优雅,请看:
 var Person = jClass({<BR>
init: function(name) {<BR>
this.name = name;<BR>
},<BR>
getName: function() {<BR>
return this.name;<BR>
}<BR>
});<BR>
var Employee = jClass(Person, {<BR>
init: function(name, employeeID) {<BR>
this.base(name);<BR>
this.employeeID = employeeID;<BR>
},<BR>
getEmployeeID: function() {<BR>
return this.employeeID;<BR>
},<BR>
getName: function() {<BR>
return "Employee name: " + this.base();<BR>
}<BR>
});<br><br>
var zhang = new Employee("ZhangSan", "1234");<BR>
console.log(zhang.getName()); // "Employee name: ZhangSan"<BR>
Salin selepas log masuk

 

至此,我们已经创建了一个完善的函数jClass, 帮助我们在JavaScript中以比较优雅的方式实现类和继承。

在以后的章节中,我们会陆续分析网上一些比较流行的JavaScript类和继承的实现。 不过万变不离其宗,那些实现也无非把我们这章中提到的概念颠来簸去的“炒作”, 为的就是一种更优雅的调用方式。

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Penjelasan terperinci tentang warisan fungsi C++: Bagaimana untuk menggunakan 'penunjuk kelas asas' dan 'penunjuk kelas terbitan' dalam warisan? Penjelasan terperinci tentang warisan fungsi C++: Bagaimana untuk menggunakan 'penunjuk kelas asas' dan 'penunjuk kelas terbitan' dalam warisan? May 01, 2024 pm 10:27 PM

Dalam pewarisan fungsi, gunakan "penunjuk kelas asas" dan "penunjuk kelas terbitan" untuk memahami mekanisme pewarisan: apabila penuding kelas asas menghala ke objek kelas terbitan, transformasi ke atas dilakukan dan hanya ahli kelas asas diakses. Apabila penuding kelas terbitan menghala ke objek kelas asas, hantaran ke bawah dilakukan (tidak selamat) dan mesti digunakan dengan berhati-hati.

Bagaimanakah warisan dan polimorfisme mempengaruhi gandingan kelas dalam C++? Bagaimanakah warisan dan polimorfisme mempengaruhi gandingan kelas dalam C++? Jun 05, 2024 pm 02:33 PM

Pewarisan dan polimorfisme mempengaruhi gandingan kelas: Pewarisan meningkatkan gandingan kerana kelas terbitan bergantung pada kelas asas. Polimorfisme mengurangkan gandingan kerana objek boleh bertindak balas kepada mesej secara konsisten melalui fungsi maya dan penunjuk kelas asas. Amalan terbaik termasuk menggunakan warisan dengan berhati-hati, menentukan antara muka awam, mengelakkan penambahan ahli data pada kelas asas dan menyahgandingkan kelas melalui suntikan kebergantungan. Contoh praktikal yang menunjukkan cara menggunakan polimorfisme dan suntikan pergantungan untuk mengurangkan gandingan dalam aplikasi akaun bank.

Tutorial JavaScript Mudah: Cara Mendapatkan Kod Status HTTP Tutorial JavaScript Mudah: Cara Mendapatkan Kod Status HTTP Jan 05, 2024 pm 06:08 PM

Tutorial JavaScript: Bagaimana untuk mendapatkan kod status HTTP, contoh kod khusus diperlukan: Dalam pembangunan web, interaksi data dengan pelayan sering terlibat. Apabila berkomunikasi dengan pelayan, kami selalunya perlu mendapatkan kod status HTTP yang dikembalikan untuk menentukan sama ada operasi itu berjaya dan melaksanakan pemprosesan yang sepadan berdasarkan kod status yang berbeza. Artikel ini akan mengajar anda cara menggunakan JavaScript untuk mendapatkan kod status HTTP dan menyediakan beberapa contoh kod praktikal. Menggunakan XMLHttpRequest

Penjelasan terperinci tentang warisan fungsi C++: Bagaimana untuk menyahpepijat ralat dalam warisan? Penjelasan terperinci tentang warisan fungsi C++: Bagaimana untuk menyahpepijat ralat dalam warisan? May 02, 2024 am 09:54 AM

Petua penyahpepijatan ralat warisan: Pastikan perhubungan warisan yang betul. Gunakan penyahpepijat untuk melangkah melalui kod dan memeriksa nilai pembolehubah. Pastikan anda menggunakan pengubah suai maya dengan betul. Periksa masalah berlian warisan yang disebabkan oleh warisan tersembunyi. Semak fungsi maya tulen yang tidak dilaksanakan dalam kelas abstrak.

Bagaimana untuk mendapatkan kod status HTTP dalam JavaScript dengan cara yang mudah Bagaimana untuk mendapatkan kod status HTTP dalam JavaScript dengan cara yang mudah Jan 05, 2024 pm 01:37 PM

Pengenalan kepada kaedah mendapatkan kod status HTTP dalam JavaScript: Dalam pembangunan bahagian hadapan, kita selalunya perlu berurusan dengan interaksi dengan antara muka bahagian belakang, dan kod status HTTP adalah bahagian yang sangat penting daripadanya. Memahami dan mendapatkan kod status HTTP membantu kami mengendalikan data yang dikembalikan oleh antara muka dengan lebih baik. Artikel ini akan memperkenalkan cara menggunakan JavaScript untuk mendapatkan kod status HTTP dan memberikan contoh kod khusus. 1. Apakah kod status HTTP bermakna kod status HTTP apabila penyemak imbas memulakan permintaan kepada pelayan, perkhidmatan tersebut

Penjelasan terperinci tentang warisan fungsi C++: Bagaimana untuk memahami hubungan 'is-a' dan 'has-a' dalam warisan? Penjelasan terperinci tentang warisan fungsi C++: Bagaimana untuk memahami hubungan 'is-a' dan 'has-a' dalam warisan? May 02, 2024 am 08:18 AM

Penjelasan terperinci tentang pewarisan fungsi C++: Kuasai hubungan antara "is-a" dan "has-a" Apakah pewarisan fungsi? Warisan fungsi ialah teknik dalam C++ yang mengaitkan kaedah yang ditakrifkan dalam kelas terbitan dengan kaedah yang ditakrifkan dalam kelas asas. Ia membenarkan kelas terbitan untuk mengakses dan mengatasi kaedah kelas asas, dengan itu memanjangkan fungsi kelas asas. Perhubungan "is-a" dan "mempunyai-a" Dalam pewarisan fungsi, perhubungan "is-a" bermakna kelas terbitan ialah subjenis kelas asas, iaitu kelas terbitan "mewarisi" ciri dan tingkah laku kelas asas. Perhubungan "mempunyai-a" bermaksud bahawa kelas terbitan mengandungi rujukan atau penunjuk kepada objek kelas asas, iaitu, kelas terbitan "memiliki" objek kelas asas. SintaksBerikut ialah sintaks untuk cara melaksanakan pewarisan fungsi: classDerivedClass:pu

Warisan fungsi C++ menjelaskan: Bilakah warisan tidak boleh digunakan? Warisan fungsi C++ menjelaskan: Bilakah warisan tidak boleh digunakan? May 04, 2024 pm 12:18 PM

Warisan fungsi C++ tidak boleh digunakan dalam situasi berikut: Apabila kelas terbitan memerlukan pelaksanaan yang berbeza, fungsi baharu dengan pelaksanaan yang berbeza harus dicipta. Apabila kelas terbitan tidak memerlukan fungsi, ia harus diisytiharkan sebagai kelas kosong atau menggunakan fungsi ahli kelas asas peribadi yang tidak dilaksanakan untuk melumpuhkan warisan fungsi. Apabila fungsi tidak memerlukan pewarisan, mekanisme lain (seperti templat) harus digunakan untuk mencapai penggunaan semula kod.

'Pengenalan kepada Pengaturcaraan Berorientasikan Objek dalam PHP: Dari Konsep kepada Amalan' 'Pengenalan kepada Pengaturcaraan Berorientasikan Objek dalam PHP: Dari Konsep kepada Amalan' Feb 25, 2024 pm 09:04 PM

Apakah pengaturcaraan berorientasikan objek? Pengaturcaraan berorientasikan objek (OOP) ialah paradigma pengaturcaraan yang mengabstrak entiti dunia sebenar ke dalam kelas dan menggunakan objek untuk mewakili entiti ini. Kelas mentakrifkan sifat dan tingkah laku objek, dan objek memberi contoh kelas. Kelebihan utama OOP ialah ia menjadikan kod lebih mudah difahami, diselenggara dan digunakan semula. Konsep Asas OOP Konsep utama OOP termasuk kelas, objek, sifat dan kaedah. Kelas ialah pelan tindakan sesuatu objek, yang mentakrifkan sifat dan kelakuannya. Objek ialah contoh kelas dan mempunyai semua sifat dan tingkah laku kelas. Sifat ialah ciri-ciri objek yang boleh menyimpan data. Kaedah ialah fungsi objek yang boleh beroperasi pada data objek. Kelebihan OOP Kelebihan utama OOP termasuk: Kebolehgunaan semula: OOP boleh menjadikan kod lebih banyak

See all articles