zoukankan      html  css  js  c++  java
  • 面向对象编程思想--你必须知道的javascript

    在写面向对象编程思想-设计模式中的js部分的时候发现很多基础知识不了解的话,是很难真正理解和读懂js面向对象的代码。为此,在这里先快速补上。然后继续我们的面向对象编程思想-设计模式

    什么是鸭子类型

    javascript是一门典型的动态类型语言,也就弱类型语言。
    那什么是鸭子类型:【如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子】

    var 鸭子 = {
        走路: function () { },
        咕咕咕: function () { }
    }
    
    var 鹦鹉 = {
        走路: function () { },
        咕咕咕: function () { }
    }
    

    这只鹦鹉同样有“走路”和“咕咕咕”的方法,那在js的世界里就可以把它当成鸭子。
    可以这样调用:

    var 鸭子们 = [];
    鸭子们.push(鸭子);
    鸭子们.push(鹦鹉);
    
    for (var i = 0; i < 鸭子们.length; i++) {
        鸭子们[i].走路();
    }
    

    所以js的世界没有抽象和接口,但可以约定“我们都是鸭子”。

    javascript的面向对象

    javascript不仅是直译式脚本语言、动态类型、弱类型语言、函数为一等公民的语言,它还是基于原型的面向对象语言。面向对象三大特性:封装、继承、多态,下面我们用js分别实现。

    封装

    var Person = (function () { 
        var sex = "纯爷们";
        return {
            name: "农码一生",
            getInfo: function () {
                console.log("name:" + this.name + ",sex:" + sex);
            }
        };
    })();
    


    虽然老的js语法没有提供private等关键字,但是我们可以利用闭包来实现私有字段,达到封装的目的。

    继承

    • 字面量表示:
    var Person = {
        name: "农码一生",
        getName: function () {
            console.log(this.name);
        }
    };
    var obj = Person;
    obj.getName();
    

    • 函数构造器:
    var Person = function () {
        this.name = "农码一生";    
    }
    Person.prototype.getName = function () {
        console.log(this.name);
    }
    
    var obj = function () { };
    obj.prototype = new Person();//obj继承于Person
    
    var o = new obj();
    o.getName();//直接调用原型中的getName(类似于C#中的调用父类方法)
    

    多态

    对于多态,其实上面的鸭子类型已经表现的很清楚了。

    var 鸭子们 = [];
    鸭子们.push(鸭子);
    鸭子们.push(鹦鹉);
    
    for (var i = 0; i < 鸭子们.length; i++) {
        鸭子们[i].走路();//对于鹦鹉来说,它可能是跳着走。对于鸭子来说,它可能左右摇摆着走。这就是多态的表现。
    }
    

    对于鹦鹉来说,它可能是跳着走。对于鸭子来说,它可能左右摇摆着走。这就是多态的表现。

    原型

    什么是原型?在js中是没有类的,那它怎么创建对象。在C#中我们可以通过new关键字实例化一个对象,在js中我们用new关键字构造一个原型对象。C#中一切对象继承于Object,js中一切对象的原型是Object。

    var Person = function () {
        this.name = "农码一生";
        this.sex = "纯爷们";
    };
    console.log(Person.prototype);
    


    我们很多时候给一个对象添加方法的时候就是写在原型上,这是为什么?直接写在对象里会有问题吗?下面我们试试:

    var Person = function () {
        this.name = "农码一生";
        this.sex = "纯爷们";
        this.getInfo = function () {
            console.log("name:" + this.name + ",sex:" + this.sex);
        }
    };
    


    好像并看不出什么问题。其实不然...

    我们发现,每次构造出来的对象中的方法都会去开辟一个空间。但是对象的方法都是一样的,完全没有必要。 我们可以把方法放入原型。

    这样一来,不过我们构造多少对象,其方法都是公用的(单例的)。
    可是为什么会这样呢?
    首先,想想原型这个词,很形象,原本的模型。我们来看一个继承的例子:

    var Person = function () {
        this.name = "农码一生";
        this.sex = "纯爷们";
        this.getInfo = function () {
            console.log("name:" + this.name + ",sex:" + this.sex);
        }
    };
    var Student = function () { };
    Student.prototype = new Person();//继承
    var s1 = new Student();
    var s2 = new Student();
    console.log(s1.getInfo === s2.getInfo);
    


    虽然getInfo在Person里面是直接实现的,但是到了Student的原型(prototype)里面就是一个Person对象的单例了。也就是说无论构造多少个Student对象其中的getInfo方法都是同一个。
    但是,构造多个Person就有多个getInfo方法。所以,我们应该把getInfo方法放入Person的原型中。

    var Person = function () {
        this.name = "农码一生";
        this.sex = "纯爷们";   
    };
    Person.prototype.getInfo = function () {
        console.log("name:" + this.name + ",sex:" + this.sex);
    };
    

    我们仔细推敲下这句话“把getInfo方法放入Person的原型中”,Person的原型是Object,那也就是说getInfo方法放到Object里面去了?
    是的,不信请看:

    如果原型和原型的原型都实现了同样的方法呢?我们来猜猜下面会打印哪个版本

    var Person = function () {
        this.name = "农码一生"; 
    };
    var Student = function () { }; 
    Student.prototype = new Person();//继承
    var stu = new Student();
    
    Student.prototype.getName = function () {
        console.log("我的名字:" + this.name);
    }
    Person.prototype.getName = function () {
        console.log("My name is:" + this.name);
    }
    
    stu.getName();
    

    如果注释掉中文版呢?

    有没有觉得特神奇,具体原因我们用图来回答:

    从另个一角度说,如果对象实现了原型中已有的方法那就等效于C#中虚方法重写了。

    this指向

    var name = "张三";
    var obj = {
        name:"李四",
        getName: function(){
            console.log(this.name);
        }
    }
    
    obj.getName();
    


    这个结果大家应该没什么疑问。
    接着看下面的:

    window.name = "张三";
    var obj = {
        name:"李四",
        getName: function(){
            console.log(this.name);
        }
    } 
    //obj.getName();
    window.func = obj.getName;
    window.func();
    


    晕了没有?没关系,告诉大家一个简单实用的方法:方法是被谁“.”出来的,this就指向的谁

    call

    "方法是被谁“.”出来的,this就指向的谁",这个口诀不一定适用所有方法。为什么这么说呢?请看下面:

    window.name = "张三";
    var obj = {
        name: "李四",
        getName: function () {
            console.log(this.name);
        }
    }
    //obj.getName();
    window.func = obj.getName;
    window.func.call(obj);
    


    虽然还是window点的,但this已经指向了obj。
    因为call可以改变this执行。
    这个特性非常有用。比如,我们要编写一个下拉选中事件。

    function func() {
        console.log("我点击了" + $(this).find("option:selected").text());
    }
    
    $("#element1").change(function () {
        func.call(this);
    });
    $("#element2").change(function () {
        func.call(this);
    });
    

    在写func方法的时候不用考虑具体是那个下拉框元素。

    apply

    apply和call区别不大。

    function func(age, sex) {
        console.log("name:" + this.name + ",age:" + age + ",sex:" + sex);
    }
    
    var obj = {
        name: "晓梅"
    }
    
    func.call(obj, "18", "妹子");
    func.apply(obj,["18","小美女"]);
    


    call和apply第一个参数都是this指向的对象。call第二个和以后的参数对应方法func的参数。而apply的第二个参数是个数组,包含方法的所有参数。

    band

    function func(age, sex) {
        console.log("name:" + this.name + ",age:" + age + ",sex:" + sex);
    }
    
    var obj = {
        name: "晓梅"
    }
    var func1 = func.bind(obj, "18", "妹子");
    func1();
    

    和apply、call的区别是,只是改变this指向并不执行。且参数传入方式和call一样。

    js中的闭包

    什么是闭包?我的理解是存在不能被回收的变量就是闭包。
    最常见最大的一个闭包就是全局变量,定义了就不会被销毁,除非自动设为null。
    而我们平时说的和使用的闭包却非如此,但同样会产生不会被销毁的变量。比如我们之前说的私有变量示例:

    var Person = (function () { 
        var sex = "纯爷们";
        return {
            name: "农码一生",
            getInfo: function () {
                console.log("name:" + this.name + ",sex:" + sex);
            }
        };
    })();
    

    之所以说它是闭包,那是因为sex这个字段是永远不会被销毁。你想想,如果被销毁了,那我们调用getInfo的时候岂不是找不到sex字段了。所以不是不会销毁,而是不能销毁。
    闭包的作用不仅仅是私有化。我们再来一例:

    for (var i = 0; i < 10; i++) {
        var t = setTimeout(function () {
            console.log(i);
        }, 100);
    }
    


    并不是我们想象的那样打印0到9。
    因为计时器还没开始循环就执行完了。而此时变量i已经是10。
    我们可以通过闭包为每次循环保存一个闭包变量。

    for (var i = 0; i < 10; i++) {
        (function (i) {
            var t = setTimeout(function () {
                console.log(i);
            }, 100);
        })(i);
    }
    

    什么是高阶函数

    “高阶函数”名字特牛逼。其实我们在js中经常使用。
    还是私有变量的例子:

    var Person = (function () { 
        var sex = "纯爷们";
        return {
            name: "农码一生",
            getInfo: function () {
                console.log("name:" + this.name + ",sex:" + sex);
            }
        };
    })();
    
    • 当函数做被return时,那么就是高阶函数。
    var getInfo = function (callback) {
        $.ajax('url', function (data) {
            if (typeof callback === 'function') {
                callback(data);
            }
        });
    }
    getInfo(function (data) {
        alert(data.userName);
    });
    
    

    getInfo在执行的时候,传入的参数是个函数。

    • 当函数被当成参数传递时,那么这也是高阶函数。

    本文已同步至索引目录:《设计模式学习》
    【demo】:https://github.com/zhaopeiym/BlogDemoCode
    【推荐】:深入理解javascript原型和闭包系列

    ***************转摘:https://www.cnblogs.com/zhaopei/p/6623460.html#autoid-4-0

     
  • 相关阅读:
    js 多物体运动
    js运动 淡入淡出
    js运动 分享到
    正则 重复项最多得子项
    jq 拖拽
    jq 弹出窗口
    jq 选项卡
    jq 写法
    Codeforces 185A Plant( 递推关系 + 矩阵快速幂 )
    HDU 2604 Queuing( 递推关系 + 矩阵快速幂 )
  • 原文地址:https://www.cnblogs.com/linybo/p/13370421.html
Copyright © 2011-2022 走看看