JS中 类的声明有两种形式:

  

    // 类的声明
function Animal() {
this.name = 'name'
} // ES6中的class声明
class Animal2 {
constructor() {
this.name = name;
}
}

而实例化类,就是一个简单的 new 就完了

    // 实例化
console.log(new Animal(), new Animal2());

类的创建都是很简单的,主要是类的继承;

JS中类的继承是通过原型链来达到这样的目的;所以在面试过程中问到继承这样的问题,就是在考察你的原型链的掌握水平。

分别像每种继承中运用到的方式、每种继承的缺点和优点等。

先附上一张,我认为很好,但是需要一定了解的人才能看懂的图(原型链的指向)。

接下来开始上继承的代码和相关的一些见解:

首先ES5,也是面试重点考察的点

以下继承中折叠起来的都是红宝书的内容,非折叠的为简单实现继承。

原型链继承

将原型对象等于另一个类型的实列。原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也包含着一个指向另一个构造函数的指针。它们的关系层层递进,构成实列与原型的链条。

     // 原型链继承
function Person1() {
this.name = "person1";
this.arr = [1, 2, 3];
}
function Child1() {
this.type = 'child1';
}
// 重点就是这句,通过将子类的原型指针指向了超类的构造函数
Child1.prototype = new Person1();
console.log(new Child1().__proto__); //Person1 {name: "person1"}
var s1 = new Child1();
var s2 = new Child1();
s1.arr.push(4);
console.log(s1.arr, s2.arr); // (4) [1, 2, 3, 4] (4) [1, 2, 3, 4]
 function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}; function SubType(){
this.subproperty = false;
} SubType.prototype = new SuperType();
//继承了SuperType,将SubType原本指向原型的指针,改为指向SuperType的原型。 SubType.prototype.getSubValue = function(){
return this.subproperty;
};//添加一个新方法 var instance = new SubType();
alert(instance.getSuperValue());//true
alert(instance.subproperty);//true
alert(instance instanceof Object);//true
//所有函数的默认原型都是Object的实例,因此默认的原型都会包含一个内部指针,指向最高层:Object.orototype;

注意点:通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样会重写原型链。

原型链的缺点?优点?:一是:会实现为各个实例所共享;二是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。

借用构造函数: 主要用在解决原型链的问题上

就是用子类型构造函数的内部去调用超类型的构造函数。构造函数去继承构造函数,就不存在共享和无法传递参数的问题了。

     // 借用构造函数继承
function Person2() {
this.name = 'person2';
this.arr = [1, 2, 3];
}
function Child2() {
this.type = 'child2';
// 重点是这句: 用子类的构造函数的内部去调用超类的构造函数。构造函数去继承构造函数
Person2.call(this);
}
// 在超类上加一个方法
Person2.prototype.sayName = function () {
return 'person2'
}
// console.log(new Child2().sayName()); // error
console.log(new Child2());
var s3 = new Child2();
var s4 = new Child2();
s3.arr.push(4);
console.log(s3.arr, s4.arr); // (4) [1, 2, 3, 4] (3) [1, 2, 3]
             function SuperType(){
this.colors = ["red","blue","green"];
}
function SubType(){
SuperType.call(this);//继承了SuperType
//因为要在内部进行继承,且函数都有自己的作用环境,所以要用call()方法来调用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
//instance2是因为子类型的原型是构造函数,当instance1创建时,它会先创建一个新的副本,以供使用,所以不会影响

它的最大的优势: 就是可以传递参数

             function SubType(){
//继承了SuperType,同时还传递了参数
SuperType.call(this,"Nicholas");
//实例属性
this.age = 29;
}

问题是:

1.它通过超类的构造函数来显示继承. 所以超类的原型中的方法,它并没有继承到。即并没有真正的实现继承。

2.都是使用的构造函数来完成继承的,那么不可避免的,它也具有着与构造函数相同的问题: 每个方法都要在每个实例上重新创建一遍。即每次工作量都很大。

组合继承:  将 原型链 和 借用构造函数 的技术组合到一块。

原型链来实现对原型属性和方法的继承(实现属性和方法的共用),而通过借用构造函数来实现对实例属性的继承(实现私有的实例化)。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例有自己的属性。

     // 组合继承
function Person3() {
this.name = 'person3';
}
function Child3() {
this.type = 'child3';
// 第一次构造函数调用
Person3.call(this); // 构造函数继承
}
// 第二次构造函数调用
Child3.prototype = new Person3(); // 原型链继承
             function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};//添加新的方法
//借用构造函数模式,用来实现对实例属性的继承
function SubType(name,age){//继承SuperType
SuperType.call(this,name);
this.age = age;
} SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
//它们是原型链模式,不过多了一个实例指向。因为在经过借用构造函数继承后,它实际上指向了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();// var instance2 = new SubType("Greg",27);
alert(instance2.colors);//red,blue,green
instance2.sayName();//Greg
instance2.sayAge();//

组合继承的最大问题就是,要调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。

组合继承需要在继承后,重新引用一下constructor, 因为在propotype继承时,它的constructor也跟着改变了;但是这样的情况下,虽然实现了对象继承,但是超类的原型对象都变成了子类的原型对象,最终导致无法去区分超类的原型对象了。

组合继承优化版

优化版1: 修改原型链继承方式,作为继承超类原型来用,完善借用构造函数继承无法继承到超类原型的缺点。

     // 组合继承优化1
function Person4() {
this.name = 'person3';
}
function Child4() {
this.type = 'child3';
Person4.call(this); // 构造函数继承
}
// 将子类原型改成超类的原型
Child4.prototype = Person4.prototype;
var s5 = new Child4();
console.log(s5 instanceof Child4, s5 instanceof Person4);
console.log(s5.constructor);

现在引出了一个新的问题: 无法判断到底是 子类 还是 超类 进行的直接对象实例化。 这个问题会出现在 原型链继承、 组合继承 和 组合继承优化1 中。

根据原型链的原理,不难看出这样的问题是因为: 子类原型指向了超类的原型,从而导致子类的constructor也成了超类的constructor;

如果只是这样的话,是否重新对 子类进行constructor进行重新指向就好了呢?

优化版2:修改子类的constructor指向

利用Object.create() 去 创建中间对象从而将子类和超类区分开;

     // 组合继承优化2
function Person5() {
this.name = 'person3';
}
function Child5() {
this.type = 'child3';
Person5.call(this); // 构造函数继承
}
// 利用Object.create来作为中间链,将子类和超类区分开,且还保持这链接。
Child5.prototype = Object.create(Person5.prototype);
// 重新修改了constructor指向
Child5.prototype.constructor = Child5;
var s7 = new Child5();
console.log(s7 instanceof Child5, s7 instanceof Person5);
console.log(s7.constructor);

此继承已经是非常好的继承了,下面的继承可以不看了。

原型式继承: 

Object.create方法规范化了原型式继承;这个方法接受两个参数,一个用作新对象原型的对象和(可选)一个为新对象定义额外属性的对象。 即将 新对象 链到 新对象原型的对象的原型链上,以解决上面 组合继承 导致的

constructor改变问题。

            //创建一个对象,并将其传送到Object()函数中,然后函数就会返回一个新对象。
var Person = {
name : "Nicholas",
friends : ["Shelby","Court","Van"]
};
//这个新对象以Peron为原型,所以他的原型中就包含一个基本类型值属性和一个引用类值属性。这意味着Person.friends不久属于Person,而且也被person1和person2所共享。 var person1 = Object.create(Person);
//原型继承这种方式,就是要求有一个对象能作为另一个对象的基础,从而进行修改。
person1.name = "Greg";//定义的新name属性值,会“屏蔽”原本的name;
person1.friends.push("Rob"); var person2 = Object.create(Person);
person2.name = "Linda";//定义的新name属性值,会“屏蔽”原本的name;
person2.friends.push("Barbie"); alert(Person.friends);//Shelby,Court,Van,Rob,Barbie
alert(person1.friends);//Shelby,Court,Van,Rob,Barbie
alert(person2.friends);//Shelby,Court,Van,Rob,Barbie
//直接应用了friend的内容,从而导致相互影响。
alert(person1.name);//Greg
alert(person2.name);//Linda
//因为定义了一个name,在查找name时,会先在实例中查找名为name的属性,从而导致了原型中的name被屏蔽。

这里也引出了Object.create中继承的参数的内容并不是直接放到子类中,而是存在与子类的原型中; 所以会出现覆盖和共享属性的特征

寄生式继承: 

寄生式继承是与原型式继承紧密相关的一种思路; 即创建一个仅用域封装过程的函数, 该函数在内部以某种方式来增强对象, 最后就像真的是它做了所有工作一样返回对象;

             function createAnother(original){ //很像工厂模式
var clone = Object.create(original);//通过调用函数创建一个新对象
clone.sayHi = function(){ //通过某种方法来增强这个对象
alert("hi");
};
return clone; //返回该对象
}
var person = {
name : "Nichloas",
friends : ["Sheldy","Court", "Van"]
};
var anotherperson = createAnother(person);
person.friends.push("Linda");
alert(anotherperson.friends);//Sheldy,Court,Van,Linda
anotherperson.sayHi(); //hi

任何能够返回新对象的函数都适用于此模式;

问题: 使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点与构造函数类似。

寄生组合式继承: 

本质上就是使用寄生式继承来继承超类的原型. 然后再将结果指定给子类的原型;

         function inheritPrototype(subtype,supertype){
var midlle = Object.create(supertype.prototype);//创建对象
midlle.constructor = subtype;//增强对象
//为创建的副本添加constructor属性,从而弥补重写原型而失去的默认construcor属性。
subtype.prototype = midlle;//指定对象。
//将新创建的对象(即副本)赋值给子类型的原型。
} function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
} SuperType.prototype.sayName = function(){
alert(this.name);
}; //使用借用构造函数,来继承属性
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
} //调用原型链的混成形式,来完成继承。
inheritPrototype(SubType,SuperType); //在完成继承后的原型中添加新方法,不影响SuperType中的属性
SubType.prototype.sayAge = function(){
alert(this.age);
} var instance1 = new SubType("Nicholas",29);
instance1.colors.push("black"); var instance2 = new SubType("Greg",27); instance1.sayName();//Nicholas
instance1.sayAge();//
alert(instance1.colors);//red,blue,green,black
instance2.sayName();//Greg
instance2.sayAge();//
alert(instance2.colors);//red,blue,green

以上的ES5继承很全了。 面试时,主要说前四种即可;不要直接写出最佳答案,最好是由浅到深的让面试官了解你的基础知识是扎实的。

面试最重要的就是: 要让面试官看到你的知识深度  和 对新技术的掌握。

ES6的类继承, 都封装好了; extends 所以不会作为重点去问

JS面向对象的类 实例化与继承的更多相关文章

  1. node.js面向对象实现(二)继承

    http://blog.sina.com.cn/s/blog_b5a53f2e0101nrdi.html 继承是面向对象中非常重要的一个概念,那么在Node.js中如何实现继承呢? node.js在u ...

  2. python3 之 面向对象(类)、继承、派生和多态

    类提供了一种 组合数据和功能 的方法.创建一个新类意味着:创建一个新 类型  的对象,从而允许创建一个该类型的新 实例. 每个类的实例可以拥有: 保存自己状态的属性. 一个类的实例也可以有改变自己状态 ...

  3. 关于JS面向对象、设计模式、以及继承的问题总结

    1.对象:JS中万物皆对象,它是一个泛指 类:对象的具体的细分 (物以类聚,人与群分.具有相同属性和方法的实例的一个集合总称) 实例:某一个类别中具体的一个事物 对象是一个抽象的概念,类似于我们的自然 ...

  4. JS面向对象(3) -- Object类,静态属性,闭包,私有属性, call和apply的使用,继承的三种实现方法

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  5. JS面向对象组件 -- 继承的其他方式(类式继承、原型继承)

    继承的其他形式: •类式继承:利用构造函数(类)继承的方式 •原型继承:借助原型来实现对象继承对象   类 : JS是没有类的概念的 , 把JS中的构造函数看做的类 要做属性和方法继承的时候,要分开继 ...

  6. js面向对象--类式继承

    //待研究//类式继承 //js中模拟类式继承的3个函数 //简单的辅助函数,让你可以将新函数绑定到对象的 prototype 上 Function.prototype.method = functi ...

  7. JS面向对象(2) -- this的使用,对象之间的赋值,for...in语句,delete使用,成员方法,json对象的使用,prototype的使用,原型继承与原型链

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  8. JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  9. js面向对象设计之class继承

    EcmaScript 2015 (又称ES6)通过一些新的关键字,使类成为了JS中一个新的一等公民.但是目前为止,这些关于类的新关键字仅仅是建立在旧的原型系统上的语法糖,所以它们并没有带来任何的新特性 ...

随机推荐

  1. K8S CoreDNS部署失败,问题分析

    1. 查询k8s集群部署pod的基本情况 如下图,我们可知容器coredns和dnsutils都部署成功,但是由于域名解析的问题,导致coredns和dnsutils的容器不断重启(原因heath检查 ...

  2. python课程单元三编程题讲解(上)

    目录 1.快乐的数字 2.凯撒密码I 3.凯撒密码II 4.括号配对检测 A @     下面向大家介绍一下我在学习python课程的一些题目的解法,如果大家有什么更好的解法请私信我.这里只显示题目与 ...

  3. 在IE中点击转跳,并打开chorme浏览器继续浏览指定页面,IE自定义ocx控件开发

    因项目需要,需要开发一个功能:在IE中点击转跳,并打开chorme浏览器继续浏览指定页面. 分析需求后,参考了: https://www.cnblogs.com/ffjiang/p/7908025.h ...

  4. golang-Json编码解码

    目录 一. 类型映射 二. 输出控制 三. 类型编码 四. 类型解码 五. 输出重写 六. 扩展功能 七. Bson编码 在线工具:https://www.json.cn 一. 类型映射 golang ...

  5. 设计模式之(十一)代理模式(Proxy)

    软件开发行业有一个观点:任务问题都可以添加一个中间层来解决.代理模式也是这个思想下的产物. 首先看下代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.就是把类委托给另外一个类,用这个类来控 ...

  6. XSS相关Payload及Bypass的备忘录(上)

    翻译学习准备自用,同时分享给大家, 来自于: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20Injecti ...

  7. Excel单元格锁定及解锁

    Excel VBA 宏 学习使用: 一.工作表单元格的锁定: 1.选择需要锁定的单元格. 2.鼠标右键----设置单元格格式. 3.设置  “保护”--锁定 -- 确定. 4.回到表头,[审阅]--- ...

  8. MySQL 错误代码:2003 idea错误:ERROR DruidDataSource:1846 - create connection error

    idea项目一启动就报错: 20:01:13,047 ERROR DruidDataSource:1846 - create connection error com.mysql.jdbc.excep ...

  9. windows设置多个JDK环境

    1.查看jdk版本 java -version 2.查看JAVA_HOME和PATH的变量值 echo %JAVA_HOME% set path 3.临时修改环境变量JAVA_HOME和PATH的变量 ...

  10. metrics-server 安装问题解决

    参考:  https://www.qikqiak.com/post/install-metrics-server/   git clone https://github.com/kubernetes- ...