如果说原型链是一条龙的躯干,那么继承以及通过构造函数创建对象的实现就是他舞出的繁花似锦

目录

 

一条链串起了,整个的大唐繁华

条条道路创对象,唯有构造函数是大道

 原型链他不就是个链嘛,有很多节点的链

原理测试是扯淡,还是测试来的快

继承就是我们在原型链上的之上的繁华似锦

prototype透视方法,constructor修正父辈,原型链双向继承

构造函数位继承,父辈构造函数我也用


一条链串起了,整个的大唐繁华

Java中所有的对象默认都继承与Object,他是通过extends来实现的,他通过层层封装继承,可以是得我们的类较少的代码通过继承来实现强大的功能,每一个子类都是可以站在巨人(父类)的肩膀上来走的更远,飞的更高。在我们JS的使用中我们一定也会有这样的感受,那就是我们的对象可以使用一些我们从来没有定义过的一些方法,eg:toString()。那么我们在Js的环境中我们也可以先站在巨人的肩膀上再去跑的更远飞的更高!

条条道路创对象,唯有构造函数是大道

在我们JS中创建对象可以直接通过new Object(),之后再通过 className.property=value的形式逐步的向我们的对象中添加属性。也可以直接通过字面量的形式来创建我们的对象,但是如果要创建很多对象的时候这样的创建形式将会造成很大的代码冗余。这时候我们可以考虑通过工厂函数的的方法类创建我们对象,函数根据参数会返回一个拥有具体信息的对象。

<script>
			function createPersion(name,sex,age){
				var temp = new Object();
				temp.name = name;
				temp.sex = sex;
				temp.age = age;
                                temp.fun = {
                                     console.log("LiuMenglei");
                                }
				return temp;
			}
			var persion1 = createPersion("persion1","男",19);
			console.log(persion1);//{"age":19,"name":"persion1","sex":"男"}
</script>

这时候虽然我们代码冗余的问题解决了但是我们内存问题并没有解决,如果我们创建的对象中包含函数,那么我们每通过工厂函数创建一个对象就势必又会产生一个函数,但是这个函数(fun)又是相同的,那么我们是否可以通过让由一个工厂函数创建的独享,可以共享一个函数对象呢?我们可以构造函数来创建我们的对象,他就是使用了原型链的原理。

//定义构造函数
function Fn(name,age){
    this.name = name;
    this.age = age;			
}
//给原型对象添加方法
Fn.prototype.test = function(){
	console.log("test()")
}
//创建实例对象
var fn = new Fn("Li",13)
var fn2 = new Fn("Liu",12)
//通过对象访问原型链中的方法
fn.test();

 原型链他不就是个链嘛,有很多节点的链

在这里我们将test()方法作为属性放置在了Fn.prototype上,我们创建的fn,fn2都共用了一个test()函数对象。他们之所以可以共用的原因就是,我们每个函数对象都有一个prototype属性,我们将这个属性称为显示原型。这个属性是在我们方法定义的时候后已经产生了的,也就是说他的产生时发生在全局上下文准备的时候在我们JS代码执行的时候有一个变量提升和一个函数提升,在这里我们可以简单的吧这个过程理解为上下文准备)。这个prototype会指向一个空Object对象,prototype指向的对象我们称之为显式隐型对象,每个函数都有,只不过是当他作为普通函数调用的时候他并没有什么用。当他作为构造函数调用(构造函数与普通函数调用的区别是构造函数我们是通过new关键字调用的)的时候,他产生的对象中的__proto__属性,指向他的prototype属性。对象的__proto__属性我们称之隐式隐型。

之所以我们的fn,fn2都可以访问到test()函数是因为,他们都是由Fn产生的,他们的隐式原型都是创建他们的构造函数Fn的显式原型。

当他们在自身找不到所要的方法的时候他们就会沿着隐式原型去找,如果他的隐式原型对象中有的话,那么他也是可以使用的。那么就像这种toString()这类我们那个类都可以使用的方法又是如何来的呢?这是因为我们的隐式原型并不只是在他构造函数中就已经结束了,他是会一直衍伸的直到我们的Object对象的显示原型为止。

 

每一个function都有一个prototype——显示原型、每一个实例对象都有一个 _ _ proto _ _ ——隐式原型 , _ _ proto _ _ 指向的是创建他的构造函数的显示原型,但是这里有连个比较特殊的就是Function的隐式原型对象是指向他的显式原型对象,Obect的显式原型对象的隐式原型是指向null的。

我们该牢记原型对象,他也是对象,既然是对象那么他就应该有隐式原型,也正是这样串起了我们的原型链,对象的方法可以从自身起步逐层在原型链上寻找属性,如果属性找到了他就可以使用如果没有找到的话就会显示undefined,如果我们把这个属性当做方法调用但是这个属性在原型链上又没有的话,那么程序在控制台是会报错的。

 

在原型上添加的方法我们的实例对象是可以引用的,现在我们也通过上面的例子知道了我们的对象上游一个__proto__的属性指向他类型构造函数的prototype属性,但是在我们的prototype属性所指显式隐型对象的内部实际上还有一个constructor指向我们的构造函数,对象原型之间通过prototype以及constructor实现相互引用。在这这里个人认为对象的创建是和原型对象有更大的相关度。

function contructorFunc1(name,age){
	name = name,
	age = age
}
console.log(contructorFunc1.prototype)

上面我们说了显示原型是在函数定义的时候添加的,我们的隐式原型是存在于我们的对象中,所以他是在对象产生的时候添加的。在上面我们是通过对象的创建引出的原型以及原型链,他就有了一些继承的意味,在我们JS中我们把原型链的作用定义于是用来查找属性的,那么如果添加呢?在Java中有覆盖重写的概念,在我们的JS 中同样也有这样的理念,如果我们要向对象1中添加一个方法,这个方法在他自身是不存在的但是在创建他的构造函数的原型对象中是存在的,这时候我们这个对象并不关心我们这个原型链上是不是有这个方法,而只是关注自身有没有。如果自身没有的话就添加上,有的话就是覆盖。当他访问的时候是先从自身找起的,如果自身有的话他就会使用自身的,如果自身没有他才会顺着原型链继续向上寻找。

原理测试是扯淡,还是测试来的快

上面尽可能的通过例子以及画图说明我们的原型链但是应该还是懂得人董,不懂的人继续迷糊。一个最好的办法就是通过自己的代码进行一步步的验证,下面给出一个自己的入门验证Demo,大家可以参考:

		<script>
			function PersionClass(){
				
			}
			
			function DogClass(){
				
			}
			
			var persionClass1 = new PersionClass();
			var persionClass2 = new PersionClass();
			console.log(persionClass1.__proto__);
			console.log(PersionClass.prototype);
			console.log("变量与所属构造函数:"+(persionClass1.__proto__==PersionClass.prototype));
			console.log("不同构造函数:"+(DogClass.prototype==PersionClass.prototype));
			console.log("同一构造函数下不同变量:"+(persionClass1.__proto__==persionClass2.__proto__))
			
			PersionClass.prototype.name = "构造函数原型";
			persionClass1.name = "persionClass1";
			PersionClass.prototype.address = "云端";
			console.log("原型链属性测试:"+persionClass1.name+"  "+persionClass1.address);
			console.log("属性归属测试:"+("name" in persionClass1)+"  "+("address" in persionClass1));
			
			console.log("自己属性测试:"+(persionClass1.hasOwnProperty("address")));
			
			console.log("Object原型链测试:"+(PersionClass.prototype.hasOwnProperty("hasOwnProperty")));
			console.log("Object原型链测试:"+(PersionClass.prototype.__proto__.hasOwnProperty("hasOwnProperty")));
				
			Object.prototype.topName = "ObjectName";
			console.log("顶级链测试: "+persionClass1.topName);
		</script>

hasOwnProperty()方法是用来检测对象自身是否含有这个属性,如果是通过原型链得到的,那么就不算是属于自身的。

继承就是我们在原型链上的之上的繁华似锦

在上面的介绍中你或许嗅到了意思继承的气息,但是他毕竟还不是,他所继承的是他构造函数的原型对象中的方法,如果对上面的内容了解的话我会知道,我们这个继承之所以可以实现,主要是因为原型链的功劳。如果我现在想要实现两个不想管方法类型之间的继承那就肯定是通过改变原型链的路径。

prototype透视方法,constructor修正父辈,原型链双向继承

		<script>
			function Supper(){
				this.supProp = "Supper property"
			}
			Supper.prototype.showSupperProperty = function(){
				console.log(this.supProp);
			}
			
			function Sub(){
				this.subProp = "Sub property";
			}
			//如果想要实现继承,子类型的原型必须为父类型的一个实例
			Sub.prototype = new Supper();
			//将子类型prototype的constructor指向正确的位置
			Sub.prototype.constructor = Sub;
			Sub.prototype.showSubProperty = function(){
				console.log(this.subProp)
			}
			
			var sub = new Sub();
			sub.showSubProperty()
			sub.showSupperProperty()
			console.log("sub constructor的检测"+(Sub.prototype.constructor===Sub))
			console.log("sub constructor的检测"+(Supper.prototype.constructor===Supper))
		</script>

 实现继承主要是包括两个步骤:

  1. 子类型的原型prototype属性指向父类型的一个对象
  2. 修正子类型的prototype对象的constructor属性使得他指向自己

构造函数位继承,父辈构造函数我也用

我们理解下图的一个基础就是我们应该记住,我们的实例对象的constructor指向的是他的构造函数,这里也是我们为什么要进行第二步constructor矫正的原因。

上面通过原型链的方式我们实现了对原型中有的属性的继承,那么我们是否可以在子类中使用父类构造函数的方法呢,就如同在Java中那样调用父类的构造函数。这在我们JS中也是可以实现的,就是通过调用我们的回调函数call、或apply。

		<script>
			function Persion(name,age){
				this.name = name;
				this.age = age;
			}
			Persion.prototype.setName = function(name){
				this.name = name;
			}
			
			function Student(name,age,price){
				//通过call的回调方法,使得我们的子类获得父类属性赋值的方法
				Persion.call(this,name,age);
				this.price = price;
			}
			//为了看到父类型的方法
			Student.prototype = new Persion();
			//修正constructor属性
			Student.prototype.constructor = Student;
			Student.prototype.setPrice = function(price){
				this.price =price
			}
			
			var student = new Student("Tom",24,150000);
			student.setName("LiuMenglei");
			student.setPrice(160000);
			console.log(student.name,student.price)
			
		</script>

Java继承、封装、多态,别的咱不知道,分装应该还是可以使用闭包来模仿一下的,更多精彩敬请期待~~~~~

中间一定有很多问题,希望有缘的朋友可以多多指正多多沟通!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值