javascript第三周——十一个对象及API

学习API可以简化我们的底层操作:*string、number、bollean *regexp *array *function math data error *object globle 难点:面向对象

一、 String 对象

多个字符组成的【只读】字符【数组】
只读:所有的字符串的API都不会修改原字符串,只会返回的新的结果
和数组有相同的地方:

              1、字符串中的个数:str.length

              2、获取字符串中某个字符:str[i]

              3、遍历字符串

              4、所有数组不修改原数组的API,字符串也可以使用(concat、slice),其他不可以

和数组也有很多不同的地方:

              所有的数组直接修改原数组的API,字符串都不可以使用,比如排序只有数组可以使用,但是字符串也有很多很多属于自己的API

引用/对象类型:11个

              String Number Boolean -> 包装类型

              Array Function Date(日期) Math(数学) RegExp(正则:验证)

              Error(错误)

              Object(面向对象开发方式)

              Global(全局对象) - 在浏览器中被window全局对象(保存全局变量和全局函数)代替了,只不过window可以省略不写

包装类型:专门将原始类型的值封装为一个引用类型的对象

            为什么:原始类型的值原本是没有任何属性和方法,意味着原始类型本身是不支持.操作

           但是前辈们发现字符串经常会被我们程序员所操作

          为了方便我们程序员为这三个人提供了包装类型(提供属性和方法)

          何时使用:只要试图用原始类型的变量调用属性或者方法时,自动包装

          何时释放:方法调用完毕后,自动释放包装类型,并且返回数据(又变回了原始类型)  

扩展:周期定时器:每过一段时间就会执行一次

//开启:
       timer=setInterval(function(){
                                   操作;
                        },间隔毫秒数)

//停止:
       clearInterval(timer)

二、String API

1、转义字符:\

作用:1、将字符串中和程序冲突的字符转为原文

      2、包含特殊功能的符号——换行:\n

      *3、输出unicode编码的字符

    \uXXXX :第一个汉字:\u4e00          ascii:19968

                     最后一个汉字:\u9fa5        ascii:40869

2、*大小写转换:str.toUpperCase()

将字符串中的每个英文字母统一的转为大写 或 小写。

   何时:只要程序不区分大小写,就要【先统一】转为大写 或 小写,再比较(验证码)

   如何:       大写:var upper=str.toUpperCase();

                     小写:var lower=str.toLowerCase();

3、获取字符串中指定位置的字符:var newStr=str.charAt(i);   ===   str[i]

4、获取字符串中指定位置的字符的ASCII码:

              var ascii=str.charCodeAt(i);

          根据ascii码转回原文:

              var 原文=String.fromCharCode(ascii);

5、***检索字符串:检查索引:获取关键字的下标

              var i=str/arr.indexOf("关键字",starti);

              从starti位置开始,查找右侧【第一个关键字】的位置

              starti可以省略,默认从0位置开始查找

              返回值:找到,返回的是第一个关键字的第一个字符的下标

                     *没找到,返回-1,我们不关心下标为多少,只关心为不为-1

              作用:判断有没有

              强调:数组也能用此方法,后期才添加上的

              笔试题:默认只能获取到第一个关键字的下标,如何才能获取所有关键字的下标?

                     var str="no zuo no die no can no bibi";

                     var index=-1;

                     while((index=str.indexOf("no",index+1))!=-1){

                            console.log(index);

                     }

                    

6、*截取字符串:

          1. var subStr=*str/arr.slice(starti,endi+1);//和数组用法一样,支持负数参数,-n代表倒数第n个

          2. str.substring(starti,endi+1);//用法几乎和slice相同,不支持负数参数

          3.*str.substr(starti,n);//支持负数参数,n代表截取的个数,不必考虑含头不含尾

7、拼接字符串:var newStr=str.concat(str1,...);//还不如+运算

8、*替换字符串:

              var newStr=str.replace("关键字"/正则,"新内容");

9、*****切割/分割字符串:

              作用:将字符串转为数组

              var arr=str.split("自定义切割符");

              特殊:1、切割后,切割符就不存在

                2、切割符"",切散每一个字符

扩展:js创建自定义元素

1、JS创建空元素:

              var elem=document.createElement("标签名");       

 2、为此空标签添加必要的属性或事件

              elem.innerHTML="内容"

              elem.属性名="属性值"

              elem.on事件名=function(){}

  3、渲染到DOM树上:

              爸爸.appendChild(新元素)

三、正则表达式

1、最简单的正则:

写关键字原文 "no" -> /no/后缀

替换:str.replace(“no”,”yes”) -> str.replace(/no/gi,”yes”)

后缀:g:global找全部 i:忽略大小写

2、备选字符集:/^[备选字符集]$/;

              强调:1、一个中括号,只管一位字符

                    2、正则表达式默认只要满足就不管后续,我们希望从头到尾完全匹配,解决:前加^后加/[备选字符集]:/^[备选字符集]/;

              特殊:如果备选字符集中的ascii码是连续的,name可以用-省略掉中间的部分

                比如:一位数字:[0-9];

                           一位字母:[A-Za-z];

                           一位字母、数字、下划线:[0-9A-Za-z_]

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

                           除了xxx之外的:[^0-9] ——很少使用,范围太广了

3、预定义字符集:

前辈们提前定义了一些字符集,方便我们程序员 - 简化备选字符集

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

              一位字母、数字、下划线:\w  ===  [0-9A-Za-z_]

              一位空白字符:\s:包括:空格、制表符、换行

              一位除了换行外的任意字符:.(点) —— 很少使用,范围太广了

              优先使用预定义字符集,预定义满足不了在备选字符集自定义补充

4、量词:规定一个字符集出现的次数

              1、有明确数量:

                     字符集{n,m}:前边相邻的字符集,至少n个,最多m个

                     字符集{n,}:前边相邻的字符集,至少n个,多了不限

                     字符集{n}:前边相邻的字符集,必须n个

              2、没有明确数量

                     ?:前边相邻的字符集,可有可无,最多1个

                     *:前边相邻的字符集,可有可无,多了不限

                     +:前边相邻的字符集,至少1个,多了不限

5、选择和分组:

          选择:在两个规则中选一个

               规则1|规则2

          分组:将多个字符集临时组成一组子规则

               (规则1|规则2)

6、指定匹配位置:

              ^:开头

              $:结尾

              特殊:两者同时使用:前加^后加$:表示从头到尾要求完全匹配 - 只要你做【验证】!

7、密码强度:

4位密码,数字和字母的组合,至少出现一位数字和一位大写字母

              /^[0-9A-Za-z]{4}$/

              预判公式:(?![0-9]+$) -> 不能全由数字组成

                       (?![a-z]+$) -> 不能全由小写字母组成

                       (?![0-9a-z]+$) -> 不能全由数字、不能全由小写字母、不能只有他俩的组合组成

              /(?![0-9a-z]+)(?![AZaz]+)(?![A-Za-z]+)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,至少出现一位数字和一位大写字母

              /(?![0-9a-z]+)(?![AZaz]+)(?![A-Za-z]+)(?![A-Z0-9]+$)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,三者必须都有

四、支持正则表达式字符串的API:

切割:var arr=str.split(reg)

***替换:

              1、基本替换法:缺陷:替换的新东西是固定的                    

      var newStr=str.replace(/正则表达式/后缀,"新内容");

                     //replace支持正则,并且搭配上后缀就可以找到全部  

              2、高级替换法:

            var newStr=str.replace(/正则表达式/后缀,function(a){
                           //a代表正则匹配到的当前的关键字
                            return a.length==2?"**":"***";
                     });

              3、格式化替换法:

        var id="500103198602215933";

        var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;

        id=id.replace(reg,function(a,b,c,d){

       //在replace的时候,正则出现了分组:我们会得到更多的形参

       //在形参a的后面就会出现n个形参,就看你有多少个分组

      //第一个分组获得的内容会保存在第二个形参中

      //第二个分组获得的内容会保存在第三个形参中

                     return b+"年"+c+"月"+d+"日";
                     })
                     console.log(id);

              

五、正则对象:

创建:

       直接量方式:var reg=/正则表达式/后缀;

       构造函数方式:var reg=new RegExp("正则表达式","后缀");  

   API:var bool=reg.test(用户输入的内容);

       true->验证成功 false->验证失败

六、Math对象:专门提供了数学计算的API,不需要创建

1、取整:3种

          1、上取整:超过一点点,就取下一个整数

              var num=Math.ceil(num);//小数位数不能超过15位

          2、下取整:无论超过多少,都会省略掉小数部分

              var num=Math.floor(num);

          3、四舍五入取整:

              var num=Math.round(num);

               //以上三个都不用:都是在取整,只能取整

              //取整的方式:以上三个 + *parseInt(str去掉单位) + *num.toFixed(d);

              //个人最推荐*num.toFixed(d):优点:可以四舍五入保留指定的小数位数
缺点:结果是一个字符串,建议搭配上一个parseFloat  

              //*笔试题:不允许用toFixed的情况下,自己封装一个函数,实现toFixed的功能并且最后返回的是一个数字:         

                function sswr(num,d){
                            num*=Math.pow(10,d);
                            num=Math.round(num);
                            num/=Math.pow(10,d);
                            return num.toString();
                     }

2、乘方和开方

              *乘方:Math.pow(底数,幂); - 简化连乘的写法

               开方:Math.sqrt(num); - 只能开平方

3、最大值和最小值:

              var 最大/最小的=Math.max/min(a,b,c,d,e,....);

              问题:本身不支持数组参数

              解决:固定用法:var 最大/最小的=Math.max/min.apply(Math,arr);

              apply其中一个作用是可以将数组打散为单个元素

4、绝对值:把负数转为正数

              Math.abs(-1);//1

5、***随机数:

Math.random():在[0-1)之间取出一个随机小数 - (取不到最大值)

只要以后网站中某一块既有随机的功能一定需要用到随机数方法

           取min-max之间的随机整数公式:parseInt(Math.random()*(max-min+1)+min);

七、Date:日期对象:

1、创建:4种

         1、*创建一个当前日期时间

              var now=new Date();

           2、*创建一个自定义时间:

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

           3、创建一个自定义时间:

              var birth=new Date(yyyy,MM-1,dd,hh,mm,ss);//修改月份,从0开始的,0->1月

         4、*复制一个日期:

              为什么:日期的所有的API都是直接修改原日期的,无法获得修改之前的日期

              所以,在执行API之前先进行赋值,然后再操作复制后的日期

              var end=new Date(start);

2、操作:

         1、两个日期对象之间可以相减,得到一个毫秒差,换算出自己想要的某一部分 - 日期的本质其实就是保存的一个毫秒数

              创建的最后一个方式:var date=new Date(毫秒数);

         2、API:

            分量:时间的单位

            年日月星期:FullYear Month Date Day

            时分秒毫秒:Hours Minutes Seconds Milliseconds

            每一个分量都有一堆getXXX/setXXX

              其中getXXX负责获取某一个分量的值

              其中setXXX负责设置某一个分量的值

              特殊:

                1.取值范围:

                     FullYear:当前年份的数字

                     *Month:0~11

                     Date:1~31

                     *Day:0~6,外国人的眼里星期天才是一周的第一天

                     Hours:0~23

                     Minutes,Seconds:0~59

                2.Day,没有set方法

         3、如果希望对某个分量进行加减计算

              date.setXXX(date.getXXX()+/-n)  

        4、格式化日期为字符串:

              用了此方法会失去一些东西:日期会自动进制,但是字符串不会,但是你却具有了字符串的API

              date.toLocaleString();//本地日期格式 - 垃圾:具有兼容性问题,解决:自定义format方法

八、Error对象 - 错误

1、浏览器自带4种错误类型:可以快速找到自己的错误**

       语法错误:SyntaxError - 多半都是你的语法符号写错了

       引用错误:ReferenceError - 没有创建就直接使用了

       类型错误:TypeError - 不是你的方法,你却去使用了

       范围错误:RangeError - 只有一个API会碰到:num.toFixed(d);//d取值范围的:0~100之间

2、只要发生错误,就会报错,会导致后续代码终止,我们不希望

      错误处理——对用户:就算发生错误,我们也不希望报错,而是给一个错误的提示,后续代码可以继续执行

      try{
              只放入你可能出错的代码
       }catch(err){
              发生错误后才会执行
              console.log(err);//err就是我们的错误提示,英文
              console.log("中文错误提示");
       }

       try...catch...的性能非常差,几乎里面的代码效率会被降到最低

       *可以用一个技术代替:if...else...

       *开发经验:记住一切的客户端输入/用户输入都是坏人 - 你不必担心,只要你做好防护就不会出现错误(!isNaN,比较运算,正则)

3、抛出自定义错误

       throw new Error("自定义错误信息") - 只要是报错都会卡主后续代码

九、Function对象:

函数的执行原理:

1、程序加载时

          创建执行环境栈(ECS):保存函数调用顺序的数组

          首先压入全局执行环境(全局EC)

          全局EC引用着全局对象window

          window中保存着全局变量

2、定义函数时

          创建函数对象:封装代码段

          在函数对象中有一个scope(作用域)属性:记录着函数来自的作用域是哪里

          全局函数的scope都是window

3、调用前

          在执行环境栈(ECS)压入新的EC(函数的EC)

          创建活动对象(AO):保存着本次函数调用时用到的局部变量

          在函数的EC中有一个scope chain(作用域链)属性引用AO

          AO有一个parent属性是函数的scope引用着的对象

4、调用时:

正是因为前面三步,我们才有了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错

5、调用完: 函数的EC会出栈,AO自动释放,局部变量也就自动释放了

5、***重载:

相同的函数名,传入不同的实参,可以自动选择对应的函数执行操作

                为什么:减轻程序员的负担!

                问题:js的语法不支持重载!

                 (js不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前的所有)

             解决:在【函数中】有一个对象 - arguments对象

              什么是arguments:只能在函数中使用,自动创建,是一个类数组对象(类似数组)

              **作用:*可以接收所有传入的实参,组成一个类数组

                 只有3个点和数组相同:

                                   1、使用下标

                                   2、使用length

                                   3、遍历

                        ***arguments可以做的事:

                            1、实现重载:通过在函数内部判断arguments,执行不同的操作(比如判断length)

                            2、以后有没有形参都无所谓

                            3、正式开发中,有可能会将多个函数整合为一个函数 - 代码优化

6、匿名函数:没有名字的函数

         1、匿名函数自调:

          节约内存,因为匿名函数,没有变量引用着,用完,就会立刻释放变量

               (function(){
                          //以后可以代替全局代码写法,尽量不要在外部再去书写JS(不用担心事件会被释放掉,只会释放掉变量)
                })();

       2、匿名函数回调:将函数作为实参,传递给其他函数调用(都是前辈们创建好的)

          1.*学习回调的目的:让你们知道哪些叫做回调:只要不是自调,就是回调

                               arr.sort(function(){})

                               str.replace(reg,function(){})

                               btn.onclick=function(){}

           2.以后ES6技术:箭头函数:简化一切的回调函数  

          3.了解一下回调函数的原理,都会有一个形参接住并调用                                        

7、*****闭包

希望保护一个可以【反复使用的局部变量】的一种词法结构 - 其实还是一个函数,写法和以前不同

       如何使用:

              1、两个函数进行嵌套

              2、外层函数创建出受保护的变量

              3、外层函数return出内层函数

              4、内层函数要去操作受保护的变量

         强调:

              1、判断是不是闭包,有没有两个函数嵌套,返回内层函数,内层函数在操作受保护的变量

              2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本

              3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量

         缺点:受保护的变量,永远都不会被释放,使用过多,内存泄漏 - 不可多用

       问题:应该在哪里用呢——三个事件需要防抖节流

                     1、elem.onmousemove - 鼠标移动事件

                     2、input.oninput - 每次输入/改变就会触发

                     3、window.onresize - 每次窗口的大小发生变化就会触发

              防抖节流公式:

elem.on需要防抖节流的事件=function(){
                            fdjl();//内层函数只要一旦移动就会触发
                     }
                    function f1(){
                            var timer=null;//3
                            return function(){//
                                   if(timer){clearTimeout(timer);timer=null;}
                                   timer=setTimeout(function(){
                                          //操作
                                   },1000)
                            }
                     }                    
                     var fdjl=f1();

                    

十、Object:对象

对象 - Array/String/RegExp...对象具有属性和方法,都是预定义好的,现在我们可以学习自定义对象

   面向对象:三大特点:封装、继承、多态

1、开发方式:面向对象 和 面向过程?

       面向过程:经过 - 开始->结束,我们一直的开发方式都是面向过程,先干什么在干什么最后干什么

       面向对象:对象(属性和方法),js有一句话万物皆对象,

       为什么要面向对象:现实中所有的数据都必须包含在一个事物中才有意义

       何时使用面向对象:以后做任何操作都要封装在一个对象中

2、封装:创建自定义对象:3种方法

   1、直接量方式:

                 var obj={
                            "属性名":属性值,
                            ...
                            "方法名":function(){},
                            ...
                     }

              强调:1.其实属性名和方法名的""可以不加 - 暂时建议你加上

                    2.访问对象的属性和方法

                            *obj.属性名    ===       obj["属性名"]

                            *obj.方法名()  ===       obj"方法名";

                            建议使用.去访问属性和方法,更简单

                            ***JS中一切都是对象,一切对象的底层都是hash数组

                  3.访问到不存在的属性,返回undefined

                  4.可以随时随地的添加新属性和新方法

                  5.如果我希望遍历出对象所有的东西,使用for in,obj[i]才能拿到,不要用.会出问题

                  6.***如果你希望在对象的方法里使用对象自己的属性:写为this.属性名

                     ***难点:this的指向

                            1、单个元素绑定事件:this->单个元素

                            2、多个元素绑定事件:this->当前触发的元素

                            3、函数中使用this,this->谁在调用此方法,this指的就是谁

                            4、定时器中this->window

                            5、构造函数中,this->正在创建的对象

  2、预定义构造函数方式:var obj=new Object();//空对象

                     //需要自己后续慢慢追加属性和方法

                     obj.属性名=属性值

                     obj.方法名=function(){}

         以上两个方法都有一个缺陷:一次只能创建一个对象,适合创建单个对象的时候(第一种方法),第二种方法永远不远

   3、自定义构造函数方法:2步

              1.创建自定义构造函数

 function 类名(name,age,salary){
                            this.name=name;
                            this.age=age;
                            this.salary=salary;
                     }

              2.调用构造函数创建出对象

 var obj=new 类名(实参1,...)

       面向对象:

           优点:1.逼格高,所有的属性和方法都保存在一个对象之中 - 更符合现实更有意义

                2.每个功能特地分开写 - 便于维护

                3.铁索连舟 - 一个方法触发多个方法联动

             缺点:对新手不友好,this的指向非常的恶心

3、*****继承:父对象的成员(属性和方法),子对象可以直接使用

        为什么:代码重用!节约内存空间!提升网站性能!

       何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象中

1、如何找到父对象(原型对象):保存一类子对象共有属性和共**有方法的父对象

          1.对象名.proto;//必须先有一个对象

          2.构造函数名.prototype;//构造函数名,几乎人人都有,除了Math

2、面试题:两链一包:作用域链、原型链、闭包

       每个对象都有一个属性:proto,可以一层一层的找到每个人的父亲,形参的一条链式结构,就称之为叫做原型链

       可以找到所有父对象的成员(属性和方法),作用:找共有属性和共有方法的

       最顶层是Object的原型,上面放着我们一个眼熟的toString,怪不得人人都可以用到toString();

       JS万物皆对象

3、有了原型对象,设置共有的属性和方法:

         1、原型对象.属性名=属性值;//添加了一个共有属性

         2、原型对象.方法名=function(){};//添加了一个共有方法

   ***继承非常多的笔试题:

1、判断是自有还是共有:

             1.判断自有:obj.hasOwnProperty("属性名");

             如果结果为true,说明是自有属性,如果结果为false,有两种可能,共有或没有

             2.判断共有:

if(obj.hasOwnProperty("属性名")==false && "属性名" in obj){//in 会自动在obj的原型上进行查找
                        共有
                            }else{
                                   没有
                            }
// 公式:

                     if(obj.hasOwnProperty("属性名")){//false
                            console.log("自有")
                     }else{ 
                            if("属性名" in obj){
                                   console.log("共有")
                            }else{
                                   console.log("没有")
                            }
                     }

                             

2、删除自有和共有

    自有:修改:obj.属性名=新值;

               删除:delete obj.属性名;

 

     共有:修改:obj.共有属性名=新值; - 非常危险的,并没有修改到爸爸,而是在本地添加了一个同名属性

                删除:delete obj.属性名; - 无效,本地本来就没有此自有属性

                 找到原型,修改/删除原型            

3、如何为老IE的数组添加indexOf

原理:

 if(Array.prototype.indexOf===undefined){//老IE
                            Array.prototype.indexOf=function(key,starti){
                                   starti==undefined&&(starti=0);
                                   for(var i=starti;i<this.length;i++){
                                          if(this[i]==key){
                                                 return i;
                                          }
                                   }
                                   return -1;
                            }
                     }

                    

4、如何判断x是不是一个正则:

4种方法:千万别用typeof,只能检查原始类型,不能检查引用类型

                     1、判断x是否继承自Array.prototype

                            Array.prototype.isPrototypeOf(x);

                     2、判断x是不是由Array这个构造函数创建

                            x instanceof Array;  

                     3、Array.isArray(x); - ES5提供的,只是ES5+,老IE不支持,此方法只有数组

                     4、输出【对象的字符串】形式

                        在Object的原型上保存着最原始的toString方法

                        原始的toString输出形式:[object 构造函数名]

                        ***多态:子对象觉得父对象的成员不好,在本地定义了同名成员,覆盖了父对象的成员

                        固定套路:借用:Object.prototype.toString.apply(x);

if(Object.prototype.toString.apply(arr)=="[object Array]"){
                                   console.log("是数组")
                            }else{
                                   console.log("不是数组")
                            }

                           

5、实现自定义继承:

                     1、两个对象之间设置继承:

                            子对象.proto=父对象                   

                     2、多个对象之间设置继承:

                            构造函数名.prototype=父对象

                            时机:应该在开始创建对象之前设置好继承关系