《JS高程第八章 第二部分》

291 阅读5分钟

《JS高程第八章 第二部分》

继承

继承是面向对象oop编程中讨论最多的。许多oop语言都支持两种继承方式,接口继承和实现继承。

8.3.1原型链**(重点)**

原型链.PNG

原型链1.PNG

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]