logo头像

孙宇

javascript 面向对象:1

一、 面向对象:是一个基于对象的多范式的编程语言

多范式:编程风格
    面向过程的方法编程
    面向对象的方式编程
    函数式

二、 面向对象的基本概念

  • JS 是不是一个面向对象的语言?
    不是: 与传统面向对象的理论有矛盾. C#, JAVA
    是: JS 里面到处都是对象. 数组, 时间, 正则 … 和 DOM

    也可以像传统面向对象的语言那样用 NEW 的方式创建对象
    
  • JS 是一个基于对象的多范式的编程语言

    多范式: 编程风格

    面向过程的方式编程
    面向对象的方式编程
    函数式 ...
    
  • 函数式的一个特点: 递归与链式
    JQUERY 就是典型的链式编程风格
    比如: 给 DIV 标签添加样式
    $( 'DIV' ).CSS( 'BORDER', '1PX SOLID RED' )
                .CSS( 'WIDTH', '400PX' )
                .CSS( 'HEIGHT', '200PX' );
    
  • 面向对象的概念

    -> 面向: 将脸朝向 ... -> 关注, 用
        面向过程开发 -> 使用过程的方式开发
        面向对象开发 -> 使用对象开发
    -> 面向过程
        早上吃早饭: 吃面
        自己做: 和面 -> 压成细面 -> 烧水 -> 煮面 -> 盛面 -> 吃
        过程: 步骤, 细节, 顺序等
    -> 今天早上
        找到对象: 找到面馆 -> 要 -> 吃
    -> 面向对象: 
        要做什么, 就找到对应的对象, 告诉它做, 等结果
    -> 生活
        买菜: 菜场
        公交
        电话
        ...
    -> 是否说面向对象可以取代面向过程
        面向对象是面向过程的封装
    

    -> 万物皆对象

    在实际开发中, 对象是一个抽象的概念, 可以将其简单的理解为: 数据集或功能集.
    -> 数据集: 很多数据打包到一起. { NAME: '张三', AGE: 19, GENDER: '男' }
            假设展示 10 条商品数据. 每一个商品: 名字, 描述, 价格, 图片
            -> 
            每一条数据可以变成对象: { NAME:'', DESC: '', PRICE: 0, IMG: '' }
            -> 引入数组, 将数据存储到数组中
    -> 功能集(函数集, 方法集)
    -> 在 JS 中, 所谓的对象就是 键值对的 集合
    -> 也可以混合使用
        例如 JQUERY:
            $( 'DIV' )    这个是一个 JQ 对象, 但是实际上里面包含 DOM 对象
            $( 'DIV' ).CSS( ... )
             $( 'DIV' ).HTML( ... )    是不是就说明 JQ 对象中包含方法
    

    -> 抽象性

    商品列表: 书店, 书的列表
        书名, 页数, 简介, 编号, 作者            对象只包含最核心的主要信息
    游戏人物: 英雄
        名字, 血量, 防御, 攻击, 护甲, 武器        只需要描述出需要的数据即可
    

    -> 名词提炼法找对象

    做一个表格排序
        -> 过程
            1) 创建表格
            2) 添加点击事件
            3) 排序, 更新数据
        -> 对象
            1) 创建表格对象
            2) 添加数据
    

    -> 实际开发的时候如果需要完成一个功能

    -> 首先考虑系统是否提供了对象
        例如创建图片: VAR IMG = NEW IMAGE(); IMG.SRC = '...JPG';
        例如获取页面元素: DOCUMENT, 标签对象
        例如获得导航栏里面的所有的 LI 标签
            VAR NAV = DOCUMENT.GETELEMENTSBYID( 'NAV' );    // NAVIGATION
            VAR LIS = NAV.GETELEMENTSBYTAGNAME( 'LI' );
    -> 如果系统没有可以考虑自定义, 或第三方
        VAR ITCAST = {
            TAG: FUNCTION ( TAGNAME ) {
                RETURN DOCUMENT.GETELEMENTSBYTAGNAME( TAGNAME );
            }, ADDCSS: FUNCTION ( ARRAY, STYLES ) {
                FOR ( VAR I = 0; I < ARRAY.LENGTH; I++ ) {
                    FOR ( VAR K IN STYLES ) {
                        ARRAY[ I ].STYLE[ K ] = STYLES[ K ]; 
                    }
                }
            }, ID: FUNCTION () {},
            CNAME: FUNCTION () {},
            NAME: ...
            ATTR: ...
            CLASS: ...
            ...
        };
    
  1. 开发者工具的打开: F12

    火狐的 FIREBUG

  2. 调试按钮
    -> 逐语句: 单步运行. 就是一次只执行一句话. 如果遇到函数, 进入函数体内一步一步执行
    -> 逐过程: 一次执行一个过程. 如果是一条语句那么与逐语句是一样的. 但是如果是一个函

    数, 那么他就会将函数执行完, 然后在往下走, 停下来.
    

    -> 继续运行

  3. 条件断点
    就是只有在 条件满足的时候(表达式为真的时候) 才会停下来的断点

  4. 利用调试工具实现列表播放
    H5 提供了 两个标签, 一个是 AUDIO, 一个是 VIDEO
    AUDIO 主要是用来播放音乐的
    VIDEO 用来播放视频

    如果标签设置了属性

    AUTOPLAY
    SRC
    

    我希望当一个音乐播放完成后, 自动播放下一首

  5. 数据在内存中的存储形式(识记)
    计算机只认识数字
    计算机根据是否有电信号表示1或0: 准确的说是高低电平
    有了二进制数据( 有兴趣的同学可以学习如何转换 )

    计算机只认识数组, 那么如何表示语言
    ASCII 码

    0 ~ 127 数字 对应的 表示成 字母
    前 32 个 是控制字符
    从 32 开始 到 127 是可视化字符
    '0'            48
    'A'            97
    'A'            65
    

    有了这样一个对应关系(映射 MAP), 那么一段文字就可以使用一串数字表示了

    表示中文的 GB2312 编码, 简体中文编码. 英文标点和数字等使用一个字节

    中文使用两个字节.
    

    UNICODE 编码( 万国码 ). 所有的字符才偶用两个字节表示.

    例如表示字符 'A'    2 个字节
    在页面中使用最多的是: 数字, 字母, 标点符号
    

    UTF-8 编码( 通用转换格式 ). 所有与 ASCII 编码相符的字符采用一个字节.

    而汉字采用 3 个字节.
    
  6. 在 JS 中允许使用 UNICODE 编码表示字符串
    语法: \U + 4 位 16 进制数

    使用这种方式表示数据, 在表示 与 ASCII 码重合字符是没有意义的. 但是对于中文
    由于存在不同的编码, 而不同编码在不同平台上可以正常显示, 但是随着网络传播,
    不是在所有平台上可以通用. 因此数据传送中文一般采用 UNICODE 编码形式.

  7. 数字 + 上下文 = 信息
    97 ‘A’ 数字 97

    在此处不要深究, 只需要了解内存模型即可.

    内存就是一个可以存储数字( 数据 )的盒子.

  1. JS 的数据类型
    -> 基本类型( 值类型 ): 数字 NUMBER, 字符串 STRING, 布尔 BOOLEAN
    -> 复合类型( 引用类型 ): 对象( 数组, 时间类型, 函数类型, 正则表达式, … )
    -> 空类型: NULL, UNDEFINED
基本数据类型和空类型的存储模型就是一个方格里面放一个数据
复合类型, 除了函数, 其他的数据类型无法使用 TYPEOF 获得数据类型名
注意: 如果需要获得对象的数据类型, 需要使用 OBJECT.PROTOTYPE.TOSTRING.APPLY( 数据 )
  1. 复合类型的存储方式
    VAR P = { NAME: ‘ITCAST’ }

    复合类型的对象是一个单独的内存区域, 对象有什么属性, 那么内存区域中就有什么数据.
    变量 P 只存储该对象的 ‘地址’. 所以 P 不是真正存储数据的区域.

    绘制数据的内存逻辑图

    VAR ARR1 = [ 1, 2, 3, 4 ];
    VAR ARR2 = [

    { NAME: '张三', AGE: 19, GENDER: '男' },
    { NAME: '李四', AGE: 18, GENDER: '男' },
    { NAME: '小李', AGE: 17, GENDER: '女' }
    

    ];

  2. 值类型与引用类型的存储特征
    -> 值类型的数据, 只需要开辟一段内存存储数据即可

    VAR A = 123; 
    VAR B = 'ABC';
    VAR C = TRUE;
    

    -> 对象类型( 引用类型 ). 对象才是真正的数据, 需要占据单独的内存.

    而变量名只是存储着对象的内存地址( 引用 ).
    
    即创建一个对象并赋值, 实际上需要两块内存空间. 一个存储数据( 对象 ),
    另一个存储变量以引用对象.
    
    VAR O = { NAME: '张三', AGE: 19, GENDER: '男' };
    
  1. 值类型与引用类型的赋值与传参的特性
    -> 赋值: 将原变量中的数据拷贝一份, 然后存储到给定变量中

    -> 值类型
        VAR A = 123;    // 有一个盒子, 叫 A, 里面存储着数字 123
        VAR B;            // 又有一个盒子, 叫 B, 里面什么都没存( UNDEFINED )
        B = A;            // 将 A 中存储的东西复制一份, 然后赋值给 B. 即存储在 B 中
    
        B 和 A 是两个独立的变量, 两者之间不再有关系. 改变其中一个数据, 另一个不变
    -> 引用类型
        VAR O1 = { NUM: 123 };
        VAR O2;
        // 赋值
        O2 = O1;     // 将 O1 中存储的内容 拷贝一份 存储到 O2 中
                    // O1 中存储的是引用, 或 '地址'
    
        O1 和 O2 虽然是不相同的两个变量, 即两个独立的盒子. 但是由于存储的地址相同.
        在访问数据的时候, O1 与 O2 也是访问同一个数据, O1 将数据修改了, O2 再读取,
        读取的就是修改后的数据. 
    

    -> 函数参数传递

    -> 什么是函数参数传递
        函数要调用, 一般会给函数传递参数
            在调用函数的时候, 回将参数中表示的数据拷贝一份, 然后给参数赋值
        FUNCTION FOO( NUM ) {}
    
        // 调用
        VAR A = 123;
        FOO( A );        // 调用函数就是要执行函数
                        // 将 A 中存储的数据 拷贝一份
                        // 进入 函数 FOO
                        // 给参数赋值, 相当于 NUM = A
                        // 进入函数体, 开始执行函数
        此时的赋值特性与前面介绍的值类型引用类型的赋值是一个特点
    
  2. 深拷贝与浅拷贝
    -> 什么是拷贝: 就是创建一个与目标数据一模一样的数据
    -> 案例:

    VAR P = { NAME: '张三' };
    VAR P1 = P;                   // 是否存在拷贝
     // 一般描述拷贝是指拷贝对象
     P1 = {};
     P1.NAME = P.NAME;
     // 才是拷贝
    

    -> 案例, 给 对象 P 提供一个 CLONE 方法, 完成拷贝
    -> 案例:

    有一辆汽车 CAR: NAME=保时捷
    有一个人 PERSON : NAME=张三
    

    -> 如果对象的属性也是一个引用类型, 拷贝的时候不重新创建一个新的对象来实现该属性的拷贝, 那么就是浅拷贝.

    即任何不完全的拷贝都是浅拷贝
    

    -> 将两个对象完全从内存中隔离开, 就是深拷贝. 即每一个引用属性, 以及引用属性的引用属性, … 全部拷贝出来.

  3. 构造函数( 构造器 CONTROCTOR )的作用
    -> JS 中对象的动态特性

    即 想要什么属性就可以提供什么属性
    在 JS 中 对象如果没有指定的属性, 只需要利用赋值就可以给对象提供该属性.
    

    -> 点语法与关联数组语法

    O.NAME = 'JIM';                // 点语法赋值(NAME是一个名字)
    CONSOLE.LOG( O.NAME );        // 点语法取值
    O[ 'NAME' ] = 'TOM';        // +关联数组语法赋值(NAME一定要是字符串)
    O[AGE];                     //会出错  AGE不存在
    O[ 'NAME' ] = 'TOM';        //这里的NAME不能为对象
    
    CONSOLE.LOG( O[ 'NAME' ] ); // 关联数组语法取值
    
    问题:
        // 如果 NAME 是一个变量, 里面存储的是 字符串, 也是可以的
        O[ NAME ] = 'JACK';   // 对或不对?
    WINDOW.NAME 为空 ""    WINDOW里面有NAME
    

    -> 例创建一个 PERSON 对象

    VAR P = {};        // 什么都没有的对象
    // 根据需要添加成员
    P.NAME = '张三';
    P.AGE = 30;
    P.GENDER = '男';
    

    -> 简化: 提供一个创建 PERSON 对象的函数

    FUNCTION CREATEPERSON( NAME, AGE, GENDER ) {
        VAR P = {};
        P.NAME = NAME;
        P.AGE = AGE;
        P.GENDER = GENDER;
        RETURN P;
    }
    
    VAR P1 = CREATEPERSON( 'JIM', 19, '男' );
    VAR P2 = CREATEPERSON( 'LILY', 18, '女' );
    
    这个( 这种类型 )的函数就是用来创建对象的, 即生产对象. 常常将这类函数
    称为 '工厂函数'
    
  4. 构造方法创建对象
    -> 构造器中不需要 RETURN 语句. 一般也可以不写
    -> 调用构造器的时候, 使用 NEW 运算符引导
    -> 在构造器中 THIS 表示当前对象. 给对象提供成员使用 THIS.XXX 的 方式

    -> 将 CREATEPERSON 改造成构造器

    // 构造器的定义
    FUNCTION CREATEPERSON( NAME, AGE, GENDER ) {
        THIS.NAME = NAME;
        THIS.AGE = AGE;
        THIS.GENDER = GENDER;
    }
    // 调用构造器创建对象
    VAR P = NEW CREATEPERSON( '李磊', 19, '男' ); 
    当构造函数,同时出现RETUR和NEW的时候,看RETURN后面的值,
    如果为数值则返回THIS ,如果是引用类型,就返回引用类型。
    

    -> 构造器创建对象的本质: 还是使用对象的动态特性

    -> 首先执行 NEW 运算符. 即创建对象. 可以简单看做为 {} 的简写
        VAR P = NEW ...   '相当于'   VAR P = {}
    -> 调用构造器. 并隐含一个参数, 即刚刚创建的对象. 
    -> 在构造器中使用 THIS 引用刚刚创建出来的对象.
    -> 构造器结束是 默认返回 THIS
    
    本质还是利用对象的动态特性
    

    -> 补充说明

    -> 构造器的名字, 一般使用 PASCAL 命名规则( 首字母大写的 )
    -> 一般为了与其他面向对象的编程语言( C++, C#, JAVA )保持一致. 称构造函数名为类名
    
    FUNCTION PERSON( NAME, AGE, GENDER ) {
        THIS.NAME = NAME;
        THIS.AGE = AGE;
        THIS.GENDER = GENDER;
    }
    // 调用构造器创建对象
    VAR P = NEW PERSON( '李磊', 19, '男' ); 
    
  5. 异常与捕获
    -> 异常

    简单的说, 就是代码在执行过程中出现的错误. 并不是代码的语法错误.
    

    -> 一旦出现异常, 其后的代码, 不再执行

    -> TRY-CATCH 语法

    1) TRY-CATCH 形态
        TRY {
            代码
        } CATCH ( E ) {
            代码
        }
    2) TRY-CATCH-FINALLY 形态
        TRY {
            代码
        } CATCH ( E ) {
            代码
        } FINALLY {
            代码
        } (无论代码出错还是没有出错都要执行)
    

    -> 自定义抛出异常

    一般可以封装函数完成特定的功能. 例如 TAG 函数
    FUNCTION TAG ( TAGNAME ) {
        IF ( TAGNAME 不是字符串 ) 抛出异常. 
        RETURN DOCUMENT.GETELEMENTSBYTAGNAME ( TAGNAME );
    }
    

    -> 抛出异常的语法

    THROW 对象
    
  6. DOM 的核心内容
    -> 什么是 DOM, 为什么需要 DOM

    <DIV><DIV></DIV><DIV></DIV></DIV>
    

    -> DOM 操作操作的是什么?

    -> 访问各亲属节点
    -> 增删改查
    

    -> 学会分析 DOM 树

    <DIV>
        <P>你好, 我是一个 <SPAN STYLE="COLOR: RED">DOM</SPAN> 树的练习</P>
    </DIV>
    
    在 HTML 文件结构中吗所有的内容都是节点(NODE)对象. 有的是文本节点.
    有的是标签( 元素 ELEMENT )对象. 还有的是属性节点( ATTRIBUTE NODE )对象.
    
  7. 访问各个亲属节点
    -> 节点对象的常用属性

    <NODE>.NODETYPE
        元素(标签): 1, 
        属性: 2, 
        文本: 3
    <NODE>.NODENAME
        元素(标签): 大写的标签名, 
        属性: 属性名
        文本: #TEXT
    <NODE>.NODEVALUE
        元素(标签): NULL
        属性: 属性赋值等号后面的值
        文本: 文本字符串
    
  8. 操作 DOM 就是创建元素, 插入元素, 修改元素, 查询元素, 删除元素
    -> 增加

    // 创建
    DOCUMENT.CREATEELEMENT( '元素名' )            创建元素标签
    DOCUMENT.CREATETEXTNODE( '文本内容' )        创建文本节点
    // 插入
    <PARENT>.APPENDCHILD( 子元素 )                追加到子元素的结尾
    <PARENT>.INSERTBEFORE( 新元素, 旧元素 )        将新元素插入到旧元素的前面
    
    // 简单的办法
    <ELEMENT>.INNERHTML = ...
    <ELEMENT>.INNERTEXT = ...
    
    // 增加属性
    <ELEMENT>.属性名 = ...
    <ELEMENT>.SETATTRIBUTE( 属性名, 值 );
    <ELEMENT>.STYLE.XXXX = ...
    

    -> 删除

    <PARENT>.REMOVECHILD( 子元素 );
    <ELEMENT>.SETATTRIBUTE( 属性, '' )
    <ELEMENT>.XXXX = NULL
    

    -> 修改

    -> 修改属性
        <ELEMENT>.XXX = ...
        <ELEMENT>.SETATTRIBUTE( 属性名, ... )
    -> 修改节点
    

    -> 查询

  9. 手动创建一个 TABLE 表格, 并且在里面显示数据
    VAR ARR = [

    { NAME: 'JIM1', AGE: 19, GENDER: '男' },
    { NAME: 'JIM2', AGE: 18, GENDER: '男' },
    { NAME: 'JIM3', AGE: 20, GENDER: '男' },
    { NAME: 'JIM4', AGE: 18, GENDER: '男' }
    

    ];

#

##