• JavaScript继承


    JavaScript继承

     

     

     

    0污染

    模块化

    实现继承的方式

    原型链继承

    构造继承

    实例继承

    拷贝继承(不推荐)

    组合继承

    寄生组合继承

     

     

     

    0污染

    什么是污染?

    当你使用一个全局变量时,就要考虑污染问题,因为一旦你占用了这个名称,其他人在想使用这个名称就会和你发生冲突,这就是污染。

    解决污染的方案:

     使用函数的方式封装

    // 将变量封装在函数内
    function f() {
        var dt = new Date()
        console.log(+dt); // 1552634315865
    }
    f()

     使用对象的方式封装

    // 将变量作为对象的属性封装
    var Module = function () {
        this.dt = new Date()
    }
    
    console.log(new Module().dt);

     在立即函数(闭包)内封装

    (function () {
        var name = 'maoamo'
        console.log(name);
    })(); // 写立即函数一定不能少了最后的;
    
    // ===================================================================
    // 为什么不能少了最后的分号?如果不写呢?
    (function () {
        console.log(123);
    })
    
    (function(){
        console.log(456);
    })
    // 上面两个立即函数在js编译的时候会变成下面这个样子:
    (function() {
        console.log(123);
    })(function(){console.log(456);}) // 也就是说第二个立即函数变成了第一个函数的参数
    
    // 在看一个例子:==============================================
    <button id="btn">点击我</button>
    <script>
        var btn = document.getElementById('btn');
        btn.onclick = function() {
            console.log(123);
        }
        (function () {
            console.log(456);
        })()
    </script>
    // 上面的代码直接打印出123然后报错:Uncaught TypeError: (intermediate value)(...) is not a function
    // 原因就是因为上面的代码最后少了;程序变成了类似下面这样:
    var btn = document.getElementById('btn');
    btn.onclick = function() {
        console.log(123);
    }(function () {console.log(456);})  // 立即函数变成了上一个函数的参数,导致上一个函数变成了立即函数
    ()
    
    // 你看上面这个函数
    function(){}() // 这个立即函数单独写是不可以的,但是经过赋值操作后这个表达式变得可运行了
    // 原因就是赋值操作,先将函数表达式赋给变量,然后在执行变量

     立即函数(闭包),将属性封装在全局变量中

    使用闭包传参(全局变量),使用全局变量封装属性,会提升效率,因为少了变量提升。

    (function (w) {
        var name = '毛毛';
        var getName = function () {
            return name;
        };
        var setName = function (name_) {
            name = name_
        };
        // 一般使用json,将所有内部东西封装起来,赋值给全局变量
        w.json = {
            name:name,
            getName:getName,
            setName:setName,
        };
    })(window); // 写立即函数一定不能少了最后的;

           对比从外部直接使用全局变量封装:考虑一丁点效率问题,会出现变量提升。

     

    (function () {
        var name = '毛毛';
        var getName = function () {
            return name;
        };
        var setName = function (name_) {
            name = name_
        };
        // 一般使用json,将所有内部东西封装起来,赋值给全局变量
        window.json = {
            name:name,
            getName:getName,
            setName:setName,
        };
    })(); // 写立即函数一定不能少了最后的;

     

     

     立即函数(闭包),使用return,将封装的属性返回

    var f = (function () {
        var name = '毛毛';
        var getName = function () {
            return name;
        };
        var setName = function (name_) {
            name = name_
        };
        return {
            name:name,
            sex:sex,
            getName:getName,
            setName:setName,
        };
    })(); // 写立即函数一定不能少了最后的;
    console.log(f);

    模块化

    演变:全局函数的方式->封装对象的方式->私有空间的划分->模块的扩展和维护

    //1.全局函数的形式
    
    <input type="text" id="x">
    <select name="" id="fa">
        <option value="+">+</option>
        <option value="-">-</option>
    </select>
    <input type="text" id="y">
    <button id="equal">=</button>
    <input type="text" id="result">
    <body>
    <script>
        var equal = document.querySelector('#equal');
    
        var add = function (x, y) {
            return x + y;
        }
    
        var subtract = function (x, y) {
            return x - y;
        }
    
        equal.onclick = function () {
            var x = parseInt(document.querySelector('#x').value);
            var y = parseInt(document.querySelector('#y').value);
            var result = document.querySelector('#result');
            var option = document.querySelector('#fa option:checked');
            switch (option.value) {
                case '+':
                    result.value = add(x, y)
                    break;
                case '-':
                    result.value = subtract(x, y)
                    break;
            }
        }
    </script>

    缺点:污染太多全局变量,对外暴露了所有变量。

    // 2.封装对象的方式
    <script>
        var calc = function () {
    
        }
    
        calc.prototype = {
            add: function (x, y) {
                return x + y;
            },
            subtract: function (x, y) {
                return x - y;
            }
        }
        /*==============上面是封装===============*/
        
        /*业务...*/
    </script>

    缺点:对外暴露了所有内部属性/方法。

    //3.私有空间划分
    <script>
        var cc =  (function () {
            var add = function (x, y) {
                return x + y;
            }
            var subtract = function (x, y) {
                return x - y;
            }
            var ceilAdd = function (x, y) {
                return ceil(x) + ceil(y)
            }
            var ceil = function (x) { // ceil仅供内部使用,是私有的
                return Math.ceil(x);
            }
            return {
                add: add,
                subtract: subtract,
                ceilAdd: ceilAdd
            }
        })();
        /*==============上面是封装===============*/
    
        /*业务...*/
    </script>

    缺点:不利于扩展和维护

    // 4.模块的扩展和维护
    <script>
        (function (calc) {
            calc.add = function (x, y) {
                return x + y;
            }
            calc.subtract = function (x, y) {
                return x - y;
            }
            calc.ceilAdd = function (x, y) {
                return ceil(x) + ceil(y)
            }
            var ceil = function (x) { // 内部方法,并未封装在对象中
                return Math.ceil(x);
            }
            window.calc = calc; // 必须在重新赋给全局({}情况)
        })(window.calc || {});
        /*==============上面是封装===============*/
    
        // 外界可直接扩展[只支持扩展,外界不能更改内部方法]
        window.calc.remander = function (x, y) {
            return x % y;
        }
    </script>

    遵循开闭原则,支持扩展和维护。

     

    在什么场景下使用模块化编程:

    1、业务复杂 2、重用逻辑非常多 3、扩展性要求较高

     

     

     

     

    实现继承的方式

    // 定义一个动物类
            function Animal(name) {
                // 属性
                this.name = name || 'Animal';
                // 实例方法
                this.sleep = function () {
                    console.log(this.name + '正在睡觉')
                }
            }
            // 原型方法
            Animal.prototype.eat = function (food) {
                console.log(this.name + '正在吃:' + food);
            }

    原型链继承

    // 1.原型链继承[核心:将父类的实例作为子类的原型]
            function Cat() {
    
            }
            Cat.prototype = new Animal();
            Cat.prototype.name = 'cat';
    
            // Test Code
            var cat = new Cat();
            console.log(cat.name);
            cat.eat('fish')
            cat.sleep()
            console.log(cat instanceof Animal); // true
            console.log(cat instanceof Cat); // true

    特点:

    1、非常纯粹的继承关系,实例是子类的实例,也是父类的实例。

    2、父类新增原型方法/原型属性,子类都能访问到。

    3、简单易于实现。

     

    缺点:

    1、要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放在构造中。

    2、无法实现多继承

    3、来自原型对象的引用属性是所有实例共享的

    4、创建子类实例时,无法向父类构造函数传参

     

     

    构造继承

    // 核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
    
    function Cat(name) {
                Animal.call(this);
                this.name = name || 'Tom';
            }
    
            // Test Code
            var cat = new Cat();
            console.log(cat.name);
            // cat.eat('fish') 报错
            cat.sleep()
            console.log(cat instanceof Animal); // false
            console.log(cat instanceof Cat); // true

    特点:

    1、解决了原型链继承中,子类实例共享父类引用属性(指定是Animal.prototype.eat)的问题。

    2、创建子类实例时,可以向父类传递参数。

    3、可以实现多继承(call多个父类对象)。

     

    缺点:

    1、实例并不是父类的实例,只是子类的实例。

    2、只能继承父类的实例属性和方法,不能继承原型属性/方法

    3、无法实现函数复用,每个子类都有父类实例函数的副本,影像性能。

     

     

     

    实例继承

            function Cat(name) {
                var instance = new Animal();
                instance.name = name || 'Tom';
                return instance;
            }
    
            // Test Code
            var cat = new Cat();
            console.log(cat.name);
            cat.eat('fish') 
            cat.sleep()
            console.log(cat instanceof Animal); // true
            console.log(cat instanceof Cat); // flase

    特点:

    1、不限制调用方式,不管是new子类()还是子类(),返回的对象具有相同的效果。

     

    缺点:

    1、实例是父类的实例,不是子类的实例。

    2、不支持多继承。

     

     

    拷贝继承(不推荐)

            function Cat(name) {
                var instance = new Animal();
                for (var p in instance) {
                    Cat.prototype[p] = instance[p]
                }
                Cat.prototype.name = name || 'Tom';
            }
    
            // Test Code
            var cat = new Cat();
            console.log(cat.name);
            cat.eat('fish')
            cat.sleep()
            console.log(cat instanceof Animal); // flase
            console.log(cat instanceof Cat); // true

    特点:

    1、支持多继承

     

    缺点:

    1、效率较低,内存占用高(因为要拷贝父类的属性)

    2、无法读取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

     

     

    组合继承

        // 通过调用父类构造,继承父类属性并保留传参的优点,然后通过将
            // 父类实例作为子类原型,实现函数复用
            function Cat(name) {
                Animal.call(this);
                this.name = name || 'Tom';
            }
            Cat.prototype = new Animal();
    
            // Test Code
            var cat = new Cat();
            console.log(cat.name);
            cat.eat('fish')
            cat.sleep()
            console.log(cat instanceof Animal); // true
            console.log(cat instanceof Cat); // true

    特点:

    1、弥补了构造继承的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法。

    2、即是子类的实例,也是父类的实例。

    3、不存在引用属性共享问题。

    4、可传参。

    5、函数可复用

     

    缺点:

    1、调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

     

     

    寄生组合继承

    // 通过寄生方式,砍掉父类的实例属性,这样在调用两次父类的构造的时候,
            // 就不会初始化两次实例方法/属性,避免组合继承的缺点
            function Cat(name) {
                Animal.call(this);
                this.name = name || 'Tom';
            }
            (function () {
                // 创建一个没有实例方法的类
                var Super = function () { };
                Super.prototype = Animal.prototype;
                // 将实例作为子类的原型
                Cat.prototype = new Super();
            })();
    
            // Test Code
            var cat = new Cat();
            console.log(cat.name);
            cat.eat('fish')
            cat.sleep()
            console.log(cat instanceof Animal); // true
            console.log(cat instanceof Cat); // true

    特点:

    1、最佳实现继承方式

     

    缺点:

    1、比较麻烦

     

    前进时,请别遗忘了身后的脚印。
  • 相关阅读:
    apache性能测试工具
    redis和memcacahe、mongoDB的区别
    redis 安装
    redis介绍
    svn基本命令
    变量
    redis持久化有几种.如何配置
    Sundy_Android开发深入浅出和高级开发视频教程
    VC++ MFC类库基础(55讲全)
    从C++起步到MFC实战VC++软件工程师高端培训 视频保存在 播音员的网盘中
  • 原文地址:https://www.cnblogs.com/liudaihuablogs/p/13468248.html
Copyright © 2020-2023  润新知