JS Pro-深入面向对象的程序设计之继承的详解_javascript技巧
原型链(prototype chaining):
利用原型来继承属性和方法。回顾一下构造函数(constructor),原型对象(prototype)和实例(instance)的关系。每一个构造函数都有一个prototype属性,该属性指向一个prototype对象;prototype对象也有constructor属性,指向该函数;而实例也有一个内部指针(__proto__)指向这个prototype对象。如果这个prototype对象是另外一个对象的实例会是怎样的呢?这样该prototype对象就包含一个指向另一个类型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。
JS的继承很简单,就是把子类的prototype设为父类的一个(实例化)对象
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
最终的结果:instance的__proto__指向SubType.prototype对象,而SubType.prototype对象的__proto__属性又指向SuperType.prototype对象。getSuperValue()是一个方法,所以仍然存在于原型中,而property是一个实例属性,所以现在存在于SubType.prototype这个实例中。 instance.constructor现在指向的是SuperType,这是由于SubType.prototype指向SuperType.prototype,而SuperType.prototype的constructor属性指向SuperType函数,所以instance.constructor指向SuperType。
默认情况下,所有引用类型都继承Object。这是因为所有函数的原型对象,默认都是Object的一个实例,所以内部prototype(__proto__)指向Object.Prototype。
原型和实例的关系:可以使用2种方法来确定原型与实例之间的关系。
- instancef操作符:使用该操作符来测试实例与原型链中出现过的构造函数,都会返回true
alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true
- isPrototypeOf()方法:只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true
给子类添加方法的注意点:我们有的时候会给子类添加方法,或者是重写父类的某些方法。这个时候就要注意,这些方法必须在继承后再定义。以下的例子里,SubType在继承SuperType后,我们给它添加了新的方法getSubValue(),而且重写了getSuperValue()方法。对于后者,只有SubType的实例才会使用重写的方法,SuperType的实例还是会使用原有的getSuperValue()方法。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
t his.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//new method
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
//override existing method
SubType.prototype.getSuperValue = function (){
return false;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false
另外一个需要注意的是,通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链。如下面的代码,在SubType继承SuperType以后,使用对象字面量给原型添加方法,但这样做,会重写SubType原型,重写后的SubType.prototype包含的是一个Object的实例,从而也切断了与SuperType的关系。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//try to add new methods - this nullifies the previous line
SubType.prototype = {
getSubValue : function (){
return this.subproperty;
},
someOtherMethod : function (){
return false;
}
};
var instance = new SubType();
alert(instance.getSuperValue()); //error!
原型链的问题:和原型一样,当使用引用类型值的时候,原型链就会出问题了。回顾一下之前的内容,包含一个引用类型值的原型属性会被所有实例共享,这就是为什么我们要把引用类型值在构造函数中定义,而不是在原型中定义。在通过原型链实现继承时,原型实际上会变成另一个类型的实例,于是,原先的实例属性也顺利成章的变成现在的原型属性了。
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){
}
//inherit from SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push(“black”);
alert(instance1.colors); //”red,blue,green,black”
var instance2 = new SubType();
alert(instance2.colors); //”red,blue,green,black”
在SuperType构造函数中,我们定义了一个colors数组,每一个SuperType实例都会拥有它自己的这个colors数组。但是当SubType使用原型链继承SuperType以后,SubType.prototype变成SuperType的一个实例,因此它拥有自己的colors属性,也就是说SubType.prototype.colors属性。所以,当创建SubType实例的时候,所有实例都共享这一属性了。如上面的代码所示。
第二个问题就是:在创建子类的实例时,不能向超类的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类的构造函数传递参数。由于这些问题,我们不会单独使用原型链。
--------------------------------------------------------------------------------
借用构造函数(Contructor stealing):
为了解决上述问题,开发人员发明了一种叫做借用构造函数的技术。这种技术的思路就是:在子类型构造函数的内部调用超类型构造函数。(函数,只不过是在特定环境中执行代码的对象?)我们可以通过使用apply()或call()方法在新创建的对象上执行构造函数。
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){
//inherit from SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push(“black”);
alert(instance1.colors); //”red,blue,green,black”
var instance2 = new SubType();
alert(instance2.colors); //”red,blue,green”
我们在SubType里使用call()方法调用SuperType的构造函数,实际上就是在新的SubType对象上执行SuperType()函数中定义的所有对象初始化代码。结果就是每个SubType实例都具有自己的colors属性的副本。
传递参数:使用借用构造函数方法的一个很大的好处在于就是,我们可以从子类的构造函数传递参数到父类的构造函数中。
function SuperType(name){
this.name = name;
}
function SubType(){
//inherit from SuperType passing in an argument
SuperType.call(this, “Nicholas”);
//instance property
this.age = 29;
}
var instance = new SubType();
alert(instance.name); //”Nicholas”;
alert(instance.age); //29
新的SuperType构造函数新增了一个参数name,我们在call SuperType的同时,往SuperType传递参数"Nicholas"。为了不让超类型的构造函数重写子类型的属性,可以在调用超类型构造函数后再定义子类的属性。
借用构造函数的问题:方法都在构造函数中定义,无法复用。而且在超类型的原型中定义的方法,对子类型而言是不可见的。结果所有类型都只能使用构造函数模式。
--------------------------------------------------------------------------------
组合继承:
结合原型链及借用构造函数各自的优点的一种继承模式。使用原型链继承原型属性及方法,使用借用构造函数来继承实例属性。如下面例子,我们使用call()方法调用SuperType的构造函数(每个SubType实例都拥有自己的name和colors属性,以及SubType的age属性);然后再把SuperType实例赋值给SubType的原型,使其继承SuperType的sayName()方法(每个实例都共用这个方法)。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
//inherit properties
SuperType.call(this, name);
this.age = age;
}
//inherit methods
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
原型式继承(Prototypal Inheritance):
function object(o){
function F(){}
F.prototype = o;
return new F();
}
--------------------------------------------------------------------------------
寄生式继承(Parasitic Inheritance):
缺点同构造函数

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

在函数继承中,使用“基类指针”和“派生类指针”来理解继承机制:基类指针指向派生类对象时,执行向上转型,只访问基类成员。派生类指针指向基类对象时,执行向下转型(不安全),必须谨慎使用。

人脸检测识别技术已经是一个比较成熟且应用广泛的技术。而目前最为广泛的互联网应用语言非JS莫属,在Web前端实现人脸检测识别相比后端的人脸识别有优势也有弱势。优势包括减少网络交互、实时识别,大大缩短了用户等待时间,提高了用户体验;弱势是:受到模型大小限制,其中准确率也有限。如何在web端使用js实现人脸检测呢?为了实现Web端人脸识别,需要熟悉相关的编程语言和技术,如JavaScript、HTML、CSS、WebRTC等。同时还需要掌握相关的计算机视觉和人工智能技术。值得注意的是,由于Web端的计

继承和多态性会影响类的耦合度:继承会增加耦合度,因为派生类依赖于基类。多态性可以降低耦合度,因为对象可以通过虚函数和基类指针以一致的方式响应消息。最佳实践包括谨慎使用继承、定义公共接口、避免向基类添加数据成员,以及通过依赖注入解耦类。实战案例展示了如何使用多态性和依赖注入降低银行账户应用程序中的耦合度。

继承错误调试技巧:确保正确的继承关系。使用调试器逐步执行代码,检查变量值。确保正确使用virtual修饰符。检查隐藏的继承带来的菱形继承问题。检查抽象类中未实现的纯虚函数。

Go语言支持面向对象编程,通过类型定义和方法关联实现。它不支持传统继承,而是通过组合实现。接口提供了类型间的一致性,允许定义抽象方法。实战案例展示了如何使用OOP管理客户信息,包括创建、获取、更新和删除客户操作。

PHP中OOP最佳实践包括命名约定、接口与抽象类、继承与多态、依赖注入。实战案例包括:使用仓库模式管理数据,使用策略模式实现排序。

js和vue的关系:1、JS作为Web开发基石;2、Vue.js作为前端框架的崛起;3、JS与Vue的互补关系;4、JS与Vue的实践应用。

Go语言支持面向对象编程,通过struct定义对象,使用指针接收器定义方法,并通过接口实现多态。面向对象特性在Go语言中提供了代码重用、可维护性和封装,但也存在缺乏传统类和继承的概念以及方法签名强制类型转换的局限性。
