《JS高程第八章 第二部分》
继承
继承是面向对象oop编程中讨论最多的。许多oop语言都支持两种继承方式,接口继承和实现继承。
8.3.1原型链**(重点)**
JS高程的解释 每一个构造函数(star构造函数)都有一个原型对象(star原型对象),原型有一个函数(constructor)指回构造函数,而实例有一个内部指针指向原型(实例.proto)。如果原型是另一个类型的实例,那就意味着这个原型有一个内部指针(原型.proto)指向另一个原型,相应的另一个原型也有一个指针(原型.constructor)指向另一个构造函数。这样就在实例和原型之间构造了一条原型链
个人更推荐MDN文档的解释
在谈到继承的时候,JavaScript只有一种结构,那就是对象。每一个对象的实例都有一个私有属性(proto)指向它的构造函数的原型(prototype).该原型对象也有一个自己的原型对象(proto),层层向上直到一个对象的原型对象为null。根据定义,null没有原型,并作为原型链的中最后一个环节。换言之_proto_指到null就没了(这不就是链表吗?proto_作为指向下一个节点的指针,然后每个节点(prototype)有一个constructor函数指向构造函数,和一个指向下一个节点的_proto,同时构造函数constructor通过prototype指回节点)
MDN解释用代码解释
let person=function(name,age){
this.name=name;
person.age=age;
}
let jackMa=new person("JackMa",55);
if(jackMa._proto_===person.prototype){
console.log("True,jackMa._proto_ === person.prototype");
}else{
console.log("False,jack._proto_!==person.prototype");
}
/*重点:你会神奇的发现最后返回了true,*/
//大致的结构
//1、只有函数有prototype,实例没有
jack{
a:1,
_proto_:person.prototype{
b:2,
_proto_:Object.prototype{
c:3,
_proto_:null;
}
}
}
console.log(jack.c);//返回3
//找jack中的c找不到就会往person.prototype中找,再找不到往Object.prototype中找,这就是原型链继承
梳理一下 1、首先 jackMa.proto ===> person.prototype|person.prototype._proto ===>Object.prototype |Object.prototype.proto ==> null
2、prototype节点会自己玩自己,也就是person.prototype.constrctor==>person,person.prototype==>person.prototype
3、Function和Object两个人互玩,见图二
2、原型与继承
原型与实例的关系可以通过两种方式来确定。第一种方式是使用instanceof操作符,如果一个实例的原型链中出现过相应的构造函数,则instancof返回true,否则返回false
MDN 1、继承属性
let f=function(){
this.a=1;
this.b=2;
}
let o=new f();
f.prototype.b=3;
f.prototype.c=4;
/*不要直接上面这样定义属性,会打破原型链*/
console.log(f); //返回 {b:3,c:4}
console.log(o); //返回 f.prototype {a:1,b:2}
console.log(o._proto_); //返回 {b:3,c:4}
console.log(o._proto_._proto_);//返回 Object.prototype
console.log(o._proto_.proto_._proto_); //返回null
console.log(f.b); //返回undefined
console.log(f.c); //返回4
MDN 2、继承方法
在JavaScript中,并没有其他语言那样的方法,任何函数都可以添加到对象上作为对象的属性。函数的继承与其他属性的继承没有差别,包括“属性遮蔽”相当于方法重写
let o={
a:2,
m(){
return this.a+1;
}
};
console.log(o.m()); //返回3
let p=Object.create(o); //ES6之前的写法
let p1=new o(); //ES6的写法
p.a=4;
console.log(p.m()); //返回5
MDN 3、在Javascript中使用原型
function doSth(){}
console.log(doSth.prototype); //这里返回对象{constructor:f},constructor构造函数指向的是f doSth()
//和声明函数的方式无关
//JavaScript中的函数永远有一个默认原型属性
var doSth=function(){};
console.log(doSth); //这里返回对象{constructor:f},constuctor指向的是f()
//
MDN 4、给原型对象添加属性
function doSomthing(){}
doSomthing.prototype.foo="bar";
doSomthing.prototype.tel=110;
console.log(doSomthing.prototype); //这里返回对象doSth里面有两个属性 1、foo和2、tel
MDN 5、通过new来创建实例
function doSth(){
}
doSth.prototype.thing="加班";
let doSthInstacne=new doSth();
doSthInstance.prototype.name="JackMa";
console.log(doSthInstance);//这里返回对象
/*
doSth{
name:"JackMa",
_proto_:{
thing:"加班"
constructor:f doSth(){}
_proto_:Object
}
}
*/
类Class
8.4.1 类定义,类实际上是语法糖
//类声明
class person{}
//类表达式
let person=class{};
MDN: class 定义创建一个基于原型继承的具有给定名称新类
class Polygon{
constructor(height,width){
this.area=height*width; //这个area 实际上是属性
this.calcArea=function(){
return height*width;
}
}
}
let p=new Ploygon(100,100);
console.log(p.area); //这里返回10000
console.log(p.calcArea()); //这里返回10000
1、把类当做特殊函数
class Person{};
console.log(typeof(Person)); //返回Function
console.log(Person._proto_===Function.prototype); //返回true
console.log(Person._proto_.constructor===Function); //返回true
记得上节原型链中的第二张图的Fuction吗,自己玩自己,比Object还会玩,而这个Person类实际上就是图中animal
还有更会玩的
class person{};
let p=new person();
console.log(p instanceof person); //返回 true
console.log(person instanceof Function) // 返回true
console.log(person instanceof Object); //返回true
/****function和obje互玩/
console.log(Function instanceof Object);//返回true
console.log(Object instanceof Function); //返回true
/**同理**/
console.log(Array instanceof Function); //返回什么?
console.log(Array instanceof Object); //返回什么?
2、类可以进行实例化,函数也可以进行实例化
let sum=function(a,b){
return a+b;
}
let s1=new sum();
console.log(s1._proto_.constructor(10,5)); //返回15
类继承。extends,背后原理依然还是原型链
1、extends关键字用于类继承或者类表达式中,创建一个类是另一个类的子类
class ploygon{
constructor(width,height){
this.width=width;
this.height=height;
this.name="多边形";
}
}
//square 继承polygon
class square extends ploygon{
constructor(length){
super(length,length);
this.name="正方形";
}
set area(){
return this.width*this.height;
}
get area(value){
this.area=value
}
}
let s1=new square(5);
console.log(s1.area); //返回25
2、抽象基类,阻止实例化
通过new.target来实现
class Polygon{
constructor(){
if(new.target==Polygon){
console.log("Ploygon是基类不能被实例化!");
throw new Error('Polygon 是基类不能进行实例化');
}
}
}
let p=new Polygon();//这里抛出异常 Error: polygon 是基类不能进行实例化
at new Polygon
3、继承内置类型
ES6类为继承内置引用类型提供了顺畅的机制,开发者方便扩展内置类型
class superArray extends Array{
shuffle(){
for(let i=this.length-1;i>0;i--){
let j=Math.floor(Math.random()*(i+1));
[this[i],this[j]]=[this[j],this[i]];
//上面那段代码拆开
let tmp=this[i];
this[i]=this[j];
this[j]=tmp;
}
}
}
supperArray s1=new superArray();
console.log(s1 instanceof superArray); //返回true
s1.shuffle();
console.log(s1); //这里一次返回 [1, 5, 3, 9, 7, 4, 2, 8, 6]
s1.shuffle();
console.log(s1); //这里一次返回[5, 9, 4, 6, 7, 8, 2, 1, 3]