JavaScript-中级:5 面向对象

1.面向对象的概念

 

// 思想的进化之路:
2. 面向对象编程:

  1. 简单方式创建一个对象和工厂模式批量创建对象
    示例代码:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>工厂创建对象的方式</title>
    </head>
    <body>
        <script>
        // 1.创建对象方式1
        var a = {}; // 缺点就是不能重复利用 设置公共属性的代码
    
        // 2.创建对象方式2 工厂模式
        // 优点:可以进行批量的创建
        // 缺点: ① 对象的方法不能和其他对象共享,多占用内存
        //       ② 不能识别对象的原型和构造函数 
        //              故:后续的代码中 c instanceOf Cat; // false
        function createCat(age, name){ // 工厂模式就是标准化的生成默认对象的函数。
            var o = new Object(); // 创建一个对象
            // 为这个对象添加属性
            o.age = age;
            o.name = name;
            o.run = function(){
                console.log(o.name + " is running!");
            }
            return o;        
        }
    
        var c = createCat(19, 'lili'); // 创建一只叫lili的猫
        c.run(); // lili is running!
        </script>
    </body>
    </html>

    示例讲解图:

  2. 构造函数创建对象

    示例代码:
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>面向对象编程</title>
    </head>
    <body>
        <script>
        // 1.创建对象方式1
        var a = {}; // 缺点就是不能重复利用 设置公共属性的代码
    
        // 2.创建对象方式2 工厂模式
        // 优点:可以进行批量的创建
        // 缺点: ① 对象的方法不能和其他对象共享,多占用内存
        //       ② 不能识别对象的原型和构造函数 
        //              故:后续的代码中 c instanceOf Cat; // false
        function createCat(age, name){ // 工厂模式就是标准化的生成默认对象的函数。
            var o = new Object(); // 创建一个对象
            // 为这个对象添加属性
            o.age = age;
            o.name = name;
            o.run = function(){
                console.log(o.name + " is running!");
            }
            return o;        
        }
    
        var c = createCat(19, 'lili'); // 创建一只叫lili的猫
        c.run(); // lili is running!
    
        // 3.创建对象方式3 构造函数创建对象的模式
        //     优点:
        //           ① 创建对象的时候默认初始化一些属性。
        //           ② 可以使用instanceOf追溯对象的原型及构造函数
        //     缺点:对象的方法不能重用,每个对象都会存储一份,造成内存浪费。
        //     当使用new来调用构造函数时:
        //                       ① 创建一个空对象
        //                       ② 把空对象赋值给this
        //                       ③ 执行构造函数内部代码,并给this的属性做赋值初始化
        //                       ④ 把创建的对象返回
        function Cat(age, name){
            this.age = age;
            this.name = name;
            this.run = function(){
                console.log(this.name + " is running!!");
            };
        }
        var c2 = new Cat(18, "golr");  // 通过构造函数创建一个对象
        c2.age = 17; // 修改对象的属性值
        c2.run(); // golr is running!!
        // c2 instanceof Cat // true
        // c2.constructor === Cat // 因为c1的__proto__指向Cat原型,Cat原型(Cat.prototype)上有constructor
        </script>
    </body>
    </html>

  3. 原型构造对象的方法
    示例代码:
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>面向对象编程</title>
    </head>
    <body>
        <script>
        // 1.创建对象方式1
        var a = {}; // 缺点就是不能重复利用 设置公共属性的代码
    
        // 2.创建对象方式2 工厂模式
        // 优点:可以进行批量的创建
        // 缺点: ① 对象的方法不能和其他对象共享,多占用内存
        //       ② 不能识别对象的原型和构造函数 
        //              故:后续的代码中 c instanceOf Cat; // false
        function createCat(age, name){ // 工厂模式就是标准化的生成默认对象的函数。
            var o = new Object(); // 创建一个对象
            // 为这个对象添加属性
            o.age = age;
            o.name = name;
            o.run = function(){
                console.log(o.name + " is running!");
            }
            return o;        
        }
    
        var c = createCat(19, 'lili'); // 创建一只叫lili的猫
        c.run(); // lili is running!
    
        // 3.创建对象方式3 构造函数创建对象的模式
        //     优点:
        //           ① 创建对象的时候默认初始化一些属性。
        //           ② 可以使用instanceOf追溯对象的原型及构造函数
        //     缺点:对象的方法不能重用,每个对象都会存储一份,造成内存浪费。
        //     当使用new来调用构造函数时:
        //                       ① 创建一个空对象
        //                       ② 把空对象赋值给this
        //                       ③ 执行构造函数内部代码,并给this的属性做赋值初始化
        //                       ④ 把创建的对象返回
        function Cat(age, name){
            this.age = age;
            this.name = name;
            this.run = function(){
                console.log(this.name + " is running!!");
            };
        }
        var c2 = new Cat(18, "golr");  // 通过构造函数创建一个对象
        c2.age = 17; // 修改对象的属性值
        c2.run(); // golr is running!!
        // c2 instanceof Cat // true
        // c2.constructor === Cat // 因为c1的__proto__指向Cat原型,Cat原型(Cat.prototype)上有constructor
        
        // 4.创建对象方式4 原型构造对象的模式
        function Cat3(){
            this.age = 12;
            this.name = 'serdy';
        }
        // 需要共享的方法或属性,可以在原型上定义
        Cat3.prototype.run = function(){   // 所有实例对象都共享的方法
            console.log(this.name + " is running...");
        };
        Cat3.prototype.type = "Orange"; // 所有实例对象都共享的属性
        var c3 = new Cat3();
        c3.run(); // serdy is running...
        var c3_2 = new Cat3();
        c3_2.run(); // serdy is running...
        console.log(c3.run === c3_2.run); // true
    
        c3.type = "Persian"; // 对象的属性分为: 读取和设置两种模式
                                // 读取:自己没有就往上找,直到找到,若找不到则为undefined。
                                // 写入:自己没有,就直接添加
        console.log(c3.type); // Persian
        console.log(c3_2.type); // Orange
    
        </script>
    </body>
    </html>

    运行结果:

  4. 组合模式(目前比较好(本笔记基于ES3),js在ES6后有了进一步发展,有了class关键字)

    示例代码:
     

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>面向对象编程</title>
    </head>
    <body>
        <script>
        // 1.创建对象方式1
        var a = {}; // 缺点就是不能重复利用 设置公共属性的代码
    
        // 2.创建对象方式2 工厂模式
        // 优点:可以进行批量的创建
        // 缺点: ① 对象的方法不能和其他对象共享,多占用内存
        //       ② 不能识别对象的原型和构造函数 
        //              故:后续的代码中 c instanceOf Cat; // false
        function createCat(age, name){ // 工厂模式就是标准化的生成默认对象的函数。
            var o = new Object(); // 创建一个对象
            // 为这个对象添加属性
            o.age = age;
            o.name = name;
            o.run = function(){
                console.log(o.name + " is running!");
            }
            return o;        
        }
    
        var c = createCat(19, 'lili'); // 创建一只叫lili的猫
        c.run(); // lili is running!
    
        // 3.创建对象方式3 构造函数创建对象的模式
        //     优点:
        //           ① 创建对象的时候默认初始化一些属性。
        //           ② 可以使用instanceOf追溯对象的原型及构造函数
        //     缺点:对象的方法不能重用,每个对象都会存储一份,造成内存浪费。
        //     当使用new来调用构造函数时:
        //                       ① 创建一个空对象
        //                       ② 把空对象赋值给this
        //                       ③ 执行构造函数内部代码,并给this的属性做赋值初始化
        //                       ④ 把创建的对象返回
        function Cat(age, name){
            this.age = age;
            this.name = name;
            this.run = function(){
                console.log(this.name + " is running!!");
            };
        }
        var c2 = new Cat(18, "golr");  // 通过构造函数创建一个对象
        c2.age = 17; // 修改对象的属性值
        c2.run(); // golr is running!!
        // c2 instanceof Cat // true
        // c2.constructor === Cat // 因为c1的__proto__指向Cat原型,Cat原型(Cat.prototype)上有constructor
        
        // 4.创建对象方式4 原型构造对象的模式
        function Cat3(){
            this.age = 12;
            this.name = 'serdy';
        }
        // 需要共享的方法或属性,可以在原型上定义
        Cat3.prototype.run = function(){   // 所有实例对象都共享的方法
            console.log(this.name + " is running...");
        };
        Cat3.prototype.type = "Orange"; // 所有实例对象都共享的属性
        var c3 = new Cat3();
        c3.run(); // serdy is running...
        var c3_2 = new Cat3();
        c3_2.run(); // serdy is running...
        console.log(c3.run === c3_2.run); // true
    
        c3.type = "Persian"; // 对象的属性分为: 读取和设置两种模式
                                // 读取:自己没有就往上找,直到找到,若找不到则为undefined。
                                // 写入:自己没有,就直接添加
        console.log(c3.type); // Persian
        console.log(c3_2.type); // Orange
    
        // 5.创建对象方式5 组合模式
        function Cat4(age, name){
            // 每个对象都有自己私有的属性值的属性,放到构造函数中。
            this.age = age;
            this.name = name;
        }
    
        // 一般‘类型’(在ES6中就进一步有了class关键字,此为后话)都放在原型上,让所有对象都共享方法的内存。
        Cat4.prototype.run = function(){
            console.log(this.name + " is running!");
        };
        var c4_1 = new Cat4(12, 'weru'); 
        var c4_2 = new Cat4(10, "oity"); 
        c4_1.run(); // weru is running!
        c4_2.run(); // oity is running!
        console.log(c4_1.run === c4_2.run); // true
    
        </script>
    </body>
    </html>
  5. 稳妥构造函数模式 (其实和工厂模式很像,不过用这种方式创建实例对象时,兼容了new)

    示例代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>面向对象编程</title>
    </head>
    <body>
        <script>
        // 6.创建对象的方式6 稳妥构造函数模式 (其实和工厂模式很像,不过用这种方式创建实例对象时,兼容了new)
        function Cat5(){
            var cat = {}; // 此处和工厂模式不一样!
            cat.age = 9;
            cat.name = 'cty';
            cat.run = function(){
                console.log(cat.name + " is running.....!");
            };
    
            return cat; // 如果是构造函数执行模式,如果返回的是一个引用类型,就把引用类型返回;如果返回的是简单类型,那么就返回this。
    
        }
    
        // 稳妥构造函数模式,要实现使用new构造一个对象和不使用new构造一个对象,效果一样。
        var c5_1 = new Cat5(); // 构造函数调用模式
        var c5_2 = Cat5(); // 函数调用模式
        c5_1.run(); // cty is running.....!
        c5_2.run(); // cty is running.....!
        // 缺点:不能溯源 原型、构造函数; 对象的方法内存不能共享,浪费内存。
        </script>
    </body>
    </html>

3.对象的继承(重点)

// 思想的进化历程

1.原型继承模式

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>对象的继承</title>
</head>
<body>
    <script>
    // 1.原型继承模式
    function Animal(age, name){ // 动物基类
        this.age = age;
        this.name = name;
    }
    Animal.prototype.run = function(){ // 在动物基类的原型上添加方法run
        console.log(this.name + " is running!");   
    };

    function Cat(age, name){
        this.age = age;
        this.name = name;
    }
    Cat.prototype = new Animal(); // 原型的继承方式,由于Animal的constructor指向的是Animal构造函数,
    // 故,这里还要改回来 !
    Cat.prototype.constructor = Cat;

    var c = new Cat(12, 'iser');
    c.run(); // 从Animal原型上继承的方法
    // 存在问题:
        // 1.子类的构造函数的参数,没法传递给父类的构造函数。
        // 2.子类的原型的constructor会被改变,需要自己改回来。
        // 3.如果父类有引用类型的属性,那么所有的子类会共享这个引用类型的属性。
    console.log(c);
    </script>
</body>
</html>

ps:(白话)原型链回溯时能找到想要的。

原理图:

2.组合继承模式:组合的原型继承和借用构造函数继承 (解决“ 1.原型继承模式” 中的存在的问题1)

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>对象的继承</title>
</head>
<body>
    <script>
    // 1.原型继承模式
    function Animal(age, name){ // 动物基类
        this.age = age;
        this.name = name;
    }
    Animal.prototype.run = function(){ // 在动物基类的原型上添加方法run
        console.log(this.name + " is running!");   
    };

    function Cat(age, name){
        this.age = age;
        this.name = name;
    }
    Cat.prototype = new Animal(); // 原型的继承方式,由于Animal的constructor指向的是Animal构造函数,
    // 故,这里还要改回来 !
    Cat.prototype.constructor = Cat;

    var c = new Cat(12, 'iser');
    c.run(); // 从Animal原型上继承的方法
    // 存在问题:
        // 1.子类的构造函数的参数,没法传递给父类的构造函数。
        // 2.子类的原型的constructor会被改变,需要自己改回来。
        // 3.如果父类有引用类型的属性,那么所有的子类会共享这个引用类型的属性。
    console.log(c);

    // 2.组合继承模式:组合的原型继承和借用构造函数继承 (解决“ 1.原型继承模式” 中的存在的问题1)
    // 父类
    function Animal1(age, name){
        this.age = age;
        this.name = name;
        this.foos = ['veg', 'ani'];
    }
    // 在父类的原型上创建一个run方法
    Animal1.prototype.run = function(){
        console.log(this.name + " is running!!!");
    };

    // 定义子类
    function Cat1(age, name){
        // Animal(age, name); // 函数执行模式,此时 Animal里的this 指向window
        // 借用父类的构造函数给子类创建实例属性;第①次执行父类的构造函数
        Animal.call(this, age, name); //解决函数执行模式的问题: 把this传过去
    }
    Cat1.prototype = new Animal(); // 第②次执行父类的构造函数
    Cat1.prototype.constructor = Cat1;
    var c1 = new Cat1(12, 'oerd');
    c1.run();
    // 存在的问题:
        // 1.子类的原型的constructor会被改变,需要自己改回来。
        // 2.如果父类有引用类型的属性,那么所有的子类会共享这个引用类型的属性。
        // 3.父类的构造函数被执行了两次(完美控不能忍)。


    </script>
</body>
</html>

3.原型式继承

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>对象的继承</title>
</head>
<body>
    <script>

    // 3.原型式继承模式
    function object(o){ // o就是要借用的对象   
        function F(){}
        F.prototype = o;    // 让空函数的原型指向o对象
        return new F(); // 创建一个f实例,f的内部原型指向o对象
    }
    var m = {age: 19, name: 'lili', friend: ['gir', 'ier']};
    var m1 = object(m);
    console.log(m1.friend); // Array [ "gir", "ier" ]
    m1.age = 20;
    console.log(m1); // 由于原型链的写规则,自己没有就自己创建,故不会对m造成影响!
    console.log(m); 
    // 优点:
        // 不需要使用new构造函数就可以直接构造其他对象。
    // 缺点:
        // 所用构造出来的实例会共享原型对象上的引用类型的属性。
    // 原型式继承模式:在EC5中被标准化了,可以直接用Object.create();
    </script>
</body>
</html>

4.寄生继承模式

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>对象的继承</title>
</head>
<body>
    <script>
   
   // 4.寄生继承模式:
   // 传一个对象到一个方法(工厂方法)。方法内部根据传来的对象构造一个新对象,并对新对象进行扩展增强。返回新对象。
   function createPerson(p){
        var o = object(p); // 通过p对象构造一个新对象o
        o.say = function(){ // 对新构造出来的对象o进行扩展
            console.log("hi"); 
        }
        return o;
   }
   
    </script>
</body>
</html>

5.[重点] 寄生组合继承模式

略。详见:寄生组合继承模式

发布了191 篇原创文章 · 获赞 1 · 访问量 4699

猜你喜欢

转载自blog.csdn.net/bluebloodye/article/details/103058685
今日推荐