javascript中级

张东

zhangdong@tedu.cn

1. ***数组:

   什么是:

   为什么:

   何时:

   如何: 创建,访问元素,遍历

 

1. ***数组:

   什么是: 内存中连续存储多个数据的一块存储空间

      vs 变量:  内存中存储一个数据的存储空间

   为什么: ***程序=数据结构+算法

                算法: 解决问题的步骤

                数据结构: 数据在内存中的存储结构

                    好的数据结构可以极大提高程序的执行效率

   何时: 今后,只要连续存储多个相关的数据,都要用数组

   如何: 创建,访问元素

      创建: 3个场景:

         1. 创建空数组: var 变量=[];

                                             =new Array();

             何时: 在创建数组时,暂时不知道数组中的元素内容

         2. 创建数组时,初始化数组内容:

                                var 变量=[值1,值2,...];

                                             =new Array(值1,值2,...);

             何时: 在创建数组时,已经知道元素的内容

             强调: 每个元素之间,必须用逗号分隔

         3. 创建n个空元素的数组: var 变量=new Array(n);

             何时: 在创建数组时,仅知道元素的个数,暂时不知道元素的内容。

 

    访问数组中的元素:

       下标: 数组中唯一标识每个元素存储位置的序号

              默认从0开始,连续不重复

       arr[i]: 访问arr数组中下标为i位置的元素值

          数组中每个元素的用法和普通变量完全一样。

          其实,数组也是一组变量的集合,再起一个统一的变量名

 

    三个不限制:

       1. 不限制元素的数据类型

       2. 不限制元素的个数

       3. 不限制下标越界:

              赋值: 如果下标越界,不报错!而是在指定位置自动添加新元素。——稀疏数组: 下标不连续的数组

              取值: 如果下标越界,不报错!而是返回undefined

 

    length属性: 记录理论上数组的元素个数

                 length永远是最大下标+1

       固定套路:

          1. 最后一个元素: arr[arr.length-1]

          2. 倒数第n个元素: arr[arr.length-n]

          3. 在末尾追加新元素: arr[arr.length]=值

          4. 清空数组: arr.length=0;

          5. 删除末尾最后一个元素: arr.length--;

          6. 删除末尾的n个元素: arr.length-=n;

 

    遍历: 依次访问数组中每个元素:

       for(var i=0;i<arr.length;i++){

           arr[i]//当前元素

       }

       何时: 今后只要对数组中每个元素执行相同的操作时

 

***数组是引用类型的对象:

    数据类型: 基础类型: 值直接存储在变量本地的数据类型

                    引用类型: 值无法保存在变量本地的数据类型

                        数据实际存储在变量外的一个独立存储空间

                            同时存储多个数据的一块存储空间-对象

                            每个对象都有唯一的内存地址

                        变量中仅保存对象的地址值!

                        访问变量等效于通过地址值找到具体对象去访问

    数组就是引用类型的对象

 

***按值传递:

    两变量间赋值,或将变量作为参数传入函数时,其实仅将原变量中值,复制一个副本给对方变量:

       对基础类型: 修改新变量的值,不影响原变量;

       对引用类型: 仅复制对象的地址给对方,不创建新对象

                          通过新变量修改对象,等效于修改原对象

 

null vs undefined

  undefined: 空, 专门用于程序自动给变量初始化空值

  null: 空, 专门用于程序员主动释放对一个对象的引用

     垃圾回收: 内存中引擎会自动释放不再被任何变量引用的对象

     垃圾回收器: 在内存中自动执行的小程序

                        自动释放不被任何变量引用的对象

     好的习惯: 只要一个对象不再使用,都要主动将变量置为null

 

 

 

 

正课:

1. ***数组:

   API:

     拼接和选取

     修改

     翻转

****排序: 自定义排序算法: 冒泡排序

              sort()

 

1. 拼接和选取:

   拼接: 将其它元素或其它数组拼接到当前数组末尾,返回新数组

      var newArr=arr1.concat(值1,值2,arr2,......)

      强调: 1. 无权修改原对象,只能返回新对象

               2. 打散传入的数组参数——珍贵

   选取: 复制原数组中指定位置的元素组成新数组

      var subArr=arr1.slice(starti,endi+1);

      强调: 1. 无权修改原对象,只能返回新对象

               2. 规律: 所有两个参数都是下标的API,都含头不含尾

      简写: 1. 省略第二个参数: arr1.slice(starti)

                      从starti位置一直选取到结尾

               2. 不写参数: arr1.slice()——复制整个数组中所有元素

               3. 如果离结尾近:

                      arr1.slice(arr1.length-n,arr1.length-m+1)

                      选择倒数第n到倒数第m的元素

                      arr1.slice(-n,-m+1);

 

2. 修改数组splice: 删除,插入,替换

    删除: arr.splice(starti,n) 从arr中starti开始,删除n个元素

        强调: 1. 不遵循含头不含尾

                 2. 直接修改原数组

                 3. starti也支持负数参数,表示倒数第n个位置

        简写: 省略第二个参数: arr.splice(starti) 删除starti后所有

        返回值: var deletes=arr.splice(starti,n)

              返回被删除的元素组成的临时新数组

    插入: arr.splice(starti,0,值1,值2,...)

              在arr中starti位置插入: 值1,值2,...

              原starti位置及其之后的值,向后顺移

        强调: 不能打散数组参数

    替换: arr.splice(starti,n,值1,值2,...)

              在arr中先删除starti位置后的n个元素

                         再在starti位置插入新元素

        强调: 删除的元素个数和插入的新元素个数不必一样

                数组自动调整length

3. 翻转数组: arr.reverse();

4. ****排序:

    自定义排序算法: 冒泡,快速,插入

       冒泡: 原理:

 

 

 

 

 

 

正课:

1. ***数组:

   排序:

   栈和队列:

   二维数组:

2. ***String

 

1. 排序:

    自定义排序: 冒泡

    排序API: arr.sort();

       大问题: 默认将所有元素转为字符串再按字符串排列

                  只能对字符串类型的元素正确排序

       解决: 自定义比较规则:

         比较器函数: 专门比较任意两值大小的函数:

                   要求: 两个参数: a,b

                            返回值: 如果a>b,就返回正数

                                        如果a<b,就返回负数

                                        如果a=b,就返回0

         最简单的数字比较器函数:

           function compare(a,b){return a-b;}

         如何使用: 将比较器函数名作为参数传入sort函数中

           arr.sort(compare) //强调: 不要加()

           compare函数作为参数传入sort中,被sort反复调用

         降序: 颠倒比较器函数的正负号,可改升序为降序

           最简单的数字降序比较器函数:

              function compare(a,b){return b-a;}

 

2. 栈和队列: js中没有专门的栈和队列类型

                   一切栈和队列都是用普通数组模拟的

   栈: 一端封闭,只能从另一端进出的数组

    特点: FILO

   何时: 今后只要希望始终使用最新的元素时

   如何:

      1. 从结尾出入栈:

          入: arr.push(值) => arr[arr.length]=值

          出: var last=arr.pop()

          特点: 每次出入栈,都不影响其他元素的位置

      2. 从开头出入栈:

          入: arr.unshift(值)

          出: var first=arr.shift()

          特点: 每次出入栈,其它元素位置都会依次顺移

        

   队列: 只能从一端进入,从另一端出的数组

     特点: FIFO

   何时: 只要希望按先后顺序使用数组中的元素时

     1. 结尾入: 入: arr.push(值)

     2. 开头出: var first=arr.shift()

       

3. 二维数组:

  什么是: 数组中的元素又引用了另一个子数组

  何时: 1. 保存横行竖列的二维数据结构

           2. 对一组数据,再进行细致分类时

  如何:

     创建: 2种:

        1. 创建数组时,还不知道子数组的内容:

            var arr=[];

            arr[0]=[值1,值2,...];

            arr[1]=[值1,值2,...];

        2. 创建数组同时初始化元素:

             var arr=[

                   [值1,值2,...],

                   [值1,值2,...],

                   ...

             ]

     访问: arr[r][c]->用法和普通数组元素的用法完全一样

         强调: 二维数组,行下标r不能越界!——报错!

     遍历: 外层循环控制行,内层循环控制列

        for(var r=0; r<arr.length; r++){//遍历每一行

            for(var c=0;c<arr[r].length;c++){//遍历第r行中每一列

             arr[r][c]//当前元素

            }

        }

 

     练习: Math.random() 生成0~1之间的一个随机小数

             

 

 

正课:

1. ***数组:

    关联数组:

    API:

      转字符串

      拼接和选取

      修改

      翻转

 

1. 关联数组:

   索引数组: 下标为数字的数组

   问题: 每个元素,只有值,没有有意义的名称

   解决: 关联数组: 可自定义下标名称的数组

   如何:

      创建: 2步: 1. 先创建空数组: var scores=[]

                       2. 在空数组中添加新元素,使用自定义的下标名

                              scores["MATH"]=81;

      访问元素: 用自定义的下标名称:

            scores["MATH"] 用法和普通变量完全一样

   关联数组原理:

       hash算法: 散列: 接收一个字符串,然后根据字符串计算出一个尽量不重复的数字。

           相同字符串,计算出的数字一定相同

           不同的字符串,计算出的数字几乎不同的

       关联数组: hash数组

          存入元素: 将下标名交给hash算法,计算出一个尽量不重复的序号,将元素存储到数组中序号指定的位置

          获取元素: 将下标名交给hash算法,计算出和存入时完全一样的序号,直接去数组中序号位置获取元素值——不用遍历

    为什么: 1. 因为下标有意义,便于维护

                 2. 查找极快——和总元素个数以及存储位置无关!

    何时: 1. 今后只要快速查找某个元素时,都用hash数组

             2. 今后只要存储有意义的一组元素时,都用hash数组

    鄙视: 谈谈对hash的理解:

      hash算法; 存入;  获取;   优;

 

   特点: length属性失效,永远为0

   遍历: for in循环:

      for(var key in hash){//in依次获得每个元素的下标名称

         key//自动获得当前元素的下标名

         hash[key]//当前元素值

      }

 

2. 数组API: 浏览器已经实现的,我们直接用的函数

    数组对象: 存储一组数据,提供操作数据的API

    1. 数组转为字符串: 2种:

        1. var str=String(arr): 将arr中每个元素转为字符串,再用逗号链接成一个字符串。

        2. 可自定义连接符:

            var str=arr.join("自定义连接符")

            固定套路: 1. 无缝拼接: arr.join("")

                            2. 判断数组是空数组: arr.join("")===""

                            3. 动态生成页面元素的内容

 

 

 

 

 

 

 

 

 

正课:

1. ***String

   什么是:

***内置对象:

***包装类型:

   字符串API

 

1. 什么是: 多个字符组成的只读字符数组

      vs 数组: 下标i

                   length

                   slice()  concat

      不同: 数组中凡是直接修改原数组的API,字符串都不能用!

 

2. 内置对象: ES标准中规定的,浏览器厂商已经实现的现成的对象和API

    11个: Number  String  Boolean

              Array Date RegExp Math

              Error

              Function  Object

              Global(浏览器中被替换为window)

3. 包装类型对象:

    什么是: 专门封装基础类型的值,并提供操作基础类型值的API的对象

    为什么: 基础类型的值,本身不包含任何API功能

    何时: 只要试图对基础类型的值调用API时,都会自动创建对应类型的包装类型对象来封装基础类型的值。

    调用后: 包装类型对象,自动释放!

       比如: var n=345.678;

                n.toFixed(2)=>345.678.toFixed(2)

                                    =>new Number(345.678).toFixed(2)

4. String的API:

***所有字符串API都无权修改原字符串,只能返回新字符串!

   大小写转换: 将字符串中所有英文字母转为统一的大小写

      何时: 只要不区分大小写时,都要先转为一致的大小写,再判断。 比如: 用户名,邮箱地址,验证码

      如何: str.toUpperCase() //都转大写

               str.toLowerCase() //都转小写

 

   获得指定位置的字符: str[i]

       var char=str.charAt(i);

       获得指定字符的unicode号:

       var unicode=str.charCodeAt(i); //省略i,默认是0

           将unicode号反向转回文字

           var char=String.fromCharCode(unicode);

 

    选取子字符串: str.slice(starti,endi+1)

       str.substring(starti,endi+1) 不支持负数参数

       str.substr(starti,n): 选取starti开始的n个元素

 

 

 

 

 

 

正课:

1. ***String API

   查找关键词

   替换

   切割字符串

2. *****正则表达式

 

1. 查找关键词: 4种

   1. 查找一个固定的关键词出现的位置:

       var i=str.indexOf("关键词",fromi)

         在str中,从fromi位置开始查找"关键词"的位置

         如果找到,返回关键词所在位置的下标

                找不到,返回-1

         简写: 省略fromi,默认从0开始

       专门找最后一个关键词的位置:

       var i=str.lastIndexOf("关键词")

         在str中,找最后一个关键词出现的位置

 

      问题: 只能找第一个关键词

      解决: 正则表达式:

 

   2. 使用正则表达式查找指定的一类关键词的位置:

       按模式匹配:

       var i=str.search(/正则表达式/);

           在str中查找第一个符合正则表达式要求的关键词的位置

           返回值: 找到的关键词的下标, 如果找不到返回-1

       何时: 仅判断是否包含敏感词时,就用search

                如果返回不是-1,说明包含,否则说明没找到

       忽略大小写: /正则/i

       问题: 1. 只能获得第一个的位置,不能获得所有敏感词

                2. 只能返回位置,不能返回内容

    3. 使用正则表达式查找指定的一类关键词的内容:

        var arr=str.match(/正则/ig);

        默认只找第一个,找所有,必须加g

        返回值: 所有敏感词组成的数组

               没找到返回null!

           强调: 如果一个API有可能返回null,就必须先判断不是null,再使用!

        arr.length 表示找到的关键词个数

        问题: 仅返回所有关键词的内容,无法返回每个关键词位置

    4. 即找所有关键词内容,又找每个关键词的位置?

        reg.exec();

 

2. 替换: 将字符串中所有敏感词替换为新内容

    基本替换:

    str=str.replace(/正则/ig,“替换值”);

   

 

4. *****正则表达式:

   什么是: 专门定义一类字符串统一规则的表达式

   何时: 1. 按照指定规则模糊查找一类关键词时

            2. 表单中验证输入项的格式

   如何: 语法:  

      1. 最简单的正则其实就是关键词原文

      2. 字符集: 规定字符串中一位字符可用的备选字符列表

         何时: 只要某一位字符,有多个备选字时

         如何: [备选字符列表]

           强调: 一个字符集只能匹配一位字符

           简写: 如果备选字符列表是连续的,就可用-省略中间字符

              一位字母: [a-zA-Z]

              一位数字: [0-9]

              一位汉字: [\u4e00-\u9fa5]

         特殊: 除了: [^排除的字符列表]

                    强调: ^必须写在[开头]

     3. 预定义字符集: 4个:

          \w  一位字母数字或_  =>[a-zA-Z0-9_]

          \d  一位数字  => [0-9]

          \s  一位空字符: 空格,Tab,...

          .    一位任意字符

        强调: 一个预定义字符集仅匹配一位字符

                 只有规则和预定义字符完全一致时,才能使用

                      如果不一致, 依然需要手写普通字符集

         字符集仅控制每个字符的内容

     4. 量词: 专门固定字符出现的次数

         有明确数量边界:

           字符集{min,max}   规定字符集必须最少出现min次

                                                                 最多max次

           字符集{min,}   最少min次, 多了不限

           字符集{n}        必须n次

         没有明确数量边界:

           字符集?     可有可无,最多一次

           字符集*     可有可无,多了不限

           字符集+    {1,}

         强调: 仅修改相邻的前一个字符集

     5. 选择和分组:

         分组: 将多个字符集分成一组:

             何时: 如果希望一个量词同时修饰多个字符集时

            比如: 我(了个?)?去: 我去   我了去   我了个去    我个去X

         regexper.com

         选择: 其实就是"或"   规则1|规则2

            只要匹配任意一个规则即可

            (微|w(ei)?)\s*(信|x(in)?)

      

     手机号:

         (\+86|0086)?      +86或0086  可有可无,最多一次

         \s*                        空字符 可有可无,多了不限

         1

         [34578]               34578中挑一个

         \d{9}                    9位数字

         (\+86|0086)?\s*1[34578]\d{9}

     邮箱:

         字母/数字或_   一次以上

         @

         字母或数字      2位以上

         (.和 字母或数字      2到3位)   1到2次

         \w+@[a-zA-Z0-9]{2,}(.[a-zA-Z0-9]{2,3}){1,2}

        

 

正课:

1. 正则:

 指定匹配位置

2. ***String API:

    替换: 衍生: 删除和格式化

    切割

3. ***RegExp对象

 

1. 正则:

    指定匹配位置: 三个位置:

      字符串的开头  ^

      字符串的结尾  $

         比如: 开头的空字符: ^\s+

                  结尾的空字符: \s+$

                  开头或结尾的空字符^\s+|\s+$

         固定套路: 只要希望字符串和正则从头到尾完全匹配

                         比如同时前加^后加$

                只要用正则表达式执行验证时,必须前加^后加$

      单词边界        \b  包含: ^  $   空格    标点

         比如: 单词首字母: \b[a-z]

                  单词尾字母: [a-z]\b

                  单独的一个单词no: \bno\b

 

2. ***StringAPI

  替换: 简单替换: str=str.replace(/正则/ig, "替换值");

           问题: 不能根据不同的关键词,选择不同的值替换

     解决: 高级替换:

      str=str.replace(/正则/ig,function(kw){

          //kw会自动获得本次找到的关键词内容

          return //根据不同kw,返回不同的替换值

      })

     何时: 只要根据不同的关键词,替换不同内容时

  衍生:

    删除: 将关键词替换为""

    格式化: 将原字符串重新拼接为新的格式

      比如: "19831226" => "1983年12月26日"

      2步: 1. 正则将原字符串分组

                 /(\d{4})(\d{2})(\d{2})/

               //    1         2         3

             2. 使用简单替换: str.replace(/正则/,"...$n...")

                  $n可自动获得第n个分组的子内容

                    n从1开始

  切割: 将原字符串,按指定字符,分隔为多个子字符串

      如何: var substrs=str.split(/正则/)

         返回切割后的多个子字符串组成的数组

                结果中,不再包含分隔符

      固定套路: 将字符串打散成字符数组: var chars=str.split("")

 

3. ***RegExp:

    什么是: 封装一条正则表达式, 提供了使用正则表达式进行查找和验证的API

    何时: 1. 查询关键词的第四种情况: 即查内容,又查位置

             2. 利用正则表达式执行验证

    如何:

       创建: 2种:

          1. 如果正则表达式是固定不变的: var reg=/正则/ig;

               强调: /正则/中正则中的/都要转义为\/

          2. 如果正则表达式是动态生成的:

               var reg=new RegExp("正则"[,"ig"]);

               强调: "正则"中的" \ 都要转义为\"  \\

                 比如: "\d{6}" => "\\d{6}"

                

 

 

 

 

 

 

 

 

 

正课:

1. ***RegExp:

2. Math

3. ***Date

 

1. ***RegExp

   API:

     验证: 检查字符串是否完全符合正则表达式的要求!

      如何: var bool=reg.test(待检测字符串)

         强调: 只要验证,reg中必须前加^后加$

    

     查找关键词: 第四种情况: 即找内容,又找位置

      如何: var arr=reg.exec(待查找的完整字符串)

              在"待查找的完整字符串"中,依次查找每个符合reg要求得关键词。

          返回值: 本次找到的一个关键词及其位置

             arr[0]: 关键词内容

                如果正则中有分组

                arr[n]: 自动保存第n个分组匹配的子内容

             arr["index"]: 当前关键词位置 -> 可简写为arr.index

             如果没找到,返回null

          每次查找后,都将reg.lastIndex属性(下次开始位置)修改为当前index+关键词长度,相当跳过当前关键词继续向后找

  

      固定套路: 找所有关键词:

         while((arr=reg.exec(str))!=null){

              arr[0] 关键词内容

                 arr[n]  自动获得第n个分组的子内容

              arr.index 当前关键词位置

         }

         如果只需要分组的子字符串,不需要完整关键词:

            可省略arr,用RegExp.$n

         while(reg.exec(str)!=null){

               RegExp.$n   自动获得第n个分组的子内容

         }

 

      练习:

       贪婪模式: 正则表达式默认匹配最长的符合条件的子字符串

              默认使用贪婪模式

              .*    .+

       懒惰模式: 仅匹配最短的符合条件的子字符串

          贪婪->懒惰:  .*?   .+?

 

2. Math:

   什么是: 专门封装数学计算所用常量,并提供数学计算所用API

   何时: 只要数学计算时

   特点: 不能new!

   API:

     1. 取整:

         Math.ceil(num) 上取整: 只要超过,就取下一个整数

         Math.floor(num) 下取整: 省略小数部分

         Math.round(num) 四舍五入取整:

         vs toFixed(d):

           1. 小数位数: Math.round()只能取整,不能规定小数位数

                                    toFixed(d)可取整,也可规定小数位数

           2. 返回值: Math.round()返回number

                           toFixed(d)返回string

         自定义round函数:

    2. 乘方和开平方:

      乘方: Math.pow(底数, 幂)

      开平方: Math.sqrt(n)

    3. 最大值和最小值:

      Math.max(值1,值2,...);

      Math.min(值1,值2,...);

        问题: 不支持数组

        解决: Math.max.apply(null,arr)

    4. 随机数:

        Math.random()  0<=r<1 随机小数

        从min~max之间取随机整数:

        Math.floor(Math.random()*(max-min+1)+min)

        从0~n之间去随机:

        Math.floor(Math.random()*(n+1));

 

3. ***Date

   什么是: 封装一个时间,提供操作时间的API

   何时: 只要存储时间,都要用Date对象

   如何:

      创建: 4种:

      1. 创建日期对象,并自动获得当前客户端系统时间

         var now=new Date();

      2. 创建日期对象,并封装自定义时间:   

         var date=new Date("yyyy/MM/dd hh:mm:ss");

         var date=new Date(yyyy,MM-1,dd,hh,mm,ss)

      3. 复制一个日期对象:

         问题: 日期对象的计算都是直接修改原日期对象

                      计算后无法保留计算前的旧时间

         解决: 今后如果需要同时保留开始和结束时间时

                     都要先将开始时间复制一个副本,再用副本计算

         var date1=new Date(...);

         var date2=new Date(date1);

      4. 用毫秒数创建日期对象:

         Date对象的原理:

            Date对象中保存了一个巨大的毫秒数

               起始时间为: 1970年1月1日0点至今的毫秒数

         var date=new Date(ms);

         两个日期对象可相减: 得到毫秒差

 

 

 

 

 

 

 

 

 

 

正课:

1. ***日期API

2. ***Error

 

1. ***日期API

   单位:  FullYear   Month     Date          Day

             Hours      Minutes   Seconds    Milliseconds

   API: 1. 每个单位都有一个对儿get/set方法

            比如: var year=date.getFullYear()//获取单位的值    

                     date.setFullYear(year)//设置单位的值

            特殊: Day没有set方法

          2. 命名: 年月日星期没有s结尾

                      时分秒毫秒都有s结尾

          3. 取值范围: 只有Date从1~31

                   不考虑FullYear, 其余都是从0开始,到进制-1结束

               Month: 0~11 总比现实中小1, 需要修正

               Date: 1~31   不用修正

               Day: 0~6      不用修正

               Hours: 0~23 不用修正

               Minutes/Seconds: 0~59 不用修正

   日期计算:

      1. 计算两个日期的时间差: 两日期相减,得毫秒数,再换算

      2. 对任意单位做加减: 3步:

           1. 取分量: var d=date.getDate();

           2. 做加减: d+=60

           3. 放回去: date.setDate(d);

                强调: 所有set方法可自动调整时间进制

          其实可简写为: date.setDate(date.getDate()+60)

   转字符串:

       date.toString() -> 当地时间的完整时间格式

       date.toLocaleString() ->当地时间的简化版格式

       date.toLocaleDateString() -> 当地时间的日期部分

       date.toLocaleTimeString() -> 当地时间的时间部分

       date.toGMTString() -> 标准时区的标准时间

 

   作业: 自定义format函数: 2_format.html

 

2. ***Error

   什么是错误(bug): 程序执行过程中遇到的异常中断。

          一旦发生错误,程序立刻退出。

   什么是错误处理: 即使程序发生错误,也能保证程序不异常中断的一种机制。

   如何:

       try{

           可能发生错误的代码

       }catch(err){//仅在发生错误时,才执行

           错误处理代码: 1. 提示用户错误信息(String(err))

                                  2. 记录系统日志

       }finally{

           无论是否发生错误,都必须执行的代码。

           比如: 释放资源!

       }

  err: Error对象: 在错误发生时,自动创建的,保存错误信息的对象。

    错误类型6种:

        SyntaxError   语法错误

        ReferenceError   要使用的变量没找到

        TypeError   错误的调用了对象的方法

        RangeError  参数范围越界 比如: toFixed(d) 0~20

 

        EvalError   URIError

 

 

 

 

 

 

 

 

 

 

 

 

 

正课:

1. ***错误处理

2. ***Function

   *****闭包

 

1. ***错误处理

    只要可以提前预料的错误,都要用if...else...来代替try catch

    只有无法提前预料的错误,采用try catch

 

    主动抛出错误:

    为什么: 抛出错误通常是为了提醒使用者错误的使用的程序

    如何: throw new Error("错误消息")

 

2. ***Function:

    什么是: js中一切函数都是对象

                函数对象是专门封装函数定义的对象。

    创建: 3种:

      1. 声明: function 函数名(参数列表){函数体; return 返回值;}

          何时: 只要一段代码被反复使用,都要先定义在一个专门的函数中,再反复调用函数即可——复用

             何时使用参数: 只要函数步骤中必须某些数据才能正常执行时,就要定义参数。

             何时使用返回值: 如果函数的调用者需要函数的执行结果时,函数就必须返回值。

         可被声明提前:

      2. 函数直接量:

       var  函数名=function(参数列表){函数体; return 返回值;};

         不会被声明提前。

****声明提前(hoist): 在开始执行程序前,将所有var声明的变量和function声明的函数提前到*当前作用域*的顶部,集中创建。

          赋值留在原地!

       何时: 只要不希望被声明提前时。

        揭示了: 函数名仅是一个普通的变量

                    函数定义其实是一个对象

                    函数名中仅保存了函数对象的地址——引用

 

      3. 用new:

var fun=

     new Function("参数1","参数2",...,"函数体; return 返回值")

 

     比如: function compare(a,b){return a-b;}

              var compare=function(a,b){return a-b;}

              var compare=new Function("a","b","return a-b;");

 

***重载(overload):

  什么是: 相同函数名,不同参数列表的多个函数,在调用时,可根据传入参数的不同,自动选择对应的函数调用!

   为什么: 减轻调用者的负担,一个函数名,可执行多种操作

   何时: 一项任务,根据不同的参数,执行不同的操作流程时

   如何:   js语法不支持重载效果

      变通:  所有函数对象内,都自动内建了一个arguments对象

         arguments对象:

            专门保存传入函数的所有参数值的类数组对象

             类数组对象: (object like array)

                vs 数组: 相同: 下标, length, for遍历

                             不同: 类数组对象是Object,不是Array,无法使用Array的API

                                     数组是Array类型,可以使用数组类型所有的API

 

    匿名函数:

    什么是: 函数创建时,不被任何变量引用的函数

    为什么: 节约内存

    何时: 如果一个函数只用一次,用完希望自动释放时

        1. 回调callback: 将函数作为参数传递给另一个函数去调用 

            比如: arr.sort(function (a,b){return a-b});

                     str.replace(/reg/g,function(kw,$1,...){return ...})

        2. 自调: 创建函数后立刻调用自己!

            何时: 如果一个函数只执行一次,不会再重用时

            为什么: 建立临时作用域!避免全局污染!

            如何:

               (function(参数列表){函数体; return 返回值})();

 

 

 

 

 

 

 

 

 

 

正课:

1. *****作用域和作用域链

2. *****闭包

 

1. *****作用域和作用域链

   作用域scope:

     什么是: 一个变量的使用范围——使用

                 本质上作用域是一个对象——存储

                     作用域中的变量都是对象的成员

     程序/函数的执行过程:

        1. 开始执行程序前:

            创建ECS(执行环境栈):

                依次保存每个调用的函数的执行环境

            在ECS中压入第一个全局执行环境(全局EC)

            创建window对象,全局EC引用window对象

            window就是全局作用域对象

        2. 开始执行程序:

            所有全局变量都保存在全局作用域对象window中

        3. 定义函数时:

            在全局添加函数名变量

            创建函数对象封装函数定义

            函数名变量引用函数对象

            函数对象中有一个scope属性,引用回创建函数时的作用域对象。通常都是window。

        4. 调用函数时:

            在ECS中压入一个新的EC

            为本次函数调用创建专门的活动对象(AO)

            在AO中创建所有函数定义中规定的局部变量

            其实AO就是函数作用域对象

                 所有局部变量都是AO的成员

            新的EC引用活动对象AO

            AO的parent指向window

            变量的使用顺序:

               先用AO(函数作用域)中的局部变量

               如果AO中没有,才去window(全局作用域)中找

        5. 函数调用后:

            本次函数调用的EC出栈

                导致函数作用域对象AO释放

                       导致局部变量一同释放

   作用域链(scope chain): 由多个作用域对象连续引用形成的链式结构。

      顺序: 先函数作用域对象AO->全局作用域对象window

      所有的变量都保存在作用域链上的对象中

          局部变量都保存在函数作用域对象AO中

          全局变量都保存在全局作用域对象window中

      控制了: 变量的使用顺序

        先用AO(函数作用域)中的局部变量

               如果AO中没有,才去window(全局作用域)中找

 

   闭包:

     什么是: 即重用变量,又防止变量被污染的一种机制

     为什么: 全局变量: 优: 可重用     缺: 易被全局污染

                 局部变量: 优: 不会被污染    缺: 不可重用

     何时: 即重用变量,又防止变量被污染

     如何: 3步:

        1. 用外层函数包裹住受保护的变量和操作变量的内层函数

        2. 外层函数将内层函数返回到外部,被外部的变量保存

        3. 通过外部变量调用内层函数,访问受保护的变量

     缺: 1. 占用更多内存: 外层函数的AO

          2. 容易造成内存泄漏

     三特点: 1. 函数嵌套:

                  2. 外层函数包含一个受保护的局部变量

                 3. 外层函数将内层函数对象返回

 

 

 

 

 

 

回顾:

1. *****闭包:

   鄙视: 快速绘制闭包图

       1. 受保护的变量,并确定外层函数调用后,变量的值

       2. 找所有操作受保护变量的内层函数

 

正课:

1. *****面向对象OOP:

   什么是: 程序中都是先用对象来定义数据和功能,再按照逻辑的需要,访问对象中的数据和功能。

   为什么: 和现实中人的想法非常接近。

   什么是对象: 内存中同时存储多个数据和功能的存储空间

              描述现实中一个具体事物的属性和功能的程序结构

                     事物的属性,会成为对象中的属性

                     事物的功能,会成为对象中的方法

   何时: 今后开始写程序前,都要先用对象,描述好要操作的事物的属性和功能,再按需使用对象的功能,访问对象的属性

   如何: 面向对象三大特点: 封装,继承,多态

      封装: 将一个具体事物的属性和功能集中定义在一个对象中

      创建自定义对象: ——封装   3种:

         1. 使用对象直接量:

              var obj={

                  属性名: 属性值,

                         ... : ... ,

                  方法名: function(){... this.属性名 ...},

                         ... : ... ,

              }

          强调: 对象自己的方法,要访问自己的属性,必须用this.属性名.

           this->正在调用函数的当前对象自己

 

       2. 使用new: 2步: 

          var obj=new Object(); //创建一个空对象

          //向空对象中添加属性和方法

          obj.属性名=属性值;

          obj.方法名=function(){...this.属性名...};

         

     对象的本质: js中一切对象的底层都是关联数组

                        每个属性/方法都是关联数组中的元素

                        属性名/方法名是key,属性值/函数对象是value

 

      问题: 一次只能创建一个对象

       3. 解决: 用构造函数:

          什么是构造函数: 专门描述一类对象统一结构的函数

          何时: 今后只要反复创建多个相同结构的对象时,都要先定义构造函数

          为什么: 复用对象的结构代码

          如何: 2步:

            1. 定义构造函数

                function 类型名(属性参数列表){

                     this.属性名=属性参数值;

                               ...=...;

                     this.方法名=function(){ ... this.属性名 ...  }

                }

            2. 用new调用构造函数,创建并装修新对象

                var obj=new 类型名(属性值列表);

                     创建一个指定“类型”的对象

                     用new调用指定"类型"的构造函数来创建对象

              new: 4件事:

                1. 创建新的空对象

                2. 让新对象继承构造函数的原型对象

                3. 用新对象去调用构造函数

                      向新对象中添加构造函数规定的属性

                      将属性参数的值,保存到新对象的新属性中

                      向新对象中添加构造函数规定的方法

                4. 将新对象的地址保存在变量

 

      按需访问对象的属性,调用对象的方法: 

         访问对象的属性: obj.属性名   用法和普通的变量完全一样

                属性就是保存在对象中的一个变量

         调用对象的方法: obj.方法名() 用法和普通的函数完全一样

            强调: 方法中的this,默认指.前的对象

 

      构造函数的问题: 只能复用代码,不能节约内存

 

      继承: 父对象的成员,子对象不用重复创建,也可直接使用

      为什么: 即节约内存,又代码重用

      何时: 只要一类子对象,需要相同的属性或功能时,都要将相同的属性和功能仅在父对象中定义一次即可

      如何:

        原型对象: 集中存储同一类型的子对象所需的所有共有属性和方法的父对象

 

 

 

 

 

 

 

正课:

1. *****OOP

   内置对象的原型对象

   共有属性和自有属性

   原型链

   原型相关API

*****自定义继承

 

1. 内置对象的原型对象:

   所有内置对象都是一个构造函数(除Math外)

   每类内置对象都有自己的原型对象(prototype)

   所有内置对象的API都保存在类型.prototype对象中

   何时: 解决浏览器兼容问题: 2步:

      如果类型.prototype.方法===undefined

         类型.prototype.方法=function(...){

            this->自动获得将来调用该方法的当前对象

         }

 

2. 共有属性和自有属性:

   自有属性: 直接保存在对象本地的属性

   共有属性: 保存在父级原型对象中的属性

   访问共有/自有属性:

      读取属性值: 即可用子对象读取,也可用原型对象读取

      修改属性值:

          自有属性: 子对象.属性名=值

          共有属性: 原型对象.属性名=值

   如何判断自有还是共有:

      自有: var bool=obj.hasOwnProperty("属性名")

          判断obj中是否包含自有属性"属性名"

      共有: 不是自有! 且 子对象可访问到!

 

3. ***原型链(prototype chain):

   什么是原型链: 多级父对象连续继承,形成的链式结构

      保存了: 对象的属性和方法

      控制了: 对象的成员的使用顺序

         优先使用自有成员

         自己没有才延原型链向父级查找,只要找到就不再向上

         如果整个原型链上都没有,才返回undefind

   vs 作用域链(scope chain)

      保存了: 全局/局部的变量

      控制了: 变量的使用顺序

         优先在当前函数调用的函数作用域对象(AO)中查找

         如果函数作用域对象(AO)中没有,才延作用域链向全局方向查找。只要找到,就不再继续

         如果整个作用域链上都没有,才报错

 

鄙视题: 判断一个对象是不是数组类型,有几种方式

   0. typeof只能区分基础类型和function

              无法进一步区分对象的类型

   1. 判断原型对象:

     如果obj的原型是Array.prototype说明是数组

      obj.__proto__==Array.prototype

      问题: __proto__是内部属性,本来浏览器是禁止使用的

      解决: Object.getPrototypeOf(obj)

               获得obj的原型对象

     

 

 

 

 

 

 

正课:

1. *****OOP

   原型链

*****自定义继承

 

1. 原型链:

  判断一个对象是不是数组类型,有几种方法: 4种

  0. typeof X

  1. 判断原型对象:

     obj.__proto__==Array.prototype

         问题: __proto__是内部属性,可能不允许使用

         Object.getPrototypeOf(obj)==Array.prototype

              问题: 只能判断直接父对象是Array.prototype的情况

                       无法判断间接继承Array.prototype的情况

              解决: var bool=father.isPrototypeOf(child)

                  判断father是否是child的父级对象

                     不但检查直接父对象,且检查整个原型链!

   2. 判断构造函数:

       每个原型对象都有一个constructor属性指回构造函数

       obj.constructor==Array

       还可以用: obj instanceof Array

          instance: 实例-用一个构造函数创建出的一个具体对象

         比如: var lilei=new Student(...);

                 称lilei是Student类型的一个实例

                 实例化一个Student类型的对象lilei

          检查整个原型链

 

   要求不够严格: 只要有继承关系,就认为是数组

   要求严格: 只有用数组类型创建的对象,才是真正的数组。

 

   3. 检查对象的class属性

      什么是class: 对象的内部属性,专门保存创建对象时使用的类型名。

       只有一个办法获得class属性值:

          调用Object.prototype.toString();->"[object Class]"

      问题: 所有内置对象都重写了Object中的toString

         重写(override): 如果子对象觉得,父对象的成员不好用,可在本地定义同名的自有成员,覆盖父对象中的。

         ——多态

      解决: 用call强行借用

        call: 强行借用一个本无法调用的方法

        何时:  想调用一个原本无法调用的方法

        如何: 想借用的函数.call(要替换的对象)

             比如: Object.prototype.toString.call(obj)

                         相当于: obj.toString()

                         返回: "[object Class]"

 

   4. Array.isArray(obj);

      问题: ES5   IE9+

      解决: 自定义isArray方法

 

鄙视题: 对象实例方法 vs 构造函数方法

     对象实例方法: 必须用一个对象实例才能调用的方法

                            仅当前类型的对象可用!

        对象实例方法一律保存在该类型的原型对象中,所有子对象共用。

        何时: 一个方法只希望当前类型的子对象才能使用时

     构造函数方法: 不需要任何对象实例,用构造函数即可直接调用的方法。

       构造函数方法一律保存在构造函数对象上

        何时: 一个方法的执行和对象的类型无关时

 

2. *****自定义继承关系:

    1. 修改单个对象的继承关系:

       obj.__proto__=father;

       问题: 内部属性: Object.setPrototypeOf(child,father);

                   设置child继承father

    2. 批量修改多个对象的继承关系:

       直接修改构造函数的prototype引用新的父对象

       obj.prototype=father

       强调: 必须在创建第一个子对象之前就换

    3. 两种类型间的继承: 最像Java的继承

      何时: 只要两种类型间包含相同的属性结构定义或相同的方法。

      如何: 3步:

         1. 抽象出一个父类型

             共同的属性定义,集中到父类型的构造函数中

             共同的方法,集中到父类型的原型对象中

         2. 在子类型构造函数中借用父类型构造函数

              不能直接调用: this->window

             应该用call,将this替换为当前正在创建的新对象

               父类型构造.call(this,参数...)

         3. 让子类型原型对象继承父类型原型对象

 

 

 

 

 

 

 

 

正课:

1. *****ES5

  对对象的保护:

     对单个属性的保护:

         数据属性:

         访问器属性:

 

对对象的保护:

   问题: 属性可随时直接用=赋值任何值

            属性可随时被访问

            可随时添加和删除属性

            ——不严格!

   解决: 对对象提供保护:

  

   如何:

     1. 对对象的属性提供保护

         将对象的属性分两大类:

            1. 命名属性: 可随时通过.属性名方式访问的属性

                  又分为2类:

                     1. 数据属性: 实际存储属性值的属性

                         如何保护: 每个属性都包含四大特性:

                            {

                              value: 实际存储属性值,

                              writable: true/false, //是否可修改

                              enumerable: true/false,//是否可for in遍历

                                    //依然可用.访问到

                              configurable: true/false,

                                  //1. 是否可修改前两个特性

                                  //2. 是否可删除当前属性

                                  //一旦改为false,不可逆!

                             }

                        特殊: 如果要定义的属性不存在:

                           defineProperty会自动添加:

                               自动添加后,属性的特性值都为false

                        问题: 只能提供基本(只读,遍历,删除)保护

                                无法按照自定义规则保护属性

                        解决:

                     2. 访问器属性: 不实际存储属性值

                                            专门对其它属性提供验证保护

                         何时: 只要按照自定义规则保护属性

                         如何: 也有四大特性:

                            {

                       get:function(){return 受保护的属性值},

                               set:function(val){

                                   验证要赋的新值val

                                   验证通过才将val保存到受保护的属性中

                               },

                               enumerable:true/false,

                               configurable:true/false,

                            }

                         当通过访问器属性获取受保护的属性值时

                                自动调用get方法

                         当通过访问器属性为受保护的属性赋值时

                                自动调用set方法

                                    参数val,自动获得要赋的新值

                         大问题: 受保护的属性值应该保存在哪儿?

                                      才能做到比人不能直接用,只能通过访问器属性访问

                         解决: 闭包!

            2. 内部属性: 无法通过.属性名方式访问到的属性

                  class    Object.prototype.toString.call(obj)

                  __proto__  Object.getPrototypeOf(obj)

                                    Object.setPrototypeOf(child,father)

     2. 对整个对象提供保护

 

 

 

 

 

 

 

正课:

1. *****ES5

  对对象的保护:

    对属性的保护

    防篡改

  Object.create();

  数组API:

*****bind()

 

1. 对对象的保护:

   对属性:

     命名属性

        数据属性:

        访问器属性:

           大问题: 受保护的属性值应该保存在?

           解决: 闭包

     内部属性

 

   防篡改: 禁止修改对象的属性结构

     3个级别:

     1. 防扩展: 禁止向对象中添加新属性

         Object.preventExtensions(obj)

     2. 密封: 即防扩展,又禁止删除旧属性

         Object.seal(obj)

            其实是将所有属性的configurable设置为false

     3. 冻结: 即密封,又禁止修改所有属性值!

          何时: 如果一个对象中保存了大量不变的属性值时

             Object.freeze(obj);

             其实是将所有属性的writable设置为false!

 

2. Object.create():

   var newObj=Object.create(father,{扩展的新属性})

    创建一个新对象newObj,继承father,并为newObj扩展新的自有属性

   何时: 只要继承一个现有对象,创建一个新的子对象时

   相当于: var newObj={};

               newObj.__proto__=father;

               Object.defineProperties(newObj,{

          扩展的新属性

               })

 

3. 数组API:

   1. 判断: 数组中的元素是否符合要求

      1. 所有元素是否都符合要求

          var bool=

         arr.every(function(val,i,arr){ return 判断条件 })              2. 是否包含符合要求的元素

          var bool=

              arr.some(function(val,i,arr){ return 判断条件 })

   2. 遍历API: 依次对数组中每个元素执行相同的操作

      1. 对原数组中每个元素执行相同的操作,结果保存回原数组

        arr.forEach(function(val,i,arr){  arr[i]=新值;  });

      2. 取出原数组中每个元素的值,执行相同的操作后,保存到一个新数组中

        var newArr=arr.map(function(val,i,arr){ 

            return 操作后的元素值

        });

   3. 过滤和汇总:

       过滤: 选择原数组中符合条件的元素,组成新数组

       var subArr=arr.filter(function(val,i,arr){

          return 判断条件;

       });

 

       汇总: 将原数组中每个元素统计出一个汇总结果

         var r=arr.reduce(function(prev,val,i,arr){

             return prev+val;

         },0);

         其中: 0: 表示初始值

                  prev: 截止到目前的阶段汇总值

                  回调函数的返回值,自动作为下次的prev值

 

 

 

 

 

 

      

 

转载于:https://www.cnblogs.com/sidings/p/8449173.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值