前端学习(五十八) JavaScript-面向对象(javaScript)

面向对象

对象是类的实例

        var a=new Object();
        a={
            name:'a',
            age:12,
            tek:['123123123',12],
            base:function(){
                return 'aa'
            }
        }

这边就是对象a是Object的实例,它可以有很多属性,对象在ES6里面有两个扩展:数据属性和访问器属性

数据属性:就是数据描述符,value(值),writable(是否可写),emumerable(是否可枚举),configurable(是否可修改删除)

Object.defineProperty(a,'x',{
            value:"x",
            writable:false,    //不允许重写
            enumerable:true,    //可以被枚举
            configurable:false    //不可以被删除
        })
        console.log(a);

访问器属性:就是存储描述符

get:返回属性值的函数。此函数没有参数,默认undefined。

set:设置属性值的函数。它具有一个包含要分配的值的参数,默认undefined。

enumerable:是否可枚举

configurable:是否可删除

创建对象

对象指的是类的实例,它将对象作为程序的基本单元,将程序数据封装其中以提高软件的重用性灵活性扩展性。

原始的对象,大量的重复代码,明显是不可取的

        let co1={
            name:"co1",
            base:function(){return 'hh'}
        }
        let co2={
            name:"co2",
            base:function(){return 'hh2'}
        }

为了简化重复代码,首先演变了一种叫做“工厂模式”

        function Co(name,age) {
            return {
                name:name,
                age:age,
                sayName:function(){
                    console.log(this.name);
                }
            }
        }
        var aa=Co('aa',16),
            ab=Co('ab',2);

这种模式的优点是大大简化了重复代码,但是有一个问题,这个是不是对象实例,出现了对象识别问题;

因此继续演变成“构造函数”模式,通过new来实例化对象

        function Co1(name,age) {
            this.name=name;
            this.age=age;
            this.sayName=function(){console.log(this.name);}
            
        }
        var aa=new Co1('aac',16),
            ab=new Co1('abc',2);

构造函数模式的优点是:实例化的对象都是指向的构造函数,而且继承自Object,可以调用Object的方法

但是这个构造函数模式有一个问题,如下

            console.log(aa.sayName===ab.sayName);    //false

同样的实例化,但是不同实例的同名函数不同

因此,继续演变,成了“原型模式”,

function Co2() {}
        Co2.prototype.name="asd";
        Co2.prototype.age=12;
        Co2.prototype.sayName=function(){console.log(this.name)}
        var ac=new Co2(),
            ad=new Co2();
            console.log(ac.sayName===ad.sayName);

这边的不同函数,就解决了不同实例的同名函数问题;

这种模式的优点:除了简化代码,实例对象之外,每个实例化对象不仅可以拥有自身的属性,还可以去追溯原型上的属性,假如有就用自己的,假如没有,就用原型的

        function Co2() {}
        Co2.prototype.name="asd";
        Co2.prototype.age=12;
        Co2.prototype.sayName=function(){console.log(this.name)}
        var ac=new Co2(),
            ad=new Co2();
            console.log(ac.sayName===ad.sayName);
            ac.name="asdff" //这时的name属性就是asdff了

            delete ac.name; //删除name属性后,name属性又会去取原型上的name属性

进一步简化:将大量prototype写在一个对象里

        function Co3() {}
        Co3.prototype={
            name:123,
            age:123,
            sayName:function(){console.log(this.name)}
        }
        var ac=new Co3();
        console.log(ac.constructor === Co2);    //fasle
        console.log(ac.constructor === Object); //true

但是这个又出现了一个新的问题,简化后的写法指向的不是构造函数,而直接是顶层对象Obejct,也可以说没有指向父级,直接指向了爷爷级

那么修改:

        function Co3() {}
        Co3.prototype={
            constructor:Co3,    //设置constructor为当前构造函数
            name:123,
            age:123,
            sayName:function(){console.log(this.name)}
        }
        var ac=new Co3();
        console.log(ac.constructor === Co3);    //fasle
        console.log(ac.constructor === Object); //true

那么所有的原型模式都存在一个缺点传址类型字段都会相互修改,如下例

传址:不是原始值类型,是对象类型的都是传址的

        function Co3() {}
        Co3.prototype={
            constructor:Co3,
            name:123,
            age:123,
            book:[],
            sayName:function(){console.log(this.name)}
        }
        let ad=new Co3(),
            ae=new Co3();
            ad.book.push(123);
            console.log(ad.book);    //[123]
            console.log(ae.book);    //[123]

我们没有对ae进行修改,但是因为ad的push修改,导致了原型被修改,原型一旦被修改,那么所有以原型为实例化的对象都遭到了修改

因此(重点)

取各方的优势,将构造函数写法和原型模式写法组合

        function Co4(name,age){
            this.name=name;
            this.age=age;
            this.book=[]
        }
        Co4.prototype={
            constructor:Co4,
            sayName:function(){
                console.log(this.book)
                
            }
        }
        let af=new Co4('qqq',20),
            ag=new Co4()
        af.book.push(999)
        console.log(af.book);   //[999]
        console.log(ag.book);   //[]

将所有参数的全部放到构造函数中,解决了相互影响,将方法放在原型

继承

大概就是:子对象对继承父级对象的属性和方法,比如构造函数继承了Obejct,实例对象继承了构造函数,那么实例对象和构造函数都可以使用Object上的属性和方法,也就是一直说的原型链

        function Co4(name,age){
            this.name=name;
            this.age=age;
            this.book=[]
        }
        Co4.prototype={
            constructor:Co4,
            sayName:function(){
                console.log(this.name)
            }
        }
        function Co4N(name,age){
            Co4.call(this,name,age) //将对象指向Co4,如果没有,那么会默认指向Obejct
        }
        Co4N.prototype=new Co4();
        Co4N.prototype.constructor=Co4;
        Co4N.prototype.sayAge=function(){
            console.log(this.age);
        }
        let ah=new Co4N("ddd",123)
        ah.sayAge()

这边就是典型的继承式,定义了祖先级的Co4,然后父级Co4N继承自Co4,然后定义了子级ah,这个ah可以调用父级sayAge方法,也可以调用Co4的SayName方法

不过这种方法存在一个多次调用的问题,可以优化一下叫做“寄生组合继承

        function zuHe(sub,subF){
            let prototype=Object(subF.prototype);
            prototype.constructor=subF;
            sub.prototype=prototype;    //将sub设置成subF的子类
        }
        function Co5(name,age){
            this.name=name;
            this.age=age;
        }
        Co5.prototype={
            sayName:function(){
                console.log(this.name)
            }
        }
        function Co5N(name,age){
            Co4.call(this,name,age) //将对象指向Co4,如果没有,那么会默认指向Obejct
        }
        zuHe(Co5N,Co5)  //将Co5N设置成Co5的子类
        Co5N.prototype.sayAge=function(){
            console.log(this.age);
        }
        let ai=new Co5N("eee",999)
        ai.sayName()

猜你喜欢

转载自blog.csdn.net/zy21131437/article/details/81389148
今日推荐