目录
1 JS中继承的概念
通过【某种方式】让一个对象可以访问到另一个对象中的属性和方法,我们把这种方式称之为继承 `并不是所谓的xxx extends yyy`
2 为什么要使用继承?-解决内存浪费
2.1 问题引入
有些对象会有方法(动作、行为),而这些方法都是函数,如果把这些方法和函数都放在构造函数中声明就会导致内存的浪费🍂
function Person(){ this.say=function(){ console.log("你好") } } var p1=new Person(); var p2=new Person(); console.log(p1.say === p2.say); //false
现象:两个实例的say方法不相等,控制台输出为false
结论:若将方法放入构造函数中,则当每创建一个实例时就会再次创建结构相似的方法。这两个方法不是同一个。所以会造成内存的浪费
2.2 问题解决
🍃解决方案:把say方法写在他们共同的(父对象)中
function Person(name,age){ this.name=name; this.age=age; this.say=function(){} } var p1=new Person(); var p2=new Person(); //p1对象和p2对象的say方法是否是同一个方法:false console.log(p1.say===p2.say); //由于say方法可能功能相似,但是不是同一个方法(没有指向同一块内存,会造成内存浪费) //解决方案:把say方法写在他们共同的(父对象)中 //其实他们共同的父对象,就可以通过:Person.prototype来获取 //-->只要把say方法写在Person.prototype中,那么say方法就是同一个方法 Person.prototype.run=function(){ console.log('时速555KM'); } //此时p1和p2都可以访问到run方法 p1.run(); p2.run(); //验证p1.run和p2.run是否是同一个方法? console.log(p1.run == p2.run); //指向同一个方法,这种方法避免了内存的浪费 console.log(p1.run == Person.prototype.run); //true var p3=new Person(); console.log(p3.run == p1.run); //true console.log(p3.run === p1.run);//true
🌸结论:只要往某个构造函数的prototype对象中添加某个属性、方法,那么这样的属性、方法都可以被所有的构造函数的实例所共享
==>这里的【构造函数的prototype对象】称之为原型对象
Person.prototype是 p1 p2 p3 的原型对象
Person.prototype是Person构造函数的【实例】的原型对象
2.3 拓展
猜猜看?
Person的原型对象是谁呢?
- 首先要知道Person的构造函数:Function
- 所以Person的原型对象是:Function.prototype
p1的原型对象是谁呢?
- 首先要知道p1是谁创建的? Person
- 所以p1的原型对象时: Person.prototype
3 继承的实现方式
3.1 原型链继承 1
代码片实现,如下:
<script> function Dog(){ } var d1=new Dog(); //为了让d1有一个叫的方法, //不行:d1.say=function(){} //正确:在原型中添加方法 Dog.prototype.say=function(){ console.log('汪汪汪'); } </script>
🍂缺点:添加1、2个方法无所谓,但是如果方法很多会导致过多的代码冗余。代码片如下:
function Cat(name){ this.name=name; } var tom=new Cat("汤姆");//实例 //目的:把say方法放在tom的原型对象中(Cat.prototype) Cat.prototype.say=function(){} //问题: Cat.prototype.s1=function(){} Cat.prototype.s2=function(){} Cat.prototype.s3=function(){} Cat.prototype.s4=function(){} Cat.prototype.s5=function(){} //通过上面的方式,给tom的原型对象添加了好多方法,也就是让tom拥有了好多方法,但是代码产生了不少的冗余(重复)
3.2 原型链继承 2
🍃解决上面问题代码冗余问题,改良版思路:
//-->为了减少这种重复,改良版: Cat.prototype = { a1:function(){}, a2:function(){}, a3:function(){}, a4:function(){}, a5:function(){} } console.log(tom.s1); //可以访问 console.log(tom.a1); //undefined
原因:tom对象在创建的时候已经有了一个确定的原型对象,就是旧的Cat.prototype由于Cat.prototype后面被重新赋值,但是tom对象的原型对象却没有改变,所以tom对象并不能访问到新原型对象中的a1-a5方法
如何解决这个问题?
-->先改变原型、再创建对象🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀
Person.prototype={ constructor:Person, say:function(){ console.log("你好"); }, run:function(){ console.log("正在进行running"); } }
+ 注意点:
+ a、一般情况下,应该先改变原型对象,再创建对象
+ b、一般情况下,对于新原型,会添加一个constructor属性,从而不破坏原有的原型对象的结构<script> function Tiger(){ } Tiger.prototype={ a:function(){ }, b:function(){ } } //创建tiger实例,此时的tiger实例的原型对象是新原型,所以tiger可以访问到新原型中的属性和方法(a/b) var tiger=new Tiger(); console.log(tiger.a); console.log(tiger.b); </script>
🐶其他继承实现方式(拷贝继承、原型式继承、借用构造函数实现继承),后续将会继续发布分享,敬请期待!!