原型对象(下)
一、原型链
是一种关系,实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的
二、原型指向改变:
- 实例对象的原型(__proto__)指向的是该对象所在的构造函数的原型对象,
- 构造函数的原型对象(prototype)的指向如果改变了,实例对象的原型(__proto__)指向会跟着改变
<script> //人的构造函数 function Person(age){ this.age=age; } //人的原型方法 Person.prototype.show=function(){ console.log("人"); } //学生的构造函数 function Student(age){ } //学生的原型方法 Student.prototype.show=function(){ console.log("学生"); } //学生的原型方法改变,指向了人的一个实例对象 Student.prototype=new Person(10); var stu=new Student(); stu.show();//人 </script>
三、原型最终指向
- 对象都有一个__proto__原型,__proto__指向某个构造函数的原型prototype
- 实例对象(__proto__)指向的是该构造函数的(prototype)
- 该构造函数的的prototype也是一个对象,__proto__原型指向的是object的原型(prototype)
- object的原型prototype也是一个对象,__proto__原型指向的是null
<script> //人的构造函数 function Person(age){ this.age=age; } //人的原型方法 Person.prototype.show=function(){ console.log("人"); } var per=new Person( ); console.log(per.__proto__==Person.prototype);//true console.log(Person.prototype.__proto__==Object.prototype);//true console.log(Object.prototype.__proto__);//null </script>
四、原型指向如果改变了,那么应该在原型指向改变后添加原型方法
<script> //人的构造函数 function Person(age){ this.age=age; } //人的原型方法 Person.prototype.sayHi=function(){ console.log("你好"); } //改变指向----一个新对象 Person.prototype={ eat:function(){ console.log("吃饭") } } var per=new Person(10);//报错 per.sayHi(); </script>
<script> //人的构造函数 function Person(age){ this.age=age; } //改变指向----一个新对象 Person.prototype={ eat:function(){ console.log("吃饭") } } //人的原型方法 Person.prototype.sayHi=function(){ console.log("你好"); } var per=new Person(10);//你好 per.sayHi(); </script>
五、举例:一个div的原型链
<div id="dv"></div> <script> var divObj=document.getElementById("dv"); console.log(divObj.__proto__);//HTMLDivElement //divObj.__proto__-------HTMLDivElement中的prototype //HTMLDivElement.prototype中的__proto__------Element中的prototype //Element.prototype中的__proto__------Node中的prototype //Node.prototype中的__proto__------EventTarget中的prototype //EventTarget.prototype中的__proto__------Object中的prototype //Object.prototype中没有__proto__------null </script>
六、总结原型
- 面向对象的特性:封装,继承,多态
- 封装就是包装,一个值存储在一个变量中,或者一堆代码放在一个函数中,或者一系列属性放在一个对象中,或者多种类似的对象放在一个js文件中
- 继承是一种关系,父类级别与类级别的关系,js中没有类,但是可以通过构造函数模拟类,然后通过原型来继承
- 原型的作用之一:数据共享,目的节省内存空间
- 原型的作用之二:为了实现继承,目的节省内存空间
<script> //Person的构造函数 function Person(name,age,sex){ this.name=name; this.age=age; this.sex=sex; } //Person添加原型方法 Person.prototype.eat=function(){ console.log("吃饭"); }; Person.prototype.sleep=function(){ console.log("睡觉"); }; Person.prototype.play=function(){ console.log("玩球"); }; //Student的构造函数 function Student(score){ this.score=score; } //改变学生的原型的指向=========>继承人的原型方法 //通过原型的继承,可以减少相同代码的书写,不会造成代码冗余 Student.prototype=new Person("小明",10,"男"); //Student添加原型方法(在指向改变之后) Student.prototype.study=function(){ console.log("学习"); }; var stu=new Student(100); console.log(stu.score);//100 console.log(stu.name);//小明 console.log(stu.age);//10 console.log(stu.sex);//男 stu.study();//学习 stu.eat();//吃饭 stu.sleep();//睡觉 stu.play();//玩球 </script>
七、借用构造函数继承
- 为了数据共享,改变原型指向,做到了继承(通过改变原型指向的继承)
- 缺陷:因为改变原型指向的同时实现的继承,直接初始化了属性,继承过来的值都是一样的
- 解决办法就是使用借用构造函数的方法(构造函数.call(对象,属性1,属性2,......))
<script> function Person(name,age,weight,sex){ this.name=name; this.age=age; this.weight=weight; this.sex=sex; this.show=function(){ console.log("人"); }; } Person.prototype.sayHi=function(){ console.log("你好"); }; function Student(name,age,weight,sex,score){ Person.call(this,name,age,weight,sex); this.score=score; } var stu=new Student("小明",18,"50kg","男",100); console.log(stu.name);//小明 console.log(stu.age);//18 console.log(stu.weight);//50kg console.log(stu.sex);//男 console.log(stu.score);//100 stu.show();//人(实例对象的方法可以继承) stu.sayHi();//报错(原型添加的方法不能继承) </script>
八、组合继承
- 借用构造函数的方法无法继承原型添加的方法
- 解决办法是使用组合继承:原型继承+借用构造函数继承
<script> //Person的构造函数 function Person(name,age,weight,sex){ this.name=name; this.age=age; this.weight=weight; this.sex=sex; this.show=function(){ console.log("人"); }; } //Person原型添加的方法 Person.prototype.sayHi=function(){ console.log("你好"); }; //Student的构造函数 function Student(name,age,weight,sex,score){ //借用构造函数继承 Person.call(this,name,age,weight,sex); this.score=score; } //原型指向改变继承原型添加的方法 Student.prototype=new Person(); var stu=new Student("小明",18,"50kg","男",100); console.log(stu.name);//小明 console.log(stu.age);//18 console.log(stu.weight);//50kg console.log(stu.sex);//男 console.log(stu.score);//100 stu.show();//人 stu.sayHi();//你好 </script>
九、拷贝继承:
把一个对象中的属性或者方法,通过遍历的方法,直接复制到另外一个对象中
<script> function Person(name){ this.name=name; } Person.prototype.age=10; Person.prototype.sex="男"; Person.prototype.weight="60kg"; Person.prototype.sayHi=function(){ console.log("你好"); }; var obj2={}; //Person的构造函数中有原型对象prototype,prototype就是一个对像,那么age,sex,weight,sayHi都是该对象的属性和方法 for(var key in Person.prototype){ obj2[key]=Person.prototype[key]; } console.log(obj2.age);//10 console.log(obj2.sex);//男 console.log(obj2.weight);//60kg obj2.sayHi();//你好 </script>
十、逆推继承看原型
<script> function F1(age){ this.age=age; } function F2(age){ this.age=age; } F2.prototype=new F1(10); function F3(age){ this.age=age; } F3.prototype=new F2(20) var f3=new F3(30); console.log(f3.age);//30 </script>
【转载文章务必保留出处和署名,谢谢!】