JavaScript-学习笔记总结-99

248 阅读57分钟

JavaScript

JavaScript特点

JavaScript是一种弱类型语言

  1. 弱类型:数据类型不固定,可以随时改变

  2. 解释型:相对于编译型来说的

    什么是编译: 我们写的代码我们认识,计算机不认识,我们需要让计算机知道我们写的代码的意思,

    1. 编译型语言就是把我们写的代码进行编译

    2. 解释型语言程序运行时不会编译拿一行执行一行

    3. 脚本语言

    脚本: 是一种程序,他不能独立运行,需要一个载体来运行他 在js中值得时浏览器

  3. 动态类型语言

    1. 代码(变量)只有执行到这个位置的时候,才知道这个变量中到底存储的是什么,如果是对象,就有对象的属性和方法,如果是变量就是变量的作用
    2. 对象没有什么,只要点了,通过点语法,那么就可以为对象添加属性或者方法
  4. 基于对象的语言

使用JavaScript的方法

  • 行内写法

    1. 局限性大,只针对事件进行添加,用的时候少
    2. 代码分离性差,可读性不强
  • 页面嵌入JavaScript代码

    • 写法

      <script>
          console.log('hello word');
      </script>
      或者
      <script type="text/javascript">
          console.log('hello word');
      </script>
      
    • 执行顺序:包含<script>标签内部的代码将会被从上 之下执行,在浏览器对<script>元素内的所有代码执行完成之前,其余的内容都不会被浏览器加载或显示。

  • 包含外部JavaScript文件

    • 写法:

       <script src="index.js"></script>
      
    • 执行顺序和上面一样

Javascript代码的注意事项

  1. 在一对script的标签中有错误的js代码,那么该错误的代码后面的js代码不会执行
  2. 如果第一对的script标签中有错误,不会影响后面的script标签中的js代码执行
  3. script的标签中可以写什么内容 **type="text/javascript"是标准写法或者写language="JavaScript"**都可以
    • 但是,目前在我们的html页面中,type和language都可以省略,原因:html是遵循h5的标准
  4. 有可能会出现这种情况:script标签中可能同时出现type和language的写法.
  5. script标签可以在页面出现多对
  6. script标签一般是放在body的标签的最后的,有的时候会在head标签中
  7. 如果script标签是引入外部js文件的作用,那么这对标签中不要写任何的js代码,如果要写,重新写一对script标签,里面写代码

单线程=>模拟多线程

​ 单线程 -> 一次只能做一件事

​ 多线程-> 一次可以做多件事

  • javascript 的引擎是单线程的
  1. 轮转时间片
  2. 短时间内轮流执行多个任务片段
  3. 任务1 任务2
  4. 切分任务1 任务2
  5. 随机排列这些任务片段,组成队列
  6. 按照这个队列顺序将任务片段送进js进程
  7. js线程执行一行一个又执行一个

计算机的计算单位

计算机的三大件:CPU(大脑)、内存(存储速度比较快,但是断电数据清空)、硬盘(存储速度比内存慢,但是断电后数据依然存在)。

程序运行之前,需要将程序从硬盘将内容读取到内存中。一个变量作为容器被存放在内存中,它对应着指定的值,当要使用的时候找到这个变量就能找到对应变量的值。

内存和硬盘的单位:GB、TB。换算规则:

  • 1Byte(字节,最小单位) = 8bit(是计算机中二进制的位)
  • 1BK = 1024Byte
  • 1MB = 1024KB
  • 1GB = 1024MB
  • 1TB = 1024GB
  • 1PB = 1024TB
  • 1EB = 1024PB

硬盘:1KB=1000B来计算的。

变量

作用,存储数据的或者是操作数据(可以读取,可以存储)

  • 操作的数据都是在内存中操作
  • js中存储数据使用变量的方式(名字,值--->数据)
  • 存储数据,数据应该有对应的数据类型
  • js中的字符串类型的值都用双引号或者单引号

变量的定义

  1. 定义变量

    var a;
    

    var是js种的关键字,他就是用来定义变量的. var 变量名

  2. 变量定义并赋值

    var a = 10;
    
  3. 特殊方式

    c = 10;
    
  4. 一次定义多个变量

    • 第一种情况

      var a = 1 , b = 2;//完全等价于 var a = 1 ; var b = 2;
      
      var c = 2 , d;//这样也行。
      
    • 第二种情况

      var d = e = 30;//var d = (e = 30)
      

案例: 交换两个变量

  • 方法一

    var a = 100;
    var b = 200;
    var c;
    
    c = a;// c= 100 a = 100
    a = b;// a = 200 b = 200
    b = c;// b = 100 c = 100
    
    console.log(a);//200
    console.log(b);//100
    
  • 方法二

    var a = 100;
    var b = 200;
    a = a +b; // 100+200=300
    b = a - b;// 300-200=100;
    a = a - b;// 300 -100 = 200;
    

变量的类型

  • 数据类型(存储数据的类型)-值类型

  • 基本数据类型

    • 数字(number)
      • 整数或者小数
    • 字符串(string)
      • 值一般用单引号或者双引号包起来
    • 布尔(Boolean)
      • 只有两个值true真(1)或者false假(0)
    • undefined未定义
      • 值只有一个undefined
        • 变量声明了,没有赋值,结果是undefined
        • 函数没有明确返回值,如果接收了,结果也是undefined
        • 如果一个变量的结果是undefined和一个数字进行计算,结果:NaN不是一个数字
    • null空类型
      • 只有一个值null一个对象指向为空,可以赋值null
  • Object 对象数据类型(引用类型) 复杂类型

  • Number类型

    • 整数

      var num = 10;
      
      console.log(Number.MIN_VALUE);//最小值
      console.log(Number.MAX_VALUE);//最大值。
      

      超过了最大值最小值就会被转为 -InfinityInfinity

    • 小数

      var flo1 = 1.23;
      var flo2 = .23;//浮点数值中必须包含一个小数点,小数点后面必须有值。
      
      console.log(flo1);
      console.log(flo2);
      
      var flo3 = 10.0;
      
      console.log(flo3);//浮点数存储需要的内存比较大,所以解析时能转换成整数就转换成整数了。
      
      console.log(0.1 + 0.2);
      
      • 浮点数在进行运算时不准确,所有语言里面都这样。计算小数会有误差。
        • 以后有专门的库来解决这个问题。
          • 先变成整数运算完之后再变回去就行了呗。
    • 其他不常用的值

      • 二进制,0、1,以0b开头
      • 八进制,0~7,以0o开头。
      • 十六进制,09AF,以0x开头。

      不管你是多少进制的,到最后都会被转换为10进制输出

    • 总结

      • Number数字类型
      • 无论整数还是小数都是数字类型
      • 不要用小数取验证小数(会出现浮点
      • 不要使用NaN判断是不是NaN,应该使用isNaN(值或者是变量))
  • String 字符串

    字符串可以由双引号和单引号包含内容。这两种方式没区别。

    //判断字符串长度
    var str = 'abcdefg';
    str.length;
    
  • Boolean类型

    布尔值,就两个值:true(真)、false(假)。并且这两个值严格区分大小写。

  • undefined类型

    只有一个值,undefined.表示声明了为赋值

  • null类型

    只有一个值,null.一般来说我们在后面讲对象时,对对象初始化或者删除一个对象的时候用到。

  • 获得变量的类型

    typeof 值/变量,最终得到的结果数据类型名的小写形式。

    Number String Undefined Null Boolean

    返回类型为undefiend,这个可以认为变量不能真正使用。 如果一个变量被定义了没被赋值那么你可以使用,但是如果一个变量没定义你使用那么直接将会报错(除了typeof时)。

类型转换的类型

类型转换分为两种类型强制类型转换隐式类型转换

强制类型转换:就是我们强制让某个值进行转换为某种类型。
隐式类型转换:变量在配合后面咱们讲的操作符进行运算的时候自动发生的类型转换。

强制类型转换

  • Boolean() 小工具: 将指定类型的值转换为布尔值。总是得到true或false。
  • 任何非空字符串都会转换为true,空字符串转换为false。

  • 任何非零数值都会被转换为 true,0和NaN会被转换为false。

    • undefiend转换为false。
  • 任何对象转换为true,null转换为false。

    • true == 1; false == 0;

    总结任何为空的都会转为false不为空的转为true

  • **Number()**小工具:将非数值(任何数据类型)转换为数值。

    NaN(Not a Number),是数值型,用于表示一个本来要返回值得操作但是并没有返回数

    • true将会被转会成1,false将会被转换成0

    • null将会转换成0

    • undefined转换为了NaN。

      应该得到数值,但是返回的值不是数值的情况。
      
    • 如果是字符串遵循以下规则:

      • 字符串中只包含数值(整数、浮点数)将其转换为对应的整数、浮点数。
      • 空字符串、空白串那么转换为0。
      • 除了上面的格式之外的字符,都转换为NaN。
    • 总结null,false,空白串空字符串转换成0 ,数字转换成数值,其他转换为nan

  • parseInt()小工具:将__字符串__转换为整数。(如果其中的值不是字符串那就得先转换为字符串),它会忽略字符串前面的空格,找到第一个非空格的字符。如果第一个字符不是数字或负号将会返回NaN。如果第一个字符是数值继续往下找一直找到所有字符完或遇到了第一个非数字字符。

  • parseFloat()小工具:将字符串转换为小数

    • 总结想要转整数使用parseInt,想要整数转小数使用parseFloat,Number比他们两个要严格
  • toString()工具:将是将对应的类型转换为对应的字符串。

    布尔型:true=>'true',false=>'false'

    数值:1=>'1'

    null和undefiend没有toString()工具。

  • String()工具:将变量转换为字符串。如果你不确定变量值是否是undefiend或null时就使用它。(或者说直接以后转字符串就用它)

    • 如果值能够使用toString()工具,那String()就调用toString()得到结果。
    • 如果值是null,直接返回**'null'**。
    • 如果值是undefined,直接返回**'undefined'**。

隐式类型转换

隐式类型转换和上面的规则相同,它通常发生在和运算符相关联的操作中。

运算符分类

只要用到了变量那么就需要配合着运算符

表达式:将数据(变量、数据)用运算符按照对应的规则链接起来的式子叫表达式。

var a = 10;
var b = 10;
a + b
1 + 1

计算机的运算符是按照功能划分的

  • 算数运算符
  • 赋值运算符
  • 符合运算符
  • 比较运算符
  • 相等运算符
  • 逻辑运算符
  • 三元运算符

算数运算符

  • +加号

    • 如果两个数都是数值则会加法,当有类型是字符串时,则会拼接,其他的类型会调用Number转换
    • +号放在数值前对数值不会造成印象,如果+号后面的非数值则会调用Number()进行转换
  • -减号

    • 如果类型不是数值 则先调用Number()进行转换然后进行运算
  • *乘号

    • 跟减号规则一样
  • /除号

    • 跟减号规则一样
    • 被除数/除数=商
      • 被除数为0,结果就是0。(0除以任何数都是0)
      • 除数为0 结果Infinity
      • 被除数、除数都是0。结果NaN
  • %取余|模

    • 跟减号规则一样
  • ++--

    • 放在操作数后面先用后加

    • 放在操作数前面先加后用。

      以上规则仅仅在使用++或--本行适用。

    • 如果操作数不是数值则内部使用Number工具进行转换,会改变原来变量的值。

    • 如果不参与运算, ++--在前面还是在后面结果都是一样的

NaN的特点

NaN(Not a Number),是数值型,用于表示一个本来要返回值得操作但是并没有返回数

  • 任何涉及到NaN的操作结果都是NaN。

  • NaN和任何值都不相等,包括自己。

复合运算符

比较的结果应该是布尔值. true或者false

  • >,大于
  • <,小于
  • >=,大于等于。大于或者等于。
  • <=,小于等于。小于或者等于。

比较规则:

  • 如果两个操作数都是数值,进行数值比较大小。

  • 如果两个操作数都是字符串,比较两个字符串对应的字符编码(不常用,知道一下。)

    从第一个字符的编码开始比较,如果第一个字符的编码就能比较出大小,后面的不比较了。如果不能得出大小就继续第二位以此类推。一只到最后都没比较出来,谁的位数多谁大。

  • 其他类型类型的比较调用Number()进行转换。

  • NaN和任何比较都是false。

总结: 数值比较比较大小,两个都是字符串的话比较编码,其他的比较转Number()进行比较

相等操作符

  • 相等和不相等

    • ==,判断是否等于。如果两个操作数相等,返回true。
    • !=,判断是否不等于。如果两个操作数不相等返回true。

    比较规则:

    1. 类型相同直接比较是否相等。

    2. 类型不同,只要有一个操作数是数值就先调用Number()

    3. 都是基本类型numberstringboolean和基本类型比较,都会先转换为数值。1 == '1'

    4. null和undefined相等。

    5. NaN和NaN不相等。

    6. null == 0不相等

      • null有一点要注意 在进行相等比较时null不转换。

      • 对于null来说如果比较的是相等运算符它是不转换的。 对于比较运算它是转换拍的。

      • null >= 0 // true

      比较运算符。比较运算符的时候null是会转换的

    总结:类型相同比较相等,null和undefined相等,其他的类型比较调用Number()进行比较

逻辑运算符

比较运算符只能比较两个值,但是有时候我们需要多个比较。 这个时候就需要逻辑运算符。

  • &&,逻辑与(并且),第一个操作数和第二个操作数都为true时候返回true,否则返回false。

    • 逻辑运算符可以使用任何类型的操作数。如果操作数是布尔型直接比较,如果不是要进行Boolean()转换。

    • 如果第一个操作数是false(或经过Boolean()转换为false),那么返回的是第一个操作数。如果第一个操作数是true(或经过Boolean()转换为true),那么返回的是第二个操作数。

    • 如果第一个操作数能够决定结果(false或经过转换后为false),那我就不对第二个操作数进行求值了,因为已经能够决定结果了。这就是所谓的短路

  • 第一个操作数是否为false(或者经过转换后是false),如果是就直接返回第一个操作数了。(但是在内部还是要经过Boolean转换的。)

总结: 这是一个短路语句,第一个为真则返回第二个,第一个为假则返回第一个,比较时会调用Boolean()

  • ||,逻辑或(或者),第一个操作数或第二个操作数为true时候返回true,否则返回false(两边都为false时候才为false)。
  • 逻辑运算符可以使用任何类型的操作数。如果操作数是布尔型直接比较,如果不是要进行Boolean()转换。
    • 如果第一个操作数是true(或经过Boolean()转换为true),那么返回的是第一个操作数。如果第一个操作数是false(或经过Boolean()转换为false),那么返回的是第二个操作数。
    • 逻辑或也有短路操作,如果第一个操作数求值为true,就不会对第二个操作数求值了(因为第一个操作数已经能决定结果了)。

总结: 这是一个短路语句,第一个为真则返回第一个,第一个为假则返回第二个,比较时会调用Boolean()

  • !,取反,用来取得指定布尔值的取反的结果。(最终结果还是布尔值)

    • 如何操作数不是布尔型,会先将它转换为布尔值。

    • 可以使用!!(两次调用!)来进行快速将变量转换为布尔值

      var a = 1;
      console.log(!!a);
      

总结: 值是布尔值,通常用来把类型转换为布尔值使用

三元运算符

  • 运算元:

    1 + 2,1和2分别代表运算元(操作数),+代表操作符。

  • 按照运算元来分类:

    • 一元运算符:!+-++--

    • 二元运算符:+-*/&&||

      • &&,逻辑与 and
      • ||,逻辑或 or
      • !,取反 not
    • 三元运算符:三元运算符只有一个。

      运算元1 ? 运算元2 : 运算元3;

      运算元1是一条语句。运算2是一条语句。运算3是一条语句。

      运算元1是条件,当运算1位true时返回运算元2,为flase时返回运算元3。

      var a = 1;
      var b = 2;
      条件语句 ? 表达式1 : 表达式2;
      var c = a > b ? a : b;
      

    总结: 三元表达式的返回值,如果条件为true返回表达式1,否则返回表达式2

优先级和关联性问题

  1. 程序是从上往下执行的。
  2. 每一条语句要严格按照从左到右的顺序计算。
  3. 再按照优先级和关联性计算各个表达式和运算符号。

优先级

简要:一元运算符通常优先级比较高,然后是二元然后是三元。如果实在你确定不了那么+括号。

运算符小口诀:一元、算数、关系、逻辑、三元、后赋值。

关联性:决定了拥有相同优先级的运算符的执行顺序。

小括号:分组操作符、函数调用符。

流程控制

代码的执行过程

有三种方式

  1. 顺序结构:从上到下,从左到右执行的顺序,就叫做顺序结构
  2. 分支结构:if语句,if-else语句,if-else if-else if...语句,switch-case语句,三元表达式语句
  3. 循环结构:while循环,do-while循环,for循环,for-in循环

分支结构

分支语句就4种,不管程序多大,多复杂都是这几种情况

  • 单一分支结构

    格式:

    if (条件表达式) {
        代码块1
    }
    

    如果条件表达式值为true则执行代码块1的代码,如果为false则忽略,不执行代码块1的代码。程序继续向下执行。

    总结:如果条件为真则执行代码块1,否则不执行代码块1

    注意

    1. 可以在括号中直接执行true或false,但是没有任何意义。
    2. 括号里面不是表达式,会将他的值通过Boolean进行转换,然后根据转换后的结果判断是否执行。
    3. 如果大括号内只有一条语句,可以省略大括号(不建议使用。因为会被打死)
  • 双向分支结构

    格式:

    if (条件表达式) {
        代码块1
    } else {
        代码块2
    }
    

    如果条件表达式的值为true则执行代码块1的代码,如果为false就执行代码块2的代码,执行完整个if语句之后就继续执行下面的代码。

    总结:if的代码必须执行不是true就是false

  • 多向分支结构

    格式:

    if (条件表达式1) {
        代码段1
    } else if (条件表达式2) {
        代码段2
    } else if (条件表达式3) {
        代码段3
    } .....
    
    else {
    	都不成立时执行的代码段。
    }
    

    如果条件表达式1返回true就执行代码段1,条件表达式2执行就返回代码段2......如果条件都不成立时执行else里面的内容。

    最后一个else可以省略,从上到下执行找到条件为true的则执行大括号里面的代码块否则继续着都不为true则执行else

    • if语句__通常__用来判断范围条件。但是也能判断是否相等。
  • switch 语句

    从本质上讲,它就是为了让打开人员免于大量使用if(){....}else if()

    格式:

    switch(有值得东西){
        case1:
            代码块1
        break;
        case2:
            代码块2
        break;
        ......
        casen:
            代码块n
        break;
        default:
            默认匹配的代码块
        break;
    }
    

    执行过程:

    1. 先确定switch中小括号的值是什么
    2. 用确定出来的值分别和case后面的值进行匹配(这里进行匹配是全等于匹配)
    3. 找到第一个能够匹配上的case之后,执行其中的对应的代码块。
    4. 执行完这个代码块之后调用breakfe退出switch语句。
    5. 如果一直到最后都没有找到匹配的内容,那么执行default中的语句。(相当于else)

    注意

    1. switch语句在比较直时使用的是全等于,不会发生类型转换。
    2. default语句可以省略。
    3. break是否可以省略,可以。如果省略break就会导致执行完当前case(当然是匹配上的case)之后继续下一个case(不管是否能够匹配上),一直遇到break。
    4. switch也可以用来进行某个条件的判断。但是我们通常用switch来进行某个值得精确匹配。
    5. 总结:switch使用场景是数值判断,他的判断条件是全等于 defaule可以不写,写的话等同于else
  • 巢状分支结构

    不是新的语法只是将前面讲的几种分支综合、嵌套使用。

    在分支结构中可以使用我们学过的任意的代码。

    我们写代码的时候一般情况下,嵌套不会超过3层,如果超过的话你就得注意了看看你的逻辑、代码是否有优化的地步。

总结:如果值不具体用if,如果确定值用switch

循环结构

程序中有些代码我们要反复执行多次,这个时候用到了循环

  • while语句

    格式:

    while(条件表达式){
          代码块
    }
    

    第一步先执行条件表达式,表达式为true,执行循环中的代码块,然后再执行条件表达式看是否为true,如果为true就再执行循环中的代码,一直这样走,直到遇到条件表达式为false的情况。

    • while循环三要素
      1. while外部要有起始值
      2. while要有条件
      3. while内部始终要想办法将while中的条件改为false。

    注意

    1. while循环里面可以写true,但是注意死循环。
    2. while循环通常用来做条件循环。
    3. 循环是可以相互嵌套的,实际应用当中嵌套不应该超过3层。

    总结:使用while循环,必须得有判断条件,自增或者自减

  • for循环

    格式:

    for (表达式1;表达式2;表达式3) {
        
    }
    

    表达式1,用在做初始条件

    表达式2,用来做判断条件

    表达式3,用来做自增自减

    for (var num = 1; num <= 3 ;num++) {
        console.log(num);
    }
    
    1. 执行表达式1,num = 1
    2. 执行表达式2,判断条件是否成立1<=3,true
    3. 执行当前的代码块中的代码,打印num,1。
    4. 执行表达式3,进行自增。执行完之后num=2
    5. 再执行表达式2判断条件是否成立2<=3,true。
    6. 执行当前的代码块中的代码,打印num,2
    7. 再执行表达式3进行自增,执行完成后num=3
    8. 再执行表达式2,判断条件是否成立3<=3,true。
    9. 打印当前的代码块中的代码,打印num,3
    10. 执行表达式3进行自增,num=4
    11. 4<=3,表达式2,false。
    12. 终止循环执行。

    表达式1只执行了一次。

    for循环通常用来做计数循环。

  • do{}whil循环

do{
    要循环执行的代码。
}while(条件表达式);

第一步先执行do中的语句,然后检查条件表达式是否成立,如果成立true则继续执行do内的代码。

不管怎么样do...while循环都会最少执行一次。

while循环和do-while循环的区别

  • while循环先判断在执行
  • 先循环后判断至少执行一次

特殊的流程控制语句break-continue

  • break:

    • 作用1:在switch中使用就是跳出当前的switch语句。
    • 作用2:在循环中使用,跳出离他最近的一层循环,break下面的代码不执行了。
  • continue:

    结束本次循环,调用continute之后本次循环剩下的代码不执行了,直接进入到下次循环。

总结:break跳出当前循环,continue跳出本次循环

数组

基本概念

基本类型只能存储一个值。如果我要存储一组值怎么办。(用的多。) 使用数组最大的好处是什么:可以进行批量操作。

  • 数组:一组值,可以存储多个值(每个值可以存储任何数据类型的数据,但是一般情况下都是相同 类型的数据),他是有序的集合

  • 下标(索引、键、key),为了能够拿到对应位置的数据,所以我给这个数组中的内容进行编号。

  • 元素:下标和值得组合。

  • 类和对象的概念:

    数组是对象类型(引用类型的一种)。javaScript中有对象,没有类。

    • 类是什么东西:具有相同特征的事物的总称。
      • 每个类中都有一些共同的特征,这些特征我们叫属性。
      • 每个类中有一些共有的行为,我们叫方法。
    • 对象:类的具体化。这种具体的我们叫他对象。
    • 类和对象的关系:根据类(模具)创造出来的实体(对象)。当然对象也有属性和方法。

    总结:数组是一组有序的集合,他可以通过下标(索引)来获取他的值,

    类和对象:数组是对象的一种类型,js中有对象没有类,类是具有相同特征事物的总称(一类),类会有一些共有的特征(属性),每个类会有行为(方法),对象是类的实体化,这种具体的叫对象

数组创建

  1. 使用Array来创建

    var colors = new Array();//创建了一个空数组。
    console.log(typeof colors);
    

    new是操作符表示调用后面的内容,可以暂时(实际上javaScript中没有类,具体为什么后面说) 将后面的内容看成是刚才所说的类。

    1. 我们可以向里面传递一组值。
    var colors = new Array('red', 'blue', 'green');
    console.log(colors);
    
    1. 在Array()中如果只有一个元素并且这个元素是数值的时候要注意。根据浏览器的不同得到的 结果不同(现在咱们浏览器是得到了有100个元素的数组,但是每个元素中都是空 undefined 。有些浏览器会只创建一个元素这个元素对应的值是100 )。

    2. 声明数组时省略new操作符,与new Array这种结果相同.

  2. 使用字面量方法创建数组

    数组字面量由一对包含数组项的方括号组成,多个元素之间使用逗号隔开。 这种方式定义的与 **new Array() **这种结果相同。

    总结:数组有两种创建方式,一种是new的方式创建(括号里面如果填写数值的话则会根据浏览器解析不同而产生不同效果),第二种方式是字面量创建方式[]使用的居多

数组元素的个数length

数组中元素的个数保存在数组的length 属性中。这个属性的值大于等于0。

var colors = ['red', 'green', 'blue'];
console.log(colors);
console.log(colors.length);

数组的最后一项的索引始终是 length-1

数组的基本操作

  • 查看元素的值

    • 数组[下标],在这你首先要找的就是下标。
    • 读取时超过了最大的下标,那么值为undefiend。
    • 中括号里面可以放置的是任何返回数值或数值型字符串的表达式。
    • 如果要始终取得数组中的最后一个值 **arr[arr.length - 1] **
  • 增加元素

    • 在不同的位置增加

      • 在末尾增加.

        设置下标时使用的是超过了数组的最大的下标。这个时候数组的长度变为( 设置后的下标 +1 )

        var colors = ['red', 'blue', 'green', 'pink'];
        // colors[4] = 'hotpink';
        colors[100] = 'white';
        console.log(colors.length);
        console.log(colors);
        

        我们更多是使用length属性始终想数组末尾添加数据

    • 在数组的开始处添加

          
      arr[0] = 0;
      
      • 执行顺序
        • i = 5; 5 > 0 true; arr[5] = arr[5-1] 5--;
    • i=4;4 > 0 true; arr[4] = arr[4-1] 4--;

    • 在数组指定位置添加

      // 在数组的指定位置添加
      

    var arr = [1, 3, 4, 5]; for (var i = arr.length; i >= 0; i--) { if (i < 3) { break; } arr[i] = arr[i - 1];

    } arr[3] = 11;

    
    - 修改数组
    
    ```js
    var arr = [1,3,4];
    arr[1] = 2;
    // 数字的下标1的值被改为了2
    
    • 删除数组

    • 删除后面所有的

        arr.length = 0;
      

      如果将length改为比原来的length小那么将会将后面的元素删除。

    • 删除末尾数组

        // 在末尾删除
      var arr = [1, 3, 4, 5];
        arr.length--;
        console.log(arr);
      
      • 删除开头的数组
        var colors = ['red', 'blue'];
      //1    i< 2   
        //2    2< 2 false
        for (var i = 1; i < colors.length; i++) {
            /*
                        colors[1-1] = colors[1]    colors[0] = colors[1]
        
                    */
            colors[i-1] = colors[i];//当前的前一个等于当前的。
        }
        console.log(colors);
        
        colors.length --;
        console.log(colors);
      
      • 删除指定位置元素
        var arr = [1, 2, 3];
      for (var i = 0; i < arr.length; i++) {
            if (i < 2) {
                continue;
            }
            arr[i] = arr[i + 1];
        }
        arr.length--;
        console.log(arr);
      

多维数组

多维数组:数组的每个元素都可以存储任意类型的数据,那我在元素中再数组。

如何区分是几维数组:

var arr = [
    [1,2],
    [3,4]
];

有几层中括号,就是几维数组。现在有2层次中括号那他就是二维数组。

数组一般最多就到三维,一维、二维用的最多。不管多少维都是一层一层的往里面找。

函数

什么是函数

函数:完成指定任务并且已经命名的代码块,可以用来进行反复使用

函数本质上也是一种数据,也属于对象类型;函数也是值与其他数据类型不同的是,函数中包含的是代码,这些代码是可以执行的。

函数作用

  1. 解决代码冗余问题,形成代码的复用。
  2. 封装代码,让函数内部的代码对外部不可见
  3. 可以把整个项目的代码,通过函数模块化。

函数的种类

  • 系统函数,系统已经提供的一些函数。alert()parseInt()
  • 自定义函数,如果x系统函数不能满足要求那就自己写。

如果说有系统函数能够实现功能就用系统函数,不要自己写。

函数自定义调用

步骤:

  1. 将你要实现的功能先写出来。
  2. 将代码用大括号包含起来。
  3. 使用关键字funciton 函数名称() 来声明函数。
  4. 将里面经常变得量提取出来作为参数。

比如一下案例

// 需求打印5行5列表格
function printTable() {
    document.write('<table border="1">');
    for (var i = 1; i <= 5; i++) {
        document.write('<tr>');
        for (var j = 1; j <= 5; j++) {
            document.write("<td>第" + i + "行,第" + j + "列</td>");
        }
        document.write('</tr>');
    }
    document.write('</table>');
}

函数不调用不执行

调用函数 函数名()

注意:

  1. 函数名要有意义。起函数名的规则要遵循标识符的定义规则。
  2. 函数有3种定义方式,__这种声明方式可以重复(可以和自定义、系统函数都可以重复)__一旦发生重复后面的覆盖前面的。
  3. 这种方式声明的函数可以在函数定义之前调用,也可以在函数定义之后调用。

函数声明的其他方式

  • 函数表达式声明

    格式:

    var variable = function(){
        语句;
    };
    

    注意:

    1. 使用函数表达式方式声明的函数,末尾要有分号。

    2. 这种方式声明函数,其外围是表达式。

    3. 这种方式调用函数 必须在创建函数之后(执行到这个表达式才行)。

      第一种方式声明函数会在程序运行之前就已经声明了,这种方式声明函数只会在运行到这一行的时候才声明。

  • 函数构造器声明(不用,不建议使用) (通过new的方式创建函数)

    格式:

    var variable = new Function('args0','arg1'......'语句');
    

    为啥不建议用:

    1. __函数是对象__是通过构造器创建对象方式声明
    2. 因为这种方法会导致代码解释两次。

函数的其他注意点

函数是数据,函数名是这个数据的标识

  1. 如果你要访问函数对应的数据而不执行函数的话,必须去掉函数后面的那对括号

    test();//因为括号前面是函数  所以他被当做函数调用符来使用了。
    console.log(1 * (2 + 3));//括号因为包含的是表达式所以当做分组操作符来使用了。
    
  2. 函数是一个数据,函数名是标识,一个函数可以有多个标识。

函数的参数

自定义函数的时设计原则:用户可以控制自定义函数,可以对自定义函数进行微调。

在函数定义的时候,函数名字后面的小括号里的变量就是参数,目的是函数在调用的时候,用户传进来的值操作

步骤:

  1. 将你要实现的功能先写出来。

  2. 将代码用大括号包含起来。

  3. 使用关键字funciton 函数名称() 来声明函数。

  4. 将里面经常变得量提取出来作为参数。

    参数分为形参跟实参

    • 形参:声明函数时,形式上的参数。
    • 实参:调用函数时在()中实际传入的参数值。

    注意:

    1. 形参和实参传值是一一对应的。

    2. 多个形参和多个实参之间使用逗号分隔。

    3. 函数的形参就相当于在函数内部定义了一个变量。

    4. 如果要获得形参的个数,那么可以使用length属性

      function fn(a, b, c, d) {}
      console.log(fn.length);
      // 不可以通过length改变长度
      
    5. JS函数的形参、实参的个数可以不相等。

      • 如果实参比形参少,那么多出来的形参将会自动被赋值为undefiend
      • 如果实参比形参多,那么多出来的实参可以使用arguments对象来获得。

arguments对象

  • arguments对象只有在函数内部才有。(参数对象)

  • array-like,类数组,像数组但不是数组。为什么像数组因为其中也包含了索引和lengt属性。

    • array-like 伪数组,具有数组的属性,没有数组的方法

    arguments对象可以使用下标的方式来访问传递进来的实参。

    function fn() {
        for (var i = 0; i < arguments.length; i++) {
            console.log(arguments[i]);
        }
        console.log(arguments.length);
    }
    fn(1, 2, 3, 4, 5, 6, 7, 8)
    
  • 函数中的形参在JS中可以不写,可以使用arguments对象获得,但是使用形参会比较方便。

    arguments对象通常用来在处理不知道有多少个实参传递进来的情况:

    任意个数的数值相加

function fn() { var sum = 0; for (var i = 0; i < arguments.length; i++) { sum += arguments[i]; } console.log(sum); } fn(1, 2, 3, 4, 5, 6, 7, 8)




- arguments对象中有一个length属性用来确定传递进来多少个实参。

- arguments对象**可以和形参一起使用**(<u>但是一般不这样用</u>),他们两个是不一样的东西。传递值给形参是赋值操作,arguments对象是接收到的参数系统自动挡到这个对象中。

```js
function test(num1){
   console.log(num1 + arguments[1]);
}

test(1,2);
  • arguments对象中保存着一个叫callee的属性,这个属性指向了拥有arguments对象的这个函数

函数的返回值

函数的返回值:在函数内部有return关键字,并且在关键字后面有内容,这个内容被返回了

return语句return后面的值返回到函数调用处(函数从哪里调用返回到哪里

​ 好比你的主管领导让你去做一件事,当你做完之后你要讲事情的结果返回给你的领导。

​ 什么时候该用return什么时候不用return完全取决于你函数的功能,如果你要使用返回的结果那你就用。如果不 会再使用函数的结果那我就不用

注意:

  1. 函数定义时返回值不是必须的,如果没有给定返回值那么默认也会返回一个值,这个值就是undefined
  2. 直接写一个return后面不写返回的值,函数也会返回一个值这个值还是undefined
  3. 函数会在执行完return语句之后停止函数的执行并且退出函数。(return语句之后的任何代码都永远不会执行。)

通过以上这些特点:return有两个作用:

  1. 返回函数最终要返回的值。

  2. 终止函数的执行。(return

    总结:return作用返回要返回的值,终止函数执行,可以不写 不写的话这个函数的返回值就是undefined

函数的函数体(函数要干什么事)、参数、返回值是组成一个完整函数的三大件。

  • 无参数无返回值。
  • 有参数没有返回值。
  • 无参数有返回值。
  • 有参数有返回。(最多的)

程序的执行过程(重要)

堆和栈

堆栈本身是是数据结构。堆(链表结构)、栈(栈结构)。

程序在运行的时候内存按照逻辑分为了堆内存和栈内存。

  • 栈:栈结构中开辟的内存比较小。速度快,操作系统都会自动回收内存。

    特点:先进后出。

  • 堆:开辟的内存通常比较大,速度比较慢。

    特点:**先进先出 **

    JS中已经封装了垃圾回收机制。

执行环境

每个运行环境我们通常也称为执行上下文。

JavaScript中运行环境分为两种:

  • Global,全局的。JavaScript代码开始运行的时候就默认存在的执行环境。
  • Function,函数执行环境。进入一个javaScript函数的运行环境(函数调用时才会进入);

代码在执行时开辟一段栈空间来说明代码的执行顺序,这个栈我我们叫执行栈

  1. 当JavaScript代码执行时会将全局执行上下文压入到栈底,当执行到函数执行上下文,也会将函数执行上下文压入到栈底。
  2. 函数执行完成之后将函数执行上下文弹出并且销毁(和执行环境相关的一系列内容都会被销毁)。
  3. 知道所有代码执行完成才将全局执行上下文销毁。

执行环境的阶段

每个执行环境分为两个阶段:

  • 创建阶段(做准备工作的)
    • 全局执行环境:一上来就进入了创建阶段。
    • 函数执行环境:当函数被调用,但是开始执行函数内部代码之前。
  • 代码执行阶段(真正的解释、执行代码)

一个执行环境对应着一个作用域。

作用域及作用域链

  • 什么是作用域?

    作用域:变量起作用的区域范围。

  • 作用域的作用?

    隔离变量(函数外部定义的和函数内部定义的同名变量没有关系)

  • 作用域的确定时机?

    作用域是在进入到全局执行环境或函数执行环境时确定好的。

  • 作用域种类?

    在ES5中作用域只分为两种,全局作用域和函数作用域(局部作用域)。ES6里面新加了一个块级作用域。

一执行javascript就进入了全局执行环境就有了全局作用域,执行某个函数时进入到函数执行环境就有了函数作用域(局部作用域);

一个执行环境对应着一个作用域。

同一个函数多次调用会产生不同的执行环境,每次调用都会产生一个。

全局变量和局部变量

  • 全局变量:定义在全局环境中的就是全局变量(函数外部定义的,全局作用域)。
  • 局部变量:定义在局部环境中就是局部变量(函数内部定义的,函数作用域)。

局部变量:只能在自己的中使用,只能在函数内部使用,外部不能用。

全局变量:在整个程序所有的地方都会起作用(整个程序当中任何地方都可以操作这个全局变量)

全局变量可以被函数内部进行操作,但是全局当中没有办法操作局部变量(如果真的要操作就需要闭包)。

  • 作用域链

    • 函数内部是可以嵌套函数的。

      function test(){
          function test1(){
              console.log('test1');
          }
          test1();
          console.log('test');
      }
      
      test();
      

      作用域链:作用域是变量起作用的范围,作用域链是用来找变量的一系列的过程。

      一个函数在定义会自动生成一个[[scope]]的属性([[]]都是不能直接通过代码访问的),[[scope]]属性中存储的是函数定义时作用域的层级。

作用域链是函数执行时创建的,作用域链由:当前执行的函数作用域 + [[scope]]属性中的作用域组成的。

执行环境如果被销毁的话,其对应的作用域链不会被保留,会被销毁。

函数当前执行的作用域是作用域链的顶端,全局作用域是作用域链的最后端。

  • 总结:
  1. 每个执行环境都会有自己独立的作用域链。
  2. 作用域链由当前的执行环境的作用域和[[scope]]属性组成。在调用时才形成。
    1. 函数调用时形成了执行环境、作用域。
    2. [[scope]]属性:函数定义时就已经生成了,函数即使不调用也会有这个属性。其中存储的是包含定义它时的各个层级的作用域

当局部作用域中使用、设置没有带var的变量的执行过程:

  1. 局部作用域中是否有同名的形参,如果有就使用。

  2. 局部作用域中是否有同名的函数,如果有就使用(不管是在定义之前使用还是在定义之后使用。);

    如果有同名的形参函数将会覆盖形参。

  3. 局部作用域中是否有声明的同名的变量。(同名的变量声明在使用之前将会得到该同名变量的值,同名变量声明在使用之后将会得到的是undefined。)

    如果你同时定义了同名的形参、函数、变量声明:函数会覆盖形参,变量声明如果在使用之前则覆盖函数和形参,在使用之后则为函数。

  4. 如果查找完了再局部作用域中没有该变量,那就沿着作用域链向上层开始查找。

    1. 如果上层能够找到则使用、设置上层的同名变量。
    2. 如果到最后没有找到,则从全局作用域中看有没有,如果有就使用、设置,如果没有使用时会报错,设置时是想全局作用域中设置新的变量。

内存中的数据存储

变量的值如果是基本类型(String、Boolean、Number、Undefined、null),会在栈内存开辟一段空间存储标识符,然后将基本类型的值也存储在栈内存中和标识符对应起来。

引用类型的值存储在堆内存里,存储引用类型的时会给堆内存一个地址。但是标识符还是存储在栈内存中,为了能够让标识符和真正的数据对应起来,栈中存储的值是引用类型有的地址。

内存地址使用十六进制0x开头表示。

查找变量是和执行环境有关系(作用域链)和堆没有绝对的关系,堆只是也来存储对象类型的数据的。

程序开始到结束执行了什么:

  1. 程序一开始就碰见了全局环境,首先会创建全局环境并且进行压栈,全局代码执行的时候依赖的是全局环境当中的东西。

    全局变量如果是基本类型,那么这个值直接在栈当中,如果这个变量是对象(函数、数组),那么函数和数组是要在堆结构中开辟自己的空间专门存储数据的。然后把堆里面这块空间的质地给栈当中对应变量进行保存(他们两个是对应的)

  2. 当程序执行碰到了函数调用(函数比较特殊,因为函数可以执行),函数执行时候也要有自己的环境,函数执行也是创建自己的函数环境进行压栈。(函数执行环境一定是压在全局环境之上的。)

    局部变量,是在函数环境中存在的,只有函数执行,局部变量才会出现。函数执行完成之后函数环境要弹出栈(函数执行环境要讲对应的一系列东西销毁,释放内存),局部变量也就不存在了。

  3. 当函数调用完成之后,会继续执行全局代码,一直到所有的代码都执行完成,代码程序执行结束,程序结束时,全局环境最后出栈。

变量提升(预解析)

在执行环境的第一步**(创建阶段)时,将带var的变量的声明及函数的声明(function abc(){})**,放在了作用域中,在执行阶段开始时代码的本身的逻辑和其他逻辑还是在原来的位置。

注意:

  1. 全局执行环境、函数执行环境,每个执行环境都会有一个变量(变量、函数声明)提升的操作。
  2. 函数表达式不会被提升。
  3. 函数名和变量同名。函数声明和变量都会被提升,但是函数会先被提升,然后才是变量。

IIFE

IIFE: Immediately Invoked Function Expression,意为立即调用的函数表达式,也就是说,声明函数的同时立即调用这个函数。

现在必须将下面代码当成函数表达式语句

函数表达式语句:不能以大括号开头,不能以function关键字开始(和函数声明很像)

function(){
    console.log('foo');
}
  1. IIFE函数只能运行一次。因为引用类型没有表示,执行完成之后垃圾回收机制就回收了。
  2. 定义之后马上调用,调用完成之后马上释放。
  3. IIFE不会被提升,但是内部的代码因为还是属于函数体所以会被提升。
// 立即执行函数
(function(){
    console.log('我是立即执行函数');
})();

递归

函数的递归调用 !!!递归'很难理解'。!!!

函数的递归调用就是自己调用自己。

递归:

:进去

:出来

注意

  1. 你自己就是这个函数,是你自己调用自己。当你自己做完一件现在的事情的时候,你会去做上一次没做完的事。
  2. 递归调用一定要有一个限制,否则就是一直自己调用自己,永不翻身。这就是无限递归。
  3. 递归调用适合用在什么地方 适合用在不确定具体调用多少次情况。

回调函数

回调函数的精髓:函数是对象类型的值,可以被当做参数传入到函数中。

  • 将一个引用类型赋值给一个变量时,变量存储的是引用类型的地址
  • 一个引用类型可以有很多名字,一个改变引用类型的值,另外一个的值也会改变。
  • 函数的参数,如果是**普通值是赋值,如果是引用类型传递的是引用类型的内存地址**。
  • 一个函数本身也是值,一个对象,可以被当做实参传递到函数中并且执行。
  • 一个本身也是值,也是一个对象,可以被当做返回值返回回来

什么是回调函数

回调函数被认为是一种高级函数,一种被作为参数传递给另外一个函数(OF)的高级函数,回调函数会在OF内被调用或执行。

function test(fn){
    console.log('!!!!');//!!!!
    console.log(fn);//打印函数本身
    fn();//执行函数。
}

function t(){
    console.log('这是t函数!!!');
}

test(t);

回调函数的本质:是一种模式,解决问题的套路。

回调函数的用处

  • 通用化:代码简洁。

    俯瞰整个代码结构。

    我将通用的功能提取出来然后将各种规则提取出来。减少了耦合容易扩展

  • 事件监听和处理 会用到。

  • Ajax请求数据 会用到。

  • 框架中的各种生命周期函数 会用到。

对象

面向对象和面向过程

面向对象和面向过程 两种编程思想

  • 面向过程

    过程为中心的编程思想。过程,完成一件事的步骤。

    凡事都要亲力亲为,每件事的具体过程都要知道,注重的是过程

  • 面向对象

    以对象作为中心的编程思想,对象,你可以理解为真真正正存在的东西。

    根据需求找对象,所有的事都用对象来做,注重的是结果

    • 面向对象的特征:封装,继承,多态(抽象性)
    • 什么是对象?
      • 看得见,摸得到,具体特质的某个东西
    • js不是面向对象的语言,但是可以模拟面向对象的思想

面向对象的优势

  1. 符合人类的思维习惯。

  2. 能够适应复杂的需求变化。

    核心就是可扩展性

类和对象

  • 类:一组相似事物的统称

    • 一组:就是有多个。
    • 相似:比较像,但不完全相同。
  • 类的组成:

    • 属性:类具有的特性(通常是名词)。
    • 方法:类具有的功能(通常是动词)。
  • 对象是类的具体化,实物,实体,由类创建出来的

    类是末班,让多个对象拥有共同的属性、方法。

JavaScript中的对象

JavaScript中没有具体的类,直接就是对象。

为什么这样设计:有类无非也就是为了复用代码(共同的属性、方法)

数组:有序的键值对。

对象:无序的键值对儿,值可以是基本类型、对象或者函数。

对象声明方式

  • 方式一:字面量方式声明

    字面量方式的对象是由多个键:值对组成的,每个键:值对儿之间使用逗号分隔,最后一个不用逗号。

    对象的属性的值也可以是函数,因为函数本身也是一种数据。

    注意:

    1. 键是字符串并且不加引号(加上也行),即使是其他类型也会被转换为字符串。
    2. 如果你的键名不符合标识符命名规则就必须加上引号。
  • 方式二:Object构造函数创建

    new Object

    new是操作符标识调用后面的内容(Object,可以暂时将它看成一个类,但Object不是类)创建一个对象。

​ new后面的Object、Array不是类,实际上是函数,当使用new调用某个函数时可以说是创建了一个xxxx类型的对象。

new Array(),创建了一个Arrayl类型的对象。

new Object(),创建了一个Object类型的对象。

注意:

  1. 对象字面量表表达式法,它的目的用来检查创建包含大量属性的对象的过程。(内部也会调用new Obejct()

  2. 我们一般声明Object类型的对象时一般使用字面量表示方式。

  3. Object类型的实例是个万能的对象,它可以根据其中的属性、方法,存储任何东西。

    《国产凌凌漆》:凌凌漆:举个例子,这个型号F40,表面上是个大哥大,你看,这里有一层白色金属网膜,实际上它是一个刮胡刀,即使在我执行任务的时候,也可以神不知鬼不觉地刮胡子。至于这个,外观上看是一个刮胡刀,但它是一个吹风机。阿琴:那另外这个吹风机呢?凌凌漆:吹风机只不过是它表面的掩饰,实际上它还是一个刮胡刀!

对象的基本操作

  • 访问对象中的属性

    1. 使用.表示法来进行访问(建议使用)

    2. 使用[]表示法访问对象属性。(这里面可以放置变量、表达式)

      1. 不符合标识符命名规则需要使用[]来进行访问。
      2. 借用可以放置表达式那么中括号中可以进行运算(可以放变量)。

      如果访问的属性不存在,代码会返回undefined。

  • 增加、修改属性(有就修改,没有就添加)

    添加、修改的键名不符合标识符规则只能用中括号添加或修改。

  • 删除属性

    格式:delete 对象.属性

    var声明的全局变量不能使用delete删除,但是不使用var声明的所谓的全局变量可以使用delete删除,因为它只是window对象上面的属性。

  • 遍历对象

    for .... in将可枚举的属性自身和原型链中的属性进行遍历。

工厂模式声明对象

如果我以new Object和对象字面量方式来创建对象有啥缺点:在创建多个对象时会产生大量重复的代码。

function createPerson(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;

    return o;
}

var zhangSan = createPerson('张三',73);
var liSi = createPerson('李四',84);

console.log(zhangSan);
console.log(liSi);

使用构造函数来创建对象

instanceof 操作符它里面涉及到原型和原型链的一些东西,具体原理一会我说。现在你只需要先知道__它可以用来检测某个对象是否是某个类型的__

构造函数: 就是普通的函数。但是使用了new 操作符调用,这个时候有些人称它为构造函数,但是在JavaScript中不存在所谓的构造函数,它应该叫函数的构造调用

function Person(){

}

function Dog(){

}

定义所谓的构造函数时函数名一般首字母大写。

使用new操作符操作函数之后将会生成一个对象,这个对象就是所谓的构造函数这种类型的new person()生成的就是一个Person类型的对象。

function Dog(){
    this.name = '旺财';
    this.cate = '泰迪';
    this.woof = function(){
        console.log('wawa!');
    }
}
var d1 = new Dog();
var d2 = new Dog();

console.log(d1);
console.log(d2);

this关键字__暂时__表示的是调用构造函数时生成的这个对象。

我们可以在定义构造函数时定义形参,构造函数内部使用形参。new的时候传入我们的个性化数据,让我们有不同的属性值。

自定义构造函数创建对象做了几件事

  • 在内存中开辟(申请一块空闲的空间)空间,存储创建的新的对象
  • 把this设置为当前对象
  • 设置对象的属性和方法的值
  • 把this这个对象返回

总结:由于Object类型的对象是万能的对象,不能识别类型,所以我们使用构造函数来生成对象,我们可以设置对应的属性和方法(引入了this关键字,this暂时先理解为生成的这个对象。)

函数构造调用中的return

构造函数中可以使用return语句,如果return的是基本类型那么return的值将会被忽略。最终得到的还是新创建的这个对象。

如果构造函数中返回的是其他对象,那么就返回这个对象了。

==的两边操作数都是对象,用来判断这两个对象是否是同一个内存地址。如果是返回true,否则返回false。

函数既是对象,又是可执行的代码:

  1. 函数的对象方面它拥有属性和方法。
  2. 代码块方面,它既可以执行特定的代码(普通函数),又可以用来创建特定类型的对象(函数的构造调用)
    1. 普通函数,没有return,返回的是undefined。有return返回的是指定的值。
    2. 函数的构造调用,没有return,返回的是新生成的对象。有return,return的值如果是对象类型返回的就是这个对象,如果是基本类型将会把返回的值忽略,返回新生成的对象。

原型

  • 函数的prototype属性

    每个函数在创建的时候都会创建一个prototype属性,这个属性就是原型属性,这个属性中存储的是一个空对象(没有自己定义的东西)

    原型对象中会有一个属性constructor,指向的是拥有这个原型对象的构造函数的引用。

    prototype属性,只有在函数被当做构造函数时才有效果,它的作用:由同一个构造函数生成的各个实例(对象)之间共享属性和方法。

  • 实例对象(通过函数构造调用新创建出来的对象),__proto__属性

    当调用构造函数创建了一个新的实例之后,该实例内部将包含一个指针,指向构造函数的原型对象。(构造函数的prototype属性)

    构造函数中的prototype属性和构造函数创建出来的实例的__proto__属性指向的是同一个对象。

    如果查询构造函数创建的实例的属性时,本实例中没有那么将会查找本实例对象中的__proto__是否有该属性,如果有就使用。

    在原型中查找值得过程是一次搜索,对原型对象所做的任何修改都能够立即从实例上反映出来。

原型:分为显式原型和隐式原型

  • 显式原型:就是构造函数中的prototype属性。
  • 隐式原型:就是调用函数生成的实例对象的__proto__属性。

原型链

重点、难点。

原型链用来描述对象通过__隐式原型__查找属性值的过程。

    function Person(name, sex) {
        this.name = name;
        this.sex = sex;
    }

    var p = new Person('张三', 'nan');

总结:实例化对象在查找属性时,先从自身去查找有没有这个属性。如果有,直接使用这个属性的值,如果没有,会继续沿着这个对象的隐式原型对象(__proto__)继续找(__proto__对应的是改对象所属的构造函数的prototype属性所值的对象),看这个对象中是否有,如果有就使用,如果还没有再去找隐式原型对象(一直到最后默认是Object的显式原型对象),找到以后看有没有这个属性,如果有就使用,如果没有就返回undefiend(代表已经找到顶了)。

原型对象中的属性给构造函数实例化出来的对象服务的,构造函数本身不能用。

Object.prototype属性中还有其他的方法,这些方法是系统默认的,后面会说。

  • instanceof 操作符

    说他的原理:

    • instanceof 操作符第一个变量对象,暂时称为A,第二个变量一般是函数,暂时称为B。

    • instanceof的判断规则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能够找到同一个对象,那么就返回true。如果找到最后也找不到最后就返回false。

    总结:

    1. 所有的引用类型的实例和instanceof操作都会返回true。引用类型的实例 instanceof Object的时候都会返回true。
    2. 基本类型不是对象,所以在使用instanceof操作符时始终会返回false。
  • for...in遍历对象。

    for .... in将可枚举的属性自身和原型链中的属性进行遍历。

    • 数组、函数都是对象,都可以添加属性,同样添加上属性之后也可以维持本来的功能。

    • 是否可遍历:一个对象中有些属性是不可以遍历出来的你可以理解为一般的时候用不到这个属性,它给你保护起来了

      for...in可以遍历的自身和原型链中可枚举的属性。

      var a = {
          name:'hello',
          sex:'1'
      };
      //一次从对象中读取一个属性,并把该属性名赋值给pro。
      for (var pro in a) {
          console.log(pro);
      }
      

      数组也是对象,我也可以使用for...in来进行遍历。多方便呐

      var a = [1,2,3,4,5,6];
      for (var v in a) {
          console.log(a[v]);
      } 
      

      上面从语法上面行的通,但是不用用for...in遍历数组。因为for...in不能保证遍历的属性,而数组最重要的特性就是有序

this指向

this指代的是当前实例的这个对象,这种说法有点片面。

this最终代表的是函数的执行者,最终指向一个对象。(决定this值的是调用方式。)

  1. 全局执行环境中的this就是指的window。

    window对象是最顶层的对象,它对应的是全局执行环境。你在全局中定义的变量和函数都是作为window对象的属性和方法。

    在JavaScript当中window对象有两个功能:

    1. window对象是最顶层的对象。
    2. window对象在BOM中是操作浏览器的接口。
  2. 如果函数你是光秃秃(只是函数名进行调用前面没有任何东西)的调用this指向的是window。

  3. 通过对象的方法调用,this指向这个对象。

apply、call方法

每个函数对象都有callapply这两个方法。

本质就是设置函数体内的this对象的值

格式:

  • call(thisArg,arg1,arg2)
  • apply(thisArg,[argsArray])

他俩的工作方式完全相同,不同之处在于传递给调用函数的参数的传递形式。

作用:

  1. 借用其他函数的方法,让本对象使用。

    var obj = {
        name : 'sex',
        say:function(){
            console.log(this);
            console.log(this.name);
        }
    
    }
    
    obj.say();
    
    var obj1 = {
        name:'hello'
    }
    
    obj.say.call(obj1);//{name:'hello'}
    
  2. 让其他构造函数中的属性变成自己的。

    function Demo(name){
        this.name = name;
    }
    
    var obj = {sex:'nan',age:18};
    
    // Demo.apply(obj,['lgzhyan']);
    Demo.call(obj,'yanhaijing');//执行Demo函数,Demo中的this指向了obj,那么当你运行时 this.name  就相当于obj.name了。  所以运行完成之后新增了属性。
    console.log(obj);
    

new运算符的流程

new运算符内部的执行过程。

过程:

  1. 创建一个对象,开辟内存空间(堆)

  2. 设置原型链

  3. this指向该内存让函数内部的this指向对象。

  4. 执行函数代码。

  5. 将创建的对象实例返回。

    new的运算过程

基本类型、引用类型判断类型

  • 基本数据类型判断:typeof

    1. undefined
    2. boolean
    3. string
    4. number
    5. object(null和对象)
    6. function(函数)
  • 其他的对象那就只能使用instanceof

    格式:对象 instanceof 类型

  • 判断是否是同一个对象(是否是同一个内存地址),使用==

    =是赋值、两个==是判断是否等于(对于对象判断是否是同一个地址)、三个等于判断是否是全等于。

JSON

JSON是一个内置的对象,不用定义能够直接使用。

大小的JSON表示的是JS中的内置对象,小写的json表示的是一种数据格式。

  • json是一种数据格式,现在我们的前后端进行交互都是使用json数据格式。

  • json本质上是字符串,简称json串

    前端往后端传递数据时,要传json格式的数据。 后端返回给前端数据也是返回的json数据。

json的值

  • 简单值:字符串、数字、布尔值、null。

    JavaScript和json字符串最大的区别在于json字符串必须使用__双引号__。

  • 对象:无序的键值对儿

    属性也是字符串 所以也得加上引号。

  • 数组:有序的键值对儿

    我们通常会将数组和对象结合起来使用,形成复杂的数据的集合。

json数据处理

  • JSON.stringify(值),将JavaScript的值转换为json字符串(用来向后端发送数据时转换为json数据)。
    • JSON.stringify()输出的JSON字符串不包含让非常空格和缩进。
    • 在进行转换时,对于JavaScript对象,所有的函数、原型成员的值和值为undefined的任何属性都会被忽略。
  • JSON.parse(值),将JSON字符串解析称为JavaScript值。(用来将从后端得到的数据转换为javascript代码)

Math

ECMAScript还为数学计算提供了全局的内置对象,即Math对象。

  • 将小数转换为整数,如果是整数则不改变。

    • Math.ceil(),进一取整。
    • Math.floor(),舍一取整。
    • Math.round(),执行标准的四舍五入。
  • 进行数学计算

    • Math.min() ,获得最小值。

    • Math.max() ,获得最大值。

    • Math.abs(),求绝对值。

    • Math.pow(num,次方),进行幂运算。

    • Math.PI,圆周率,这是一个属性。

    • Math.random(),返回大于等于0小于1的一个随机数。

Date

Date是javaScript提供的构造函数。它用来处理日期、时间。

时间戳

  1. 是一个整数,用来计算的。
  2. 从1970年1月1日(计算机的元年)0点0分0秒~现在的毫秒数。

时间处理方法

Date既是函数(可运行的代码),对象(其中包含属性和方法)

Date.now(),获得当前的时间戳。

当做构造函数来使用:

格式:new Date()

new Date不写参数的话,直接获得当前的日期的对象。

传入参数我要获得指定日期的对象:

格式:new Date(年,月[,日[,时[,分[,秒]]]])

  • getFullYear(),获得4位的年份。
  • setFullYear(),设置年份(传入的值是4位的数字);
  • getMonth(),获得日期的月份。 返回日期中的月份,其中0表示一月,11表示12月份。
  • setMonth(),设置日期的月份。
  • getDate(),返回天数。(1~31)
  • setDate(),设置,如果超过了本月应有的天数那么增加月份。
  • getDay(),获得星期几(0表示星期日,6表示星期六);
  • getHours(),获得小时。
  • setHours(),设置小时,传入的值超过了23则增加天数。
  • getMinutes(),获得分钟数。
  • setMinutes(),设置分钟数。如果超过59增加小时
  • getSeconds(),返回秒数(0~59)
  • setSeconds(),设置秒数。如果超过59增加分钟数。

如果不支持Date.now()或者date.valueOf使用以下方法

var date1 = +new Date();
console.log(date1);

包装对象

实际上再JavaScript中提供了三个特殊的构造函数Number()String()Boolean()。可以通过new操作符来调用它们生成对应类型的对象。

var str = new String('abc');
console.log(str);

通过上面可以看出来,确实是个对象__proto__属性,指向了原型对象,原型对象中有大量的方法、属性。

根据不使用这三个构造函数。当你把number、boolean、string类型当做对象调用的时候它会自动的调用对应的构造函数生成一个对象。

var str = 'abc';//声明了一个字符串放在了str中。
//当执行str.length时 发现是对象调用,js内部自动调用new String('abc')创建一个包装对象。
//然后你就可以使用属性、方法。当使用完成之后(本行  str.length),又会自动将包装对象转换为字符串。
console.log(str.length);
//你这里打印还是基本类型
console.log(str);

number、boolean、string都会经过上面的这个过程。

普通对象和包装对象的区别:对象的生存周期。

  • 使用new操作符创建的引用类型的实例,在执行时候当前作用域中会一直存在(存在于内存中)。
  • 自动创建的基本包装类型对象,只存在于一行代码的一瞬间,然后立即会销毁。

字符串方法

三大件:功能(干什么的),参数,返回值。

ES5方法

  • charAt(),根据传入的位置取得其所在的字符。

  • charCodeAt(),根据传入的位置取得其所在的字符编码(unicode码)。(不常用)

  • String.fromCharCode(),根据传入的unicode码,转换为相应的字符。返回转化后的字符。

  • concat(),将一或多个字符串拼接起来返回拼接的到的新字符串。

  • indexOf(searchValue[,offset]),从一个字符串中向后搜索给定的子字符串,然后返回子字符串的位置(如果没有找到该子字符串,则返回-1)。第二个参数可以指定从哪开始(默认从0位置开始查找);(常用)

  • lastIndexOf(),从一个字符串中向前搜索给定的子字符串,然后返回子字符串的位置(如果没有找到该子字符串,则返回-1)。

  • replace()方法,替换子字符串。(其他功能讲正则的时候说)将某个字符串置换成某个字符串。

  • slice(beginSlice[,endSlice]),提取一个字符串的一部分,返回一个新的字符串。beginSlice从0开始,endSlice可以省略,如果省略endSlice会一直提取到字符串末尾。(beginSlice是开启的位置,endSlice是结束的下标的----但结果不会包含结束的下标)---用的较多。(按下标取字符串)

  • substr(start[,length]),返回一个字符串中从指定位置开始到指定字符数的字符。start,提取字符的位置,索引从0开始。length提取的字符数,length的最大值为字符串的长度减去1。省略length会从起始位置一直到字符串结束位置(按长度取字符串)

  • split(),基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。(常用)

  • toLowerCase(),将字符串转换为小写。

  • toUpperCase(),将字符串转换为大写。

  • toLocaleLowerCase() 将字符串转为小写

  • toLocaleUpperCase() 将字符串转为大写

  • trim() 去除两端空格

  • trimLeft() 去除左空格

  • trimRight() 去除右空格

  • valueOf(),返回对象的字符串、数值或布尔值表示。如果是包装类型返回包装类型的值,如果是对象则返回对象本身。

    如果是包装类型获得的是__proto__原型对象中的valueOf(),返回的是基本值。如果是其他的对象类型,获取的是Object构造函数原型对象中的valueOf(),返回的是对象本身。

    该方法没有参数

    返回值:如果是包装对象,返回的是包装对象中的基本值;如果不是包装对象类型,返回的是对象本身。

  • toString(),返回对象的字符串表示。

    该方法没有参数

    返回值:如果是包装对象,返回的是包装对象中的基本值转换为字符串;如果不是包装对象类型,根据对象类型的不同返回值也不同。

    总结:

    对象分为包装对象和非包装对象,都可以使用valueOf和toString()方法。

    • valueOf():用来取对象中基本值的。

      • 包装对象:调用的都是各自的valueOf(),可以获得基本值。
      • 非包装对象:调用的是同一个valueOf(),是Object原型当中的;获取不到基本值,返回的海是对象本身。
    • toString,用来把对象转换为字符串

      • 所有的对象都调用的是各自的toString()都可以转换为字符串。

      • 包装对象:转换的时候将基本值拿出来转换为字符串。

      • 非包装对象:转换的时候根据对象不同结果不同。

        • 如果是数组,返回的是去掉中括号之后的列表。

        • 如果是函数,返回的是函数本身转换为字符串。

        • 普通的对象,返回的是[object Object]

  • match(),根据正则来匹配指定的字符串。

  • search()方法,返回字符串中第一个匹配项的索引,如果没有找到则返回-1。该方法是中从字符串开头向后查找模式。

ES6方法

  • includes(string,n),返回布尔值,表示是否找到了参数字符串。

  • startsWith(string,n),返回布尔值,表示参数字符串是否在源字符串的头部。

  • endsWith(string,n),返回布尔值,表示参数字符串是否在源字符串的尾部。

  • repeat(N),返回一个新字符串,表示将原字符串重复N次。返回新字符串。

  • padStart(length,str),如果没有到达length的长度使用str在头部进行填充。

  • padEnd(length,str),如果没有到达length的长度使用str在尾部进行填充。

数组方法

ES5方法

Array.prototype开头的。只要是prototype 那么就是给实例用的。

  • Array.prototype.push(),接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。

  • Array.prototype.pop(),从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。

  • Array.prototype.unshift(),在数组前端添加任意个数选项并返回新数组的长度。

  • Array.prototype.shift(),移除数组中的第一个项并返回该项,同时将数组长度减1。

  • Array.prototype.splice(),向数组的中部插入项。

    整体格式:arr.splice(起始项,删除的个数,要插入的项目1,要插入的项目2......)

    • 删除,可以删除任意数量的项,只需指定2个参数:要删除的第一项的位置和要删除的项数,返回删除的项组成的数组,原数组受到更改。

    • 替换,可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需要指定3个参数:起始位置、要删除的项数、要插入的任意数量的项(插入的项数不必与删除的项数相等)。

    • 插入,可以向指定位置插入任意数量的项,只需要提供3个参数:起始位置、0(要删除的项数)、要插入的项目(要插入的项目可以传入任意多个项)。

  • Array.prototype.concat(value1,.....,valuen),基于当前数组中的所有项创建一个新数组。这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾。最后返回新构建的数组。

  • Array.prototype.join(),使用指定的字符串拼接数组中的每个元素,组成一个字符串。

    如果说不给join方法传递任何值,或者传递给他一个undefiend,则使用逗号作为分隔符。

  • Array.prototype.reverse(),反转数组项的顺序,返回反转后的原数组。

  • Array.prototype.slice(),基于当前数组中的一个或多个项创建一个新数组(该方法不会影响原始数组),返回值是一个新数组。

    • 一个参数,返回从该参数指定位置到当前数组末尾的所有项。
    • 两个参数,该方法返回起始和结束位置之间的项(但是不包括结束位置的项);
    • 一个都不写,该方法返回从0开始一直到最后元素的一个新数组。
  • Array.prototype.sort([sortFunc]),按升序排列数组项(最小的值位于最前面,最大的值排在最后面)。排序后改变原数组。

    • 不传递参数的情况下,sort()方法会调用每个数组项的toString()方法然后比较得到的字符串。即使都是数值也是按照字符串来进行比较。

    • sort可以接收一个函数作为参数,让这个函数作为排序的规则。

      这个函数接收两个参数,分别是要比较的两个值。

      如果第一个参数应该位于第二个参数之前需要返回一个附属,如果两个参数相等则返回0,如果第一个参数应该位于第二个之后则返回正数。

      排序规则:

      val1-val2 升序
      val2-val1 降序
      
  • Array.prototype.toString(),返回数组的字符串表示。

  • Array.prototype.indexOf(),查找指定的值并返回要查找的项在数组中的位置。没有找到返回-1。比较使用全等操作符。

    注意:

    1. indexOf()方法接受两个参数,要查找的项和要查找的起点位置(可选的)。第二个参数如果不写默认从0开始向后查找。
    2. 进行查找时使用的是全等操作。
  • Array.prototype.forEach(),对数组中的每一项运行给定函数,这个方法没有返回值(返回值是undefiend)。

    forEachfor循环都可以遍历数组,他们的区别在于:

  • Array.prototype.map(),对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。

  • Array.prototype.filter(),对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。

  • Array.prototype.every(),对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true

  • Array.prototype.some(),对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。

  • Array.isArray(value),确定value是否是数组,如果是数组返回true否则返回false。

ES6

  • Array.from方法用于将类数组对象转为真正的数组:类数组对象(array-like object,有length属性和索引元素)和可遍历对象。

  • Array.of用于将一组值转换为数组。返回新数组。

  • Array.prototype.includes()方法返回一个布尔值,表示某个数组是否包含给定的值。

    该方法主要用来替代indexOf(),因为indexOf()全等运算,而NaNNaN也不相等。