zoukankan      html  css  js  c++  java
  • JavaScriptOOP

    1. OOP基础


    1.1面向对象编程OOP

    1、语言的分类:
         ① 面向机器:汇编语言
         ② 面向过程:C语言
         ③ 面向对象:C++ Java PHP 等
     
    2、面向过程与面向对象
         ① 面向过程:专注于如何去解决一个问题的过程步骤。编程特点是由一个个的函数去实
                                现每一步的过程步骤,没有类和对象的概念。
         ② 面向对象:专注于由哪一个对象来解决这个问题。编程特点是出现了一个个的类,从
                               类中拿到对象,由这个对象去解决具体问题。
     
    对于调用者来说,面向过程需要调用者自己去实现各种函数;而面向对象,只需要告诉调用者对象中具体方法的功能,而不需要调用者了解方法中的实现细节。
     
    [面向对象的三大特征]
    继承、封装、多态
    JS可以模拟实现继承和封装,但是无法模拟实现多态,所以说JS是一门基于对象的语言,而并非是面向对象的语言!
     

    1.2类和对象

    1、类:一类具有相同特征(属性)和行为(方法)的集合。
      eg:人类-->属性:身高、体重、性别 方法:吃饭、睡觉、说话
     
    2、对象:从类中,拿出具有确定属性值和方法的个体。
      eg:张三-->属性:身高180 体重160 方法:说话-->我叫张三
     
    3、类和对象的关系:
      类是抽象的,对象是具体的(类是对象的抽象化,对象是类的具体化)
     
    类是一个抽象的概念,只能说类有属性和方法,但是不能给属性赋具体的值。
    eg:人类有姓名,但是不能说人类的姓名是什么
     
    对象是一个具体的个例,是将类中的属性进行具体赋值而来的个体
    eg:张三是人类的一个个体,可以说张三的姓名是张三,也就是张三对人类的每一个属性进行了赋值,那么张三就是由人类产生的一个对象。
     
    4、使用类和对象的步骤:
        ① 创建一个类(构造函数):类名必须使用大驼峰法则,即每个单词首字母都要大写。
            function Person(name,age){
                this.name=name;
                this.age=age;
                this.say=function(content){
                    // 在类中,访问类自身的属性,必须使用this.属性 调用
                    alert("我叫"+this.name+",今年"+this.age+"岁。我说了一句话:"+content);
                }
            }        
     
     ② 通过类,实例化(new)出一个对象。
    var zhangsan = new Person("张三",18);
        var obj = new 类名(属性1的具体值);
        obj.属性; 调用属性
        obj.方法; 调用方法
     
      ③ 注意事项:
          >>> 通过类名,new出一个对象的过程,叫做"类的实例化"
          >>> 类中的this,会在实例化的时候,指向新new出的对象;
        所以,this.属性 this.方法 实际上是将属性和方法绑定在即将new出的对象上面
        >>> 在类中,要调用自身属性,必须使用this.属性名。如果直接使用变量名,则无法访问对应属性。
          >>> 类名必须使用大驼峰法则,注意与普通函数区分。
     
    5、两个重要属性:
        ① constructor:返回当前对象的构造函数。
            >>> lisi.constructor==Person; √
        ② instanceof:检测一个对象,是不是一个类的实例
            >>> lisi instanceof Person √ lisi是通过Person类new出的
            >>> lisi instanceof Object √ 所有对象都是Object的实例
            >>> Person instanceof Object √ 函数本身也是对象
     
    6、广义对象与狭义对象:
        ① 狭义对象:只有属性和方法,除此之外没有任何其他内容。
                               var obj = {} var obj = new Object();
        ② 广义对象:除了用字面量声明的基本数据类型之外,JS中万物皆对象。换句话说,只要能够添加属性和方法的变量,都可以成为对象。
    var s="123"; // 不是对象
    s.name="aaa";
    console.log(typeof s); // String
    console.log(s.name); // Undefined 字面量声明的字符串不是对象,不能添加属性。
    2. 成员属性、静态属性和私有属性

    2.1成员属性与成员方法

    在构造函数中,使用this.属性 声明,或者在实例化出对象以后,使用"对象.属性"追加的,都属于成员属性或成员方法,
    也叫实例属性和实例方法。
     
    成员属性/方法,是属于由类new出的对象的。
    需要使用"对象名.属性名"调用
            var zhangsan = new Person("张三");
            zhangsan.age = 14; // 追加成员属性
            console.log(zhangsan.age); // 调用成员属性

    2.2静态属性与静态方法

    1、通过"类名.属性"、"类名.方法"声明的属性和方法,称为静态属性、静态方法,也叫类属性和类方法。
     
       类属性/类方法,是属于类的(属于构造函数的)
       通过"类名.属性名"调用
     
    2、成员属性是属于实例化出的对象的,只能使用对象调用。
       静态属性是属于构造函数的,只能使用类名调用。
            Person.count = "60亿"; // 声明静态属性
            console.log(Person.count); // 调用静态属性
            var lisi = new Person("李四");
            console.log(lisi.count); // Undefined 静态属性是属于类的,只能用类名调用

    2.3私有属性与私有方法

    在构造函数中,使用var声明的变量,称为私有属性;
    在构造函数中,使用function声明的函数,称为私有方法;
                 function Person(){
                      var num = 1; // 私有属性
                      function func(){}; // 私有方法
                 }

    私有属性和私有方法的作用域,只在构造函数内部有效。即,只能在构造函数内部使用,无论使用对象名还是类名都无法调用。

    3. JS模拟实现封装
     
    1、 什么叫封装?
        ① 方法的封装:将类内部的函数进行私有化处理,不对外提供调用接口,无法在类外部使用的方法称为私有方法,即方法的封装。
     
        ② 属性的封装:将类中的属性进行私有化处理,对外不能直接使用对象名访问(私有属性)。同时,需要提供专门用于设置和读取私有
                                  属性的set/get方法,让外部使用我们 提供的方法,对属性进行操作。这就叫属性的封装。
     
    2、 注意:封装不是拒绝访问,而是限制访问。要求调用者,必须使用我们提供的set/get方法进行属性的操作,而不是直接拒绝操作。
                      因此,单纯的属性私有化,不能成为封装!必须私有化之后,提供对应的set/get方法
    function Person(){
       var age=0;
       this.setAge=function(ages){
          age=ages;
       }
       this.getAge=function(){
          return age;
       }
    }
    4. JS中的this指向详解


    4.1谁最终调用函数,this就指向谁

    4.1.1  this指向谁,不应该考虑函数在哪声明,而应该考虑函数在哪调用!!!
    4.1.2  this指向的,永远只可能是对象,而不可能是函数!!!
    4.1.3  this指向的对象,叫做函数的上下文/context,也叫函数的调用者
     

    4.2this指向的规律!!!(与函数的调用方式息息相关)

    4.2.1  通过函数名()调用的,this永远指向window  
     func();
     
    4.2.2  通过对象.方法调用的,this指向这个对象
    obj.func(); // 狭义对象
    document.getElementById("div1").onclick = func; // 广义对象
     
    4.2.3  函数作为数组中的一个元素,用数组下标调用的,this指向这个数组
    var arr = [1,2,3,func,4,5,6];
    arr[3]();
     
    4.2.4  函数作为window内置函数的回调函数使用,this指向window。setInterval setTimeout 等
    setTimeout(func,1000);
     
    4.2.5  函数作为构造函数,使用new关键字调用,this指向新new出的对象。
    var obj1 = new func();
    5. 原型与原型链

    5.1__proto__与prototype

    5.1.1   prototype:函数的原型对象
         ① 只有函数才有prototype,而且所有函数必有prototype。
         ② prototype本身也是一个对象!
         ③ prototype指向了当前函数所在的引用地址!
     
    5.1.2   __proto__:对象的原型!
         ① 只有对象才有__proto__,而且所有对象必有__proto__。
         ② __proto__也是一个对象,所以也有自己的__proto__,顺着这条线向上找的顺序,就是原型链。
         ③ 函数、数组都是对象,都有自己的__proto__
     
    5.1.3   实例化一个类,拿到对象的原理?
          实例化一个类,实际上是将新对象的__proto__,指向构造函数所在的prototype。
          也就是说:zhangsan.__proto__==Person.prototype √
     
    5.1.4   所有对象的__proto__沿原型链向上查找,都将指向Object的prototype。
          Object的prototype的__proto__,指向null
     

    5.2原型链的指向问题

    研究原型链的指向,就是要研究各种特殊对象的__proto__指向问题。
    5.2.1   通过构造函数,new出的对象,新对象的__proto__指向构造函数的prototype
    5.2.2   函数的__proto__,指向Function()的prototype
    5.3.3   函数的prototype的__proto__指向Object的prototype
               (直接使用{}字面声明,或使用new Object拿到的对象的__proto__直接指向Object的prototype)
    5.2.4   Object的prototype的__proto__指向null
               (Object作为一个函数,它的__proto__指向Function()的prototype)
     
    6. 原型属性与原型方法
    6.1   成员属性与成员方法:
       this.name = ""; this.func = function(){}
       >>> 属于实例化出的新对象,使用 对象.属性 调用
     
    6.2   静态属性与静态方法:
       Person.name = ""; Person.func = function(){}
       >>> 属于类的,用 类名.属性 调用
     
    6.3   私有属性与私有方法:
       构造函数中,使用var声明属性,使用function声明方法;
       >>> 只在类内部能够使用,外部无法使用任何方式调用。
     
    6.4   原型属性与原型方法:
       Person.prototype.name = "";
       Person.prototype.func= function(){};
       >>> 将属性或方法写到类的prototype上,在实例化的时候,这些属性和方法就会进入
          到新对象的 __proto__上,就可以使用对象名调用
     
    也就是说,1/4适用对象名访问,2使用类名访问,3只能在函数内部使用。
     
    6.5   当访问对象的属性或方法时,会优先使用对象自身上的成员属性和成员方法。如果没
       有找到,则使用 __proto__上面的原型属性和原型方法。如果依然没有继续沿原型链
       查找,最后返回Undefined。
     
    6.6   习惯上,我们会将属性写为成员属性,讲方法定义为原型方法:
        例如:
    function Person(){
        this.name = "张三"; // 成员属性
    }
    Person.prototype.say = function(){};
       原因:
       ① 原型属性在定义后不能改变,无法在实例化时进行赋值。所以属性不能使用原型
            属性。但是方法,写完以后基本不用改变,所以,方法可以使用原型方法。
       ② 实例化出对象后,属性全在对象上,方法全在原型上,结构清晰。
       ③ 使用for-in遍历对象时,会将属性和方法全部打印出来。而方法往往并不需要展
            示,那么将方法写在原型上,就可以使用hasOwnProperty将原型方法过滤掉。
       ④ 将方法写道prototype上,将更节省内存
       ⑤ 官方都这么写
     
    6.7   通过prototype扩展系统内置函数的原型方法
    /**
    * 给数组添加一个遍历打印所有值的方法
    */
    Array.prototype.eachAll = function(){
        for (var i=0;i<this.length;i++) {
            console.log(this[i]);
        }
    }
    var arr = [1,2,3,4,5,6,7];
    arr.eachAll();    
    7. for-in循环
    for-in 循环主要用于遍历对象
    for()中的格式:for(keys in zhangsan){}
     
    keys表示obj对象的每一个键值对的键!所以循环中,需要使用obj[keys]来取到每一个的值
     
    for-in循环,遍历时不只能读取对象自身上面的成员属性,也能沿原型链遍历出对象的原型属性。
    所以,可以使用hasOwnProperty判断一个属性是不是对象自身上的属性。
    zhangsan.hasOwnProperty(keys)==true 表示这个属性是对象的成员属性,而不是原型属性。
            function Person(){
                this.name = "张三";
                this.age = 14;
                this.func1 = function(){
    } } Person.prototype.name1
    = "zhangsan"; Person.prototype.age1 = 15; Person.prototype.func2 = function(){}; var zhangsan = new Person(); for(keys in zhangsan) { if (zhangsan.hasOwnProperty(keys)) { console.log(zhangsan[keys]); } }
    8. 继承

    8.1扩展Object实现继承

    8.1.1   扩展Object实现继承
          ① 声明父类
       function Parent(){}
              声明子类
       function Son(){}
     
       ② 通过prototype给Object类添加一个扩展方法:
        Object.prototype.extend = function(parent){
            for(var i in parent){
                this[i]=parent[i];
            }
        }
     
       ③ 分别拿到父类对象和子类对象:
       var p = new Parent();
       var s = new Son();
       ④ 用子类对象,调用扩展方法,实现继承操作:
       s.extend(p);
    8.1.2   实现继承的原理:
       通过循环将父类对象的所有属性和方法,全部赋给子类对象。关键点在于for-in循环,
       即使不扩展Object,也能通过简单的循环实现操作。
     
    8.1.3   扩展Object继承的缺点:
       ① 无法通过一次实例化,直接拿到完整的子类对象,而需要先拿到父类对象和子类对象两个对象,再手动合并。
       ② 扩展Object的继承方法,也会保留在子类的对象上。
     

    8.2使用原型实现继承

    8.2.1   使用原型实现继承
       ① 定义父类
       function Parent(){}
        定义子类
       function Son(){}
     
       ② 将父类对象,赋值给子类的prototype
       Son.prototype = new Parent();
       ③ 拿到子类对象,就会将父类对象的所有属性和方法,添加到__proto__
       var s = new Son();
    8.2.2   使用原型继承的原理:
       将父类对象,赋值给子类的prototype,那么父类对象的属性和方法就会出现在子类的
       prototype中,那么,实例化子类时,子类的prototype又会到子类对象的__proto__
       中,最 终,父类对象的属性和方法,会出现在子类对象的__proto__中。
     
    8.2.3   这种继承的特点:
       ① 子类自身的所有属性,都是成员属性,父类继承过来的属性,都是原型属性
       ② 依然无法通过一步实例化拿到完整的子类对象。
     

    8.3call/apply/bind实现继承

    [call/bind/apply]
    1、 三个函数的作用:通过函数名调用这三个函数,可以强行将函数中的this指定为某个对象
     
    2、 三个函数的写法(区别):
      call写法:func.call(func的this指定的obj,func参数1,func参数2,...);
      apply写法:func.call(func的this指定的obj,[func参数1,func参数2,...]);
      bind写法:func.call(func的this指定的obj)(func参数1,func参数2,...);
     
    3、 三个函数的唯一区别,在于接收func的参数列表的方式不同,除此之外,功能上没有任何差异!!!
     
    [使用call/bind/apply实现继承]
    1、 实现步骤:
      ① 定义父类
      function Parent(name){}
      ② 定义子类时,在子类中使用三个函数,调用父类,将父类函数中的this,指向位子类函数中的this:
        function Son(no,name){
            this.no = no;
            Parent.call(this,name);
        }
      ③ 实例化子类时,将自动继承父类属性
      var s = new Son(12,"张三");
    9. 闭包
     
    [JS中的作用域]
    1、 全局变量:函数外声明的变量,成为全局变量;
      局部变量:函数内部使用var声明的变量,称为局部变量;
     
      在JS中,只有函数作用域,没有块级作用域!!也就是说,if/for等有{}结构体,
      并不能具备自己的作用域
     
      所以,函数外部不能访问函数内部的局部变量(私有属性)。因为,函数内部的变量,在
      函数执行完毕以后,就会被释放掉。
     
    2、 使用闭包,可以访问函数的私有变量!
      JS中,提供了一种"闭包"的概念:在函数内部,定义一个子函数,子函数可以访问父
      函数的私有变量。可以在子函数中进行操作,最后将子函数通过return返回
           function func1(){
             var num = 1;
             function func2(){
                return num;
             }
             return func2;
          }
          var num = func1()();
    3、 闭包的作用:
      ① 可以在函数外部访问函数的私有变量;
      ② 让函数内部的变量,可以始终存在于内存中,不会在函数完成后释放掉


  • 相关阅读:
    VS2015复制VS2013的项目,编译报错
    Asp.Net MVC的几个文件上传方式
    一个基于Jquery的涂鸦插件
    Js 自定义日期格式的正则表达式验证
    无聊做的小游戏,斗牛.html
    MSSql Server 自定义导出
    Asp.Net Ambiguous match found 错误另一种解决方法
    韩天峰(Rango)推荐书目
    微信创建菜单报 must use utf-8 charset hint 错误
    InstallShield 2015 LimitedEdition VS2012 覆盖安装
  • 原文地址:https://www.cnblogs.com/sin0/p/7673872.html
Copyright © 2011-2022 走看看