一、JS中继承的概念:

  • 通过【某种方式】让一个对象可以访问到另一个对象中的属性和方法,我们把这种方式称之为继承 `并不是所谓的xxx extends yyy`

 二、为什么要使用继承?

  • 有些对象会有方法(动作、行为),而这些方法都是函数,如果把这些方法和函数都放在构造函数中声明就会导致内存的浪费
1 function Person(){
2     this.say=function(){
3        console.log("你好")
4     }
5 }
6 var p1=new Person();
7 var p2=new Person();
8 console.log(p1.say === p2.say); //false
 1 function Person(name,age){
 2         this.name=name;
 3         this.age=age;
 4         this.say=function(){}
 5     }
 6     var p1=new Person();
 7     var p2=new Person();
 8     
 9     //p1对象和p2对象的say方法是否是同一个方法:false
10     console.log(p1.say===p2.say);
11 
12     //由于say方法可能功能相似,但是不是同一个方法(没有指向同一块内存,会造成内存浪费)
13     //解决方案:把say方法写在他们共同的(父对象)中
14     //其实他们共同的父对象,就可以通过:Person.prototype来获取
15 
16     //-->只要把say方法写在Person.prototype中,那么say方法就是同一个方法
17     Person.prototype.run=function(){
18         console.log('时速500KM');
19     }
20     //此时p1和p2都可以访问到run方法
21     p1.run();
22     p2.run();
23     //验证p1.run和p2.run是否是同一个方法?
24     console.log(p1.run == p2.run); //指向同一个方法,这种方法避免了内存的浪费
25     
26     console.log(p1.run == Person.prototype.run);//true
27 
28     var p3=new Person();
29     console.log(p3.run == p1.run); //true
30     console.log(p3.run === p1.run);//true
31     //结论:只要往某个构造函数的prototype对象中添加某个属性、方法,那么这样的属性、方法都可以被所有的构造函数的实例所共享
32     //==>这里的【构造函数的prototype对象】称之为原型对象
33     //  Person.prototype是 p1 p2 p3 的原型对象
34     //  Person.prototype是Person构造函数的【实例】的原型对象
35 
36     //猜猜看?
37     //  Person的原型对象是谁呢?
38     //  -->首先要知道Person的构造函数:-->Function
39     //  -->所以Person的原型对象是:Function.prototype
40 
41     //  p1的原型对象是谁呢?
42     //  -->首先要知道p1是谁创建的?    -->Person
43     //  -->所以p1的原型对象时:     Person.prototype

三、继承的第一种方式:原型链继承1

 1     function Dog(){
 2 
 3     }
 4     var d1=new Dog();
 5     //为了让d1有一个say的方法,
 6     //错误:d1.say=function(){}
 7     //正确:
 8     Dog.prototype.say=function(){
 9         console.log('汪汪汪');
10     }
  • 缺点:添加1、2个方法无所谓,但是如果方法很多会导致过多的代码冗余

四、继承的第二种方式:原型链继承2

 1     function Cat(name){
 2         this.name=name;
 3     }
 4     var tom=new Cat("汤姆");
 5     //目的:把say方法放在tom的原型对象中(Cat.prototype)
 6     Cat.prototype.say=function(){}
 7 
 8     //问题:
 9     Cat.prototype.s1=function(){}
10     Cat.prototype.s2=function(){}
11     Cat.prototype.s3=function(){}
12     Cat.prototype.s4=function(){}
13     Cat.prototype.s5=function(){}
14     //通过上面的方式,给tom的原型对象添加了好多方法,也就是让tom拥有了好多方法,但是代码产生了不少的冗余(重复)
15 
16     //-->为了减少这种重复,改良版:
17     Cat.prototype = {
18         a1:function(){},
19         a2:function(){},
20         a3:function(){},
21         a4:function(){},
22         a5:function(){}
23     }
24     console.log(tom.s1);    //可以访问
25     console.log(tom.a1);    //undefined
26     //原因:tom对象在创建的时候已经有了一个确定的原型对象,就是旧的Cat.prototype
27     //由于Cat.prototype后面被重新赋值,但是tom对象的原型对象却没有改变,所以tom对象并不能访问到新原型对象中的a1-a5方法
28 
29     //如何解决这个问题?
30     //-->先改变原型、再创建对象
 1     function Tiger(){
 2 
 3     }
 4     Tiger.prototype={
 5         a:function(){
 6             
 7         },
 8         b:function(){
 9 
10         }
11     }
12     //创建tiger实例,此时的tiger实例的原型对象是新原型,所以tiger可以访问到新原型中的属性和方法(a/b)
13     var tiger=new Tiger();
14     console.log(tiger.a);
15     console.log(tiger.b);
  •  注意点:

a、一般情况下,应该先改变原型对象,再创建对象

b、一般情况下,对于新原型,会添加一个constructor属性,从而不破坏原有的原型对象的结构

五、继承的第三种方式:拷贝继承(混入继承)

场景:有时候想使用某个对象中的属性,但是又不能直接修改它,于是就可以创建一个该对象的拷贝

  • 实现1:
var source={name:"李白",age:15}
var target={};
target.name=source.name
target.age=source.age;
 
 
  • 上面的方式很明显无法重用,实际代码编写过程中,很多时候都会使用拷贝继承的方式,所以为了重用,可以编写一个函数把他们封装起来:
    function extend(target,source){
        for(key in source){
            target[key]=source[key];
        }
        return target;
    }
    extend(target,source)  
  • 由于拷贝继承在实际开发中使用场景非常多,所以很多库都对此有了实现

– jquery:$.extend

  • es6中有了对象扩展运算符仿佛就是专门为了拷贝继承而生:
var source={name:"李白",age:15}
var target={ ...source }

六、继承的第四种方式:原型式继承

  • 场景:
  1. 创建一个纯洁的对象
  2. 创建一个继承自某个父对象的子对象
  • 使用方式:

空对象:Object.create(null)

var o1={ say:function(){} }
var o2=Object.create(o1); 

七、继承的第五种方式:借用构造函数实现继承

  • 场景:适用于2种构造函数之间逻辑有相似的情况
function Animal(name){
    this.name=name;
}
function Person(name,age){
    this.name=name;
    this.age=age;
}
  • 以上代码用借用构造函数实现
 1 function Animal(name,age){
 2     this.name=name;
 3     this.age=age;
 4 }
 5 function Person(name,age,address){
 6     Animal.call(this,name);
 7     //this.name=name;
 8     //this.age=age;
 9     this.address=address;
10 }

 

版权声明:本文为junjingyi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/junjingyi/p/9188312.html