JavaScript 面向对象

基本概念

对象定义

  • 可以把JavaScript对象理解为属性集合,每个属性存放一个原始值、对象或函数
  • 对象一般认为由方法和属性构成。方法的实质就是函数,而属性的实质就是变量,只不过这里由于它从属于某个对象所以叫法不同

面向对象

  • 可以简单理解为,不必去了解对象的内部结构,就可以去使用它。比如我们打电话,不必去了解它内部的工作原理;亦或者我们使用Date对象的方法可以获取和设置时间,但是我们不需要了解内部是怎么实现这个功能的

创建对象的方法

本质上都是把“属性”和“方法”,封装成一个对象

基本模式

普通创建对象的方法,缺陷:

  • 如果创建多个对象会比较繁琐,效率低
  • 实例与原型之间没有任何办法,以及有什么联系
    <script>
        var people = new Object();
        people.name = "OliGit";
        people.age = "21";
        people.show = function(){
            //this指的是当前作用域下的对象,注意和谁调用这个方法有关,和在哪定义没关系。
            //这里也可以简单理解为this就是指这个函数属于谁,属于谁this就是指的谁
            // alert(this); //Object
            return this.name + "今年" + this.age;
        }
        alert(people.name);
        alert(people.show());
    </script>

工厂模式

  • 工厂模式:使用创建并返回特定类型的对象的工厂函数(起始就是普通函数,没啥区别,只是叫法不同)
  • 创建过程类似于工厂生产产品的过程:原材料–加工–产品
  • 解决了多次重复创建多个对象的麻烦
  • 新问题:
    1、创建出的实例之间没有内在的联系,不能反映出它们是同一个原型对象的实例
    2、创建对象的时候没有使用new关键字
    3、浪费资源,因为每生成一个实例,都增加一个重复的内容,多占用一些内存
<script>
        //工厂模式
        function createPeople(name, age){
            var people = new Object();//可以类比为加工对象的原材料
            people.name = name;
            people.age = age;
            people.show = function(){
                return this.name + "今年" + this.age;
            }//以上步骤可以类比为加工对象的过程
            return people;//注意一定要将创建的对象返回
            //可以类比为产品的加工完毕出厂的工作
        }
        var daSheng = createPeople("大圣", 233);
        var OliGit2 = createPeople("OliGit", 23);
        // alert(daSheng.show());
        // alert(OliGit2.show());
    </script>

构造函数模式

  • new调用的函数为构造函数,构造函数和普通函数区别仅仅在于是否使用了new来调用
  • 所谓“构造函数”,就是专门用来生成“对象”的函数。它提供模板,作为对象的基本结构
  • 构造函数内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上
  • instanceof验证原型与实例对象之间的关系
  • call和apply方法的使用
  • 新问题:
    1、浪费内存,使用构造函数每生成一个实例,都增加一个重复的内容,多占用一些内存。
    <script>
        // 构造函数模式
        //构造函数不需要使用return语句返回对象,它的返回是自动完成的
        //如果有new, 函数里的this就是新创建出来的对象,如果没有就是window
        function NewPeople(name, age){
            this.name = name;
            this.age = age;
            this.show = function(){
                return this.name + "今年" + this.age;
            }
        }
        var OliGit3 = new NewPeople("OliGit3", 22);
        var OliGit4 = new NewPeople("OliGit4", 24);
        // alert(OliGit3.show());
        // alert(OliGit4.show());
        // alert(OliGit4 instanceof NewPeople);

        var master = new Object();
        // NewPeople.call(master, "Oliver", 22);
        NewPeople.apply(master, ["oliver", 23]);
        alert(master.show());
        alert(master.name);
    </script>

原型模式

JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。可以把那些不变的属性和方法,直接定义在prototype对象上

  • prototype方式定义函数不会拷贝到每一个实例中,所有的实例共享prototype中的定义,节省了内存

  • prototype模式的验证方法
    1、isPrototypeOf():这个方法用来判断,某个prototype对象和某个实例之间的关系
    2、hasOwnProperty():每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是自身独有属性,还是继承自prototype对象的属性
    3、in运算符可以用来判断,某个实例是否有某个属性,不管是不是本地属性。in运算符还可以用来遍历某个对象的所有属性

  • 对象的constructor属性用于返回创建该对象的构造函数;在JavaScript中,每个具有原型的对象都会自动获得constructor属性

  • 新问题:
    1、构造函数没有参数。使用原型方式,补鞥呢通过给构造函数传递参数来初始化属性的值
    2、属性指向的是对象,而不是函数时。函数共享不会造成问题,但是对象却很少被多个实例共享,如果共享的是对象就会造成问题

    <script>
        //原型模型
        function PeopleObj(){}
            PeopleObj.prototype.name = "OLIGIT";
            PeopleObj.prototype.age = 18;
            PeopleObj.prototype.show=function(){
                return this.name + "今年" + this.age;
            }
        
        var OliGit5 = new PeopleObj();
        var OliGit6 = new PeopleObj();
        // alert(OliGit5.name + '\n' + OliGit5.show());
        // alert(OliGit5.show == OliGit6.show);//true;说明他们引用的是同一个地址
        //这时所有实例的方法,其实都是同一个内存地址,指向prototype对象,因此提高了运行效果

        // alert(PeopleObj.prototype.isPrototypeOf(OliGit6));
        OliGit6.say = "hello!";//自身独有属性,非继承prototype属性
        // alert(OliGit6.hasOwnProperty("name"));//继承prototype属性返回false
        // alert(OliGit6.hasOwnProperty("say"));//自身独有属性返回true

        // alert("name" in OliGit6);
        // alert("say" in OliGit6);

        //原型模式简约写法
        function PeopleObj2(){}
        PeopleObj2.prototype = {
            name : "OLIGIT",
            age : 18,
            show : function(){
                return this.name + "今年" + this.age;
            }
        }
    </script>

构造函数和原型组合模式

这是目前最常用的创建对象的方式

  • 这种概念非常简单,即用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数属性(方法),结果是,所有函数都只创建一次,而每个对象都具有自己的对象属性实例
  • 此外,组合模式还支持向构造函数传递参数,可谓是集两家之所长
  • 在所接触的js库中,JQuery类型的封装就是使用组合模式来实例的
    <script>
        // 构造函数和原型组合模式
        function Person(name, age){
            this.name = name;
            this.age = age;
        }
        Person.prototype = {
            show:function(){
                return this.name + "今年" + this.age;
            }
        }
        var pson = new Person("OliGit7", 22);
        var pson2 = new Person("OliGit8", 25);
        alert(pson.show());
        alert(pson2.show());
    </script>

动态原型方法

动态原型方法的基本想法与混合的构造函数原型方式相同,即在构造函数内定义非函数属性,而函数属性则利用原型属性定义

    <script>
        //动态原型模型
        function Person2(name, age){
            this.name = name;
            this.age =age;
            if(typeof this.show != "function"){
                Person2.prototype.show = function(){
                return this.name + "今年" + this.age;
                }
            }
        }
        var pson3 = new Person2("OliGit9", 19);
        var pson4 = new Person2("OliGit10", 20);
        alert(pson3.show());
        alert(pson4.show());
    </script>

继承

继承的实现

实现继承的方式不止一种。这是因为JavaScript中的继承机制并不是明确规定的,而是通过模仿实现的。

对象冒充(构造函数绑定)

原理如下:使用对象冒充(call或apply方法)(实质上是改变了this指针的指向)继承基类

    <script>
        function Monkey(type, home){
            this.type = type;
            this.home = home
            this.say = function(){
                alert("我是" + this.type + ", 家住" + this.home);
            }
        }
        function Hero(HP){
            this.HP = HP;
        }
        function Magic_monkey(type, home, arr, HP){
            Monkey.call(this, type, home);
            Hero.call(this, HP);
            this.skill = arr;
        }
        var wukong = new Magic_monkey("猴子", "花果山", ["七十二变", "筋斗云"], 1000);
        // alert(wukong.home);
        // alert(wukong.type);
        // alert(wukong.skill);
        alert(wukong.HP);
        wukong.say();
    </script>

原型链

这种方法更常见,使用prototype属性

  • prototype对象是个模板,要实例化的对象都以这个模板为基础。总而言责,prototype对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制
  • 原型链的弊端是不支持多重继承。记住,原型链会用另一类型的对象重写类的prototype属性
  • 子类的所有属性和方法都必须出现在prototype属性被赋值后,因为在它之前赋值的所有方法都会被删除,因为在它之前赋值的所有方法都会被删除,因为prototype属性被替换成了新对象,添加了新方法的原始对象将被销毁
<script>
        //原型链
        function Monkey2(){};
        Monkey2.prototype.type = "猴子";
        Monkey2.prototype.say = function(){alert("我是一个快乐的猴子");}

        function MagicMonkey(){};
        //将MagicMonkey的prototype对象指向一个Monkey2的实例
        //相当于删除了prototype对象原先的值,然后赋予一个新值
        //不能继承多个类,后面的会覆盖前面的
        MagicMonkey.prototype = new Monkey2();//继承Monkey2
        
        MagicMonkey.prototype.skill = "土遁术";

        var sunWuKong = new MagicMonkey();
        // alert(sunWuKong.type);
        // sunWuKong.say();
        // alert(sunWuKong.skill);
    </script>

混合方式

创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法

    <script>
        // 混合模式
        function Monkey3(type, home){
            this.type = type;
            this.home = home;
        }
        Monkey3.prototype.say = function(){
            alert("我是一只快乐猴子, 家住" + this.home);
        }

        function MagicMonkey3(type, home){
            //用对象冒充继承Monkey3类的type、home属性
            Monkey3.call(this, type, home);
        }
        //用原型链继承Monkey3类的say方法
        MagicMonkey3.prototype = new Monkey3();

        var wukong3 = new MagicMonkey3("猴子", "水帘洞");
        alert(wukong3.type);
        alert(wukong3.home);
        wukong3.say();
    </script>
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值