原型继承

一、原型判断方法
*术语解释:成员=属性+方法
实例成员:实例属性和实例方法
原型成员:原型对象属性和原型对象方法
1、原型对象:每个构造函数都有一个与之相关联的对象,这个对象称之为原型对象。 (相关联是指通过一个prototype属性来连接这个对象,然后原型对象可以通过 constructor回到构造函数)
2、特点:构造函数的原型对象上的所有属性和方法会被构造函数创建出来的所有对象共享。

3、 访问原型对象:(1) prototype
(2)proto
(3)object.getPrototypeof()
4、设置原型对象:(1)动态特性(利用prototype属性直接添加,)
(2)使用字面量替换(1、在替换前创建出的对象和替换后创建出的对象,他们所指向的原型对象不是同一个原型对象。2、修改constructor指向)
(3)系统的原型对象只可以添加属性和方法,不能替换或删除。

5、(1)hasownproperty: 检查对象中是否存在制定的属性(不包含原型成员)。
作用:过滤处理,过滤原型对象成员。
(2) in 检查对象中是否存在制定的属性(包含原型成员)。
(3)isPrototypeof()检查某个对象是否是指定对象的原型对象。(包含原型链)
(4)object.getpototypeof():获取某个对象的原型对象。
(5)instanceof :判断某个构造函数的原型对象是否在当前的原型链上面。

6、call方法和apply方法:借用其他对象的方法,并且绑定方法的内部this。方法是在函数Fuction原型对象上的。所有的函数对象都可以调用。


    call和 aplly区别:传参方式不同,call可以接受多个参数,但apply方法只接受连两个参数,后面的实参需要写入到一个字符串中;
                                 形参的默认长度不一样。(函数的length属性,获取的是函数形参的个数)

7.this关键字:指向一个对象,具体到某一个由当前上下文决定。在函数的内部,和函数的调用方法有关。
7.1函数调用方式: A、直接调用(指向window)
function demo1(){ console.log(this); }
demo1();//
B、对象的方法调用(指向调用该方法的object)
function demo1(){ console.log(this);}
var obj={}
obj.demo=demo1;
obj.demo();
C:构造函数的方式调用(指向新创建的对象)
function demo1(){ console.log(this);}
var obj= new demo1();
console.log(obj);
D:函数上下文调用(传递的第一个参数)
call和apply
7.2(重点)this的对视问题
var demo1={
name:'123',
show: function () {
console.log(this.name)}
};
var t=demo1.show;
t();
用函数t直接调用对象方法时,相当于在赋值之后,已经脱离了原对象,此时t内部的this变量已经独立出来,和源对象没有了关系,此时的this已经指向了Window,运行后,在window中查询自定义的方法,未查询到的情况下输出空字符。

8.tostring方法:转换为字符串,以字符串的方式来描述对象。 返回值是一个数组,有两个参数,他们分别表示 [对象类型,构造函数类型]
但数组类型的返回值是数组中的值,原因在于数组的tostring方法在数组的原型对象中单独存在,所以需要借用对象类型原型的方法来做为判断,
Object.prototype.toString.call()

  1. Array.isAarray判断某个对象是否是数组类型。
    注意点:这个方法是ES5的,存在兼容性问题。需要自定义一个函数来解决此问题。

二、继承
1、继承分类:(1)、基于接口的继承(2)基于实现的继承
2、javascript使用了一种独特继承机制——原型继承(不包含原型成员 )。prototype属性,是建立这种机制的关键,这个属性存在于每一个构造函数中。
3、实现方式:
(1)属性拷贝
1.1 属性拷贝分为:浅拷贝和深拷贝
存在问题:A、如果要拷贝多个对象需要多次for……in循环。B、如果属性是引用类型,存在共享问题;
解决问题A:使用object.assign来解决多次拷贝问题。
作用:拷贝对个对象的属性。
语法:object.assign(目标对象,源对象1,源对象2 …………)
注意点:---ES6退出的新方法,使用需要注意兼容性。
---存在同名属性会被按照参数顺序覆盖。
---有返回值,为目标对象。
---如果只传递一个参数,直接返回当前对象。如果不是对象,会转
换为对应的包装对象。
---如果是null或者undefined,会报错。
---目标对象正常,其他值会忽略处理其他值。字符串会特殊处理为
数组对象来处理,
---同样存在共享问题。
1.2 用深拷贝解决共享问题:
浅拷贝在对对象进行拷贝时,传递的是地址,所以存在数据共享的问题。
方案:遍历对象的过程中,值类型直接赋值,属性为引用类型,添加一个引用类型的属性,添加的时被添加的对象中并不存在一个引用类型的值,所以需要在内部初始化一个引用类型。属性的值为一个引用类型,然后遍历,再赋值。
具体代码实现:

function deepcopy(obj,obj1){
    obj=obj||{};
    for(var key  in obj1){
        if(typeof obj1[key]=='object'){
                        obj[key]={};
                 deepcopy(obj[key],obj1[key])
        }else{
                 obj[key]=obj1[key]
        }
    }
}

存在缺点:在循环遍历初始化中,无法对数组类型做出正确的判断,被解析成了object类型属性,所以需要进行改进。
在添加对象初始化中:用Array.isArray判断:
obj[key]=Array.isArray(obj1[key])?[]:{};

                    (2)原型式继承
                            第一种实现方式:
                               1.1 ---提供构造函数
                               1.2---设置构造函数原型的成员
                               1.3---直接在原型对象写入方法的缺点:
                            (a) 团队开发会出现相同属性名覆盖的问题,造成混乱,
                            (b)性能不好,和属性的访问方式相关,原型对象的属性增
                                    多,遍历的复杂度会上升。
                            1.4--- 如何克服缺点:安全扩展原型对象 
                                  提供自己的构造函数;设置构造函数的原型对象;在自己提
                            供的构造函数原型对象上添加成员;然后用自己的构造函数创建
                           对象。引出了第二种实现方式。

                           第二种实现方式:
                                1.1 ---提供构造函数
                                1.2---设置构造函数原型对象,然后替换掉构造函数原来的原
                             型。
                            对象,并且同时需要修正构造器的constructior属性,使它指向原
                            实例对象的构造函数。
                            
                         第三种实现方法:
                           1.1---提供父构造函数
                           1.2---设置父构造函数的原型对象
                           1.3---提供子构造函数
                           1.4---设置子构造函数的原型对象
                           1.5---创建对象(子函数原型对象替换为父函数原型对象)
                   缺点:a:无法获取父构造函数的实例成员;b:存在共享问题。
                            无法共享的可以通过原型链来解决(原型链继承),但依然存在
                  共享问题。

                    (3)原型链继承
                         ** 原型链结构:
                         1.1 所有的对象都是由构造函数创建出来,每个对象都有相对应的
                  构造函数,
                         1.2每个构造函数都有一个与之相关联的原型对象。 
                         1.3构造函数原型对象本身也是对象,因此,构造函数原型对象也
                   有自己的构造函数。 
                         1.4构造函数原型对象的构造函数也有相关联的原型对象。而这个
                  原型对象也是对象。
                     以上是一种链式的访问结构,JS对原型链的终点进行了特殊处理,
                object.prototype._prto_=null。
                          ** 原型链搜索规则:对象在访问属性时,首先在自身查找,然后
                 去自己的原型对象,然后去原型对象的原型对象。一直重复到找到。
                 属性和方法在位没有找到的情况下,会出现属性未定义,方法报错的情
                 况。
                     1.5 注意点:a:代码的书写顺序,完成原型链继承之后,再修改
               constructor属性指向构造函数,然后添加原型成员;b:无法向父函数传递
               参数。父构造函数的实例对象的实例成员会自动成为子构造函数的实例
               成员的原型成员。
                    1.6 object.create()该方法用来创建一个对象,并且设置创建的对象的
             原型对象为他括号内参数指定的某一对象。
                            注意点:ES5推出的,有兼容性问题,(自定义一个新方法)
           实现步骤为:先自定义一个空函数后,并设置其原型对象,然后返回。


      存在问题:无法向父构造函数传递参数。
                        共享问题。
                 

                   (4)借用构造函数(经典继承)

函数的创建:a.直接声明,
b.函数表达式(匿名式,命名式【使用命名式创建函数后,关键字后function后的名称只提供给内部调用】)
c.使用构造函数创建 var fuc = new function(形参1,形参2,函数体)


依然会存在共享的问题,需要使用深拷贝解决。
(6)组合继承2(深拷贝+借用构造函数)

    function deepcopy(obj,obj1){
        obj=obj||{};
        for(var key  in obj1){
            if(typeof obj1[key]=='object'){
                obj[key]={};
                deepcopy(obj[key],obj1[key])
            }else{
                obj[key]=obj1[key]
            }
        }
    }
    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    Person.prototype.eat = function(){
        console.log(this.name);
    }

    function Man(GF,name,age)
    {
        this.GF = GF;
        Person.call(this,name,age);
    }
    deepcopy(Person.prototype,Man.prototype);
    var m1 = new Man("范冰冰","小米",12);
    console.log(m1);

你可能感兴趣的:(原型继承)