javascript学习笔记整理

javascript从零到精通笔记整理

js写在哪


   - css写在哪
     - 内联(行内):属性形式:style="样式属性:样式属性值"
     - 内部:style双标签,包裹css样式
     - 外部(外联):link单标签,的href属性,引入css文件的路径

     - 知道啥叫公共样式不?
       - 好几个页面都要用到的样式,叫公共样式

1:内联

     - 内联(行内):属性形式:事件触发
       - ``
       - 不建议使用,结构和行为的分离,优化项目,便于管理
       - 为了测试方便
       

2:内部

 - 内部:script双标签,包裹js代码

     - 

- - 不太建议内部,结构和行为的分离,非公共行为部分,简单行为,可以放在当前页面
    - 公共行为:好几个页面都要用到的行为,叫公共行为
  - script标签可以放在哪?
    - 可以放在任何位置
    - 行业习惯:
      - head标签内
      - body后:内部的最后;外边的最后;没有区别
      - script标签,放在不同的位置,执行顺序受到了影响。目前如果不是特殊要求,暂时将script标签写在body的后面

3:外部

  
- 外部(外联):script双标签,通过**src**属性,引入js文件的路径

  - 创建js文件,再在html文件中使用script双标签,通过**src**属性,引入js文件的路径
  - script标签放在哪
    - 任何位置
    - 行业习惯:
      - head标签内
      - body后:内部的最后;外边的最后;没有区别
      - script标签,放在不同的位置,执行顺序受到了影响。目前如果不是特殊要求,暂时将script标签写在body的后面
    - 按需放置位置

4:注意

- 注意:错误写法

  ```html
  
  不允许在引入外部js的script标签内,写js代码,写了,也不会执行

javascript的组成

- ECMAScript:语法,规定,规则,法律,规定了,以什么样的格式,写代码
- BOM:浏览器对象模型,提供了浏览器的操作
- DOM:文档对象模型,提供了网页的操作
- 关系:ECMAScript规定了,写什么样的代码,在BOM中操作DOM
  - 交通法规,规定,以什么样的方式,在马路上开车

- 除非特殊说明,目前学习的大部分都是ECMAScript。后期,DOMBOM会单独作为知识点讲解

打印信息的方式


 - 什么叫打印信息,不是输出信息,是**打印**:
  - 将计算机的执行结果或数据,给用户呈现出来
- 打印到浏览器的弹出框,操作的是BOM对象,浏览器
  - `alert()`
- 打印到页面,操作的是DOM对象,页面
  - `document.write()`
- 打印到浏览器的控制台,操作的是BOM对象,浏览器(操作的V8引擎)
  - `console.log()`

js的注释


  单行注释
  - // 单行注释
- 多行注释
  - /* 多行注释,可以换行 */

数据类型


分门别类,记录信息的方式
- 字符型(string):必须加引号:单''、双""、反``;任何数据,只要加了引号,就是字符!!!

- 数值型(number):29

- undefined(undefined):undefined,未赋值,不是未定义

- 布尔值(boolean):true、false

- 对象(object):标志是{},并不是绝对的

- 函数(function):标志是function,并不是绝对的
  - 不是数学中的函数,不用担心,跟数学一点关系都没有
- null(object):null,不是空对象,空值,虚无
  - 我说,见到了一个空的UFO。空:null
  - 跟我来,来了,手指,指着地上的这个机器,告诉你说,这是一个空的UFO。空:undefined

- 注意:
  - 描述,空的时候,除了null,其他都要加 类型
    - 空 字符:""
    - 空 对象:{}
    - 空 函数:function(){}
    - 空 数组:[]
    - null:null
    - undefined未赋值:undefined

如何检测数据类型

通过typeof关键字,检测数据类型

-1 typeof 要检测的数据或变量

  - `typeof 变量或数据`

-2 typeof(要检测的数据或变量)

  - `typeof(变量或数据)`

-3 typeof的检测的结果的数据类型,必然是字符型数据

  - `typeof typeof 变量或数据`

检测数据的注意点

console.log(({}).toString())                              /* [object Object]  */
console.log(Object.prototype.toString.call({}))           /* [object Object]  */
console.log(Object.prototype.toString.call([]))           /* [object Array]   */
console.log(Object.prototype.toString.call(new Function)) /* [object Function]*/
console.log(Object.prototype.toString.call(new RegExp))   /* [object RegExp]  */
console.log(Object.prototype.toString.call(new Error))    /* [object Error]   */
console.log(Object.prototype.toString.call(new Set))      /* [object Set]     */
console.log(Object.prototype.toString.call(new Map))      /* [object Map]     */


function Person() {};
let p = new Person;

class Animal {};
let a = new Animal;
console.log(Object.prototype.toString.call(p)) /* [object Object] */
console.log(Object.prototype.toString.call(a)) /* [object Object] */

console.log(p instanceof Person)                            /* true */
console.log(a instanceof Person)                            /* false */
console.log(a instanceof Animal)                            /* true */
console.log(a instanceof Function, a instanceof Object)     /* false true */

console.log(a.constructor == Person);                       /* false */
console.log(a.constructor == Animal);                       /* true */
console.log(a.constructor == Object);                       /* false */
console.log(p.constructor == Person);                       /* true */

console.log(({}).constructor == Object)                     /* true */
console.log(([]).constructor == Array)                      /* true */	


/* 1、条件表达式 */
/* if() 里面的条件表达式的结果一定是布尔值 true | false */
/* false :null undefined "" 0 NaN*/
/* !    : 取反操作得到的一定是布尔值 */

console.log(1 + true) /* 把布尔值转换为数字1  == 2*/
console.log(1 + null) /* 把 null 转换为  0  == 1*/
console.log(1 + undefined) /* NaN 因为 undefined 无法转换为数字,得到NaN */

console.log(1 + {}); /* "1[object object]"*/
/* 逻辑:先调用valueOf()转换为数字,如果得到的仍然是对象,则继续调用 toString方法 */
console.log({}.valueOf()); /* {} */
console.log({}.toString()); /* "[object Object]" */

console.log(undefined ==  null)  /* true */


/* 特殊的几种情况 */
console.log({}  == {})                /* false 因为这是引用类型的数据 */
console.log({} == "[object Object]")  /* true  {}.toString()      */
console.log([] == ![]);               /* true */

变量

当程序中,需要重复操作某些相同数据的时候,为了方便,可以将数据起个别名,通过这个别名,找到数据,使用,这个别名,就叫变量
- 语法
  - 通过var的关键字,声明
    - var a;
  - 通过赋值的方式,将数据,存到变量中:一个等号赋值
    - var a = "hello";
  - 将等号右侧的数据,存在左侧的变量中(将等号右侧的数据,起了一个别名叫等号左边的单词)

1:关键字和保留字


- 关键字
  - 被ECMAScript定义了功能的单词
- 保留字
  - 暂时还没有被ECMAScript定义了功能,但是将来有可能被定义功能的单词

2:变量名的规则


- - 必须以 `字母,_,$` 开头
  - 不允许出现 `字母,_,$,数字` 之外的内容
  - 不建议使用中文,有兼容,不仅浏览器有兼容,操作系统也会有兼容,在公司中,容易挨揍

  - 尽量语义化
    1. 词必达意
    2. 建议使用当前数据类型的首字母作为前缀
    3. 尽量使用驼峰式
       - 小:从第二个单词的首字母开始大写,常用
       - 大:从第一个单词的首字母开始大写,面向对象中,专用

  - 不允许使用关键字或保留字
    - 诀窍:如果担心是关键字或保留字,可以在单词前加前缀
- 注意
  - 变量名,千万别加,引号!!!!!!

数据类型的转换

为什么要转换?

   - 如果,用户给了一个数据,或者计算机给了一个数据,或者其他程序给了一个数据,不是咱们想要的数据类型
   - 需要转成当前程序所需的数据类型,再进行运算
   - 转换方法有哪些

1:字符转数值


1:- `parseInt(要转换的数据或变量)`

  - 从左向右依次检测,遇到第一个非数字的字符,停止转换
  - 忽略小数点后所有的内容,其实是将小数点识别成了非数字
  - 如果第一位就是非数字,直接NaN
    - NaN是什么?not a number
    - 不是一个数字的数值型数据
    - 非法的数值运算的结果
      - 非法:不合法,不合规,本来不能转,非要转

2:`parseFloat(要转换的数据或变量)`

- 从左向右依次检测,遇到第一个非数字的字符,停止转换
- 可以识别小数点
- 如果第一位就是非数字,直接NaN
  - NaN是什么?not a number
  - 不是一个数字的数值型数据
  - 非法的数值运算的结果
    - 非法:不合法,不合规,本来不能转,非要转

 3:- `Math.round(要转换的数据或变量)`

  - Math.round不是专门做字符转数值的
  - 专门做取整的,取最近的整数,顺带着给可以实现转换
    - 严格转换:只要出现非数字,那么就是NaN 

 4:- `Number(要转换的数据或变量)`

  - Number不是专门做字符转数值的
  - 系统内置的构造函数,用来创建数值对象,后话...顺带着给可以实现转换,直接转换
    - 严格转换:只要出现非数字,那么就是NaN
      如何选择?

- 按需选择
  - 当需要转换时,观察当前程序的运行情况,以及要转换数据的实际情况,对应实际规则,选择方法
  - 不管选哪个,只要注意规则,都能灵活使用

2:数值转字符

- `数值变量.toString()`
  - 直接转换,相当于加个引号

- `数值变量.toFixed(n)`
  - 保留n为小数

3:隐式类型转换

   - 其他转布尔
   - 布尔转数值
   - 其他转字符

- 隐式转换(被动转换,什么都没做,自动就转了)
  1. 算数运算
     - `+`号两边只要出现字符,结果必然为字符
     - `-`,`*`,`/`,`%`的结果必然是数值
  2. 关系运算
     - `>`,`<`,`>=`,`<=`,`!=`,`==`两边只要出现数值,那么另一边也会转成数值,进行比较

数据的运算符

1:算数运算符

- `+` 加号,加法
  - 当加号两边都是数值型数据时,是数学的加法运算
  - 任意一边是字符,那么就变成了拼接,拼接就是将两个数据,直接组合起来

- `-`,`*`,`/`,`%`
  - 就是正常的数学运算
  - 就算两边不是数值,也会默认转成数值,进行运算
  - 如果某个数据不能转成数值,会得到`NaN`

2:关系运算符

关系运算符 - 的结果是布尔值

- `>`,`<`,`>=`,`<=`
  - 只要有一边出现数值,都是数值的**比较规则**
  - 如果都是字符,才是字符的**比较规则**:**逐位比较**,得到结果,就停止

- `!=`,`==`
  - 会发生隐式类型转换,或者只比较大小,不比较类型

- `!==`,`===`
  - 不会发生隐式类型转换,不仅比较大小,还比较类型

3:逻辑运算符

 - 或
  - ||:两边只要出现true,结果就是true,必须都为false,结果才是false
- 且
  - &&:两边只要出现false,结果就是false,必须都为true,结果才是true
- 非
  - !:取反

4:赋值运算符

- `=`
  - 将等号右侧的数据,存在左侧的变量中
  - 把右边的内容,放在左边的变量中
  - 如果左侧的变量,原本有值,那么会覆盖

- `+=`,`-=`,`*=`,`/=`,`%=`
  - 先计算,再赋值(覆盖)
  - 请参考:`+ - * / %`

5:一元运算符

- 自增
  - `++`
  - 增加1
  - 前后之分
    - 前自增`++n`:先计算,后使用
    - 后自增`n++`:先使用,后计算

- 自减
  - `--`
  - 减少1
  - 前后之分
    - 前自减`--n`:先计算,后使用
    - 后自减`n--`:先使用,后计算

6:三目运算符

1. 语法

   - `条件 ? 条件为真,值1 : 条件为假,值2`

2. 注意

   - 三目其实就是一种简写方式,如果不能简写,那就不用三目
   - 三目是个表达式,表达式最终会计算出一个值
   
    var n = 16;
	var m = n>10 ? n.toString() : "0"+n;
	console.log(m);	
	能用就用,不能用,老老实实的写if-else去

js中的三大特殊数据

undefined,null,NaN

1:NaN

每一个NaN都是非法运算的结果,每一个NaN的来源都不一样

`NaN`:非法的数值运算得到的结果
- 特殊之处:
  1. 是一个数值型的数据,但是不是一个数字
  2. NaN不等于任何值,和任何数据都不相等,NaN不等于NaN
- 检测方法:`isNaN(要检测的数据)` 检查的数据如果是数字类型就是true  否则都是false
  - true:检测结果为 NaN 或能转成 NaN
  - false:检测结果不是 NaN 或不能转成 NaN
  
         // 还会发生隐式类型转换
         // 场景是:isNaN的小括号
         // 规则:转成数值,严格转换,能识别小数点
         // // 严格转换:只要出现非数字,就是NaN
         
         
          /* 延伸:Object.is() */
        /* 作用:基本上等同于全等 ===  ,但是有两点不同*/
        /* [1] 可以比较 NaN */
        console.log(NaN === NaN, Object.is(NaN, NaN)); /* false true */
        /* [2] 比较-0 +0 */
        console.log(-0 === +0, Object.is(-0, +0)); /* true false */

2:undefined

`undefined`是:未赋值,类型是undefined;`null`是:空,类型是object

- 特殊1:undefined和null在关系运算符两边隐式类型转换后,得到了一致的数据
- 特殊2:undefined被Number转成NaN,null被Number转成0

3:判断三大特殊的值


- 如何判断三大特殊的值,分别是谁?

  - `NaN`可以使用`isNaN()`来判断

    - isNaN(要判断的值或变量);

    ```js
        var a = NaN;
        console.log(isNaN(a)); // 结果是布尔值:true为是的,false为不是
        
        
        `undefined`可以使用`typeof`判断
		- typeof 要判断的值或变量
		 var b = undefined;
    console.log( (typeof b) === "undefined" );// 结果是布尔值:true为是的,false为不是
    
    `null`需要配合`typeof`和 关系运算符判断( && ),要判断的值或变量 == null
	- typeof 要判断的值或变量 === "object"	
	    var c = null;
   		 console.log( (typeof c) === "object" && c === null);

选择结构,分支结构

程序的三大结构

- 顺序结构 - 每天
  - 代码逐行执行,一行一行自上而下执行
- 分支结构
  - 有选择了,十字路口的选择,只能选择一个,如果...否则...
- 循环结构 - 后话
  - 重复,重复执行多次,起因:无聊,然后开始:吃,胖,心情不好,吃,胖....

分支语句

分支和循环结构,需要配合一些专用代码才能实现,专用代码叫:语句

1:if


- `if`:单分支
  - `if(条件){执行语句}`

- `if else`:双分支
  - `if(条件){条件为true时,要执行的语句}else{条件为false时,要执行的语句}`
  - 分支的嵌套(双分支模拟的多分支)
    - 在执行体内,可以继续写其他分支
    - `if(条件1){
    
    }else if(条件2){
    
    }else if(条件3){
    
    }else......`

- if的小括号里面也会发生隐式类型转换规则
  - 规则:其他转布尔
    - 数值转布尔:非0为true,0为false
    - 字符转布尔:非空字符,为true,空字符,为false
    - 对象转布尔:对象为true
    - 数组转布尔:数组为true
    - 函数转布尔:函数为true
    - 特殊数据转布尔:undefined,NaN,null都为false

2: switch

var n = 2;
var msg = "";
switch(Number(n)){
    case 0:
        msg = "星期日";
        break;
    case 1:
        msg = "星期一";
        break;
    ...
    default:
        msg = "你输入的数字不对,请输入0~6之间的数字";
}
console.log(msg);
- 
- 规则:
  - 当要判断的值是固定的数据时,选择使用switch
    - 如果要判断的值是范围时,选择使用if-else

  - switch的case判断,不存在隐式类型转换
    - 某些情况下,需要根据场景,选择是否需要主动转换

  - 如果你不管上面的使用建议,非要使用switch判断范围,注定要多掉点头发

3:逻辑运算符的高级用法

- 所有的隐式类型转换,仅仅发生在该数据被使用时,该数据还是原本的内容
- `||`
  - 基础规则
    - 或的两边只要出现一个true就是true
    - 如果两边都是false,就是false
  - 原理
    - 如果左侧是true,那么就确定 或 的结果了,不再向右检测
    - 如果左侧是false,不能确定结果,需要继续向右检测,不管右侧是啥,结果都停在右侧了
	
- `&&`
  - 基础规则
    - 且的两边只要出现一个false就是false
  - 原理
    - 如果左边是true,不能确定结果,向右检测;左边是false,确定结果,不向右检测
    console.log(true && false); // f
	console.log(0 && false);    // 0
	console.log(1 && false);    // f
	console.log(false && 0);    // f
	console.log( 1 && 2 );      // 2
	console.log( 2 && 1 );      // 1	
	
- `!`
  - 规则:取反
  - 灵活利用非,实现其他转布尔

4:小数的问题

1. 现象
   - 明明只有一位小数,但是最后出来很多位
   - 0.1 + 0.7 = 0.799999999999999
   - 59.999999999999999 不小于 60
   - 诸如此类问题
2. 原因
   - 计算机最终执行或识别的符号只有0和1,二进制
   - 计算机中有一套完善的转换机制,所有内容,不管是文字,字符,任何数据,图片,声音,视频,最终都会被解析成二进制
   - 计算机对于小数的计算,会产生误差

3. 解决
   - 主动保留n为小数,得到数据类型是字符
     - ` num = num.toFixed(n); `
   - 转成数值
     - ` num = Number(num); `

循环结构

1. 程序的三大结构
   - 顺序:代码自上而下,按顺序执行
   - 分支:根据指定的条件,选择不同的过程执行
   - 循环:重复

2. 循环的意义
   - 循环的意义是什么?
     - 节省操作,减少代码冗余

3. 循环的三要素
   - 计数器的创建
   - 执行条件(停止条件)的设定
   - 计算器的改变
   
4. 循环语句
- 配合指定的语句,这个语句,叫循环语句

1:while

var i = 0;
while( i<10 ){
    document.write("hello world
"
); i++; }

2:do while

var i = 0;
do{
    console.log(i);
    i++;
}while(i < 10){
    console.log("循环结束了");
}

3:while和do-while

do-while语句比while语句多执行一次

- 计数器的改变最好放在循环体的最后,除非特殊需要
- 如非特殊需要,千万不能在循环中**额外**改变计数器
- 循环,最好理解执行过程,不要硬记,因为还有循环的嵌套
- **while更适合不知道循环次数的需求

4:for

for(var i=0;i<=10;i++){
    console.log(i);
}
- 特点
  - 三要素,更集中,无需考虑计数器改变的位置,而影响程序结果
  - 顺序不能变(计数器的初始值; 条件; 计数器的改变)
  
- 执行顺序
  - 1:初始化计数器
  - 2:条件,4:循环体,3:改变,243.......

5:循环中的控制关键字

- break
  - 立即结束循环,后面的任何代码都不再执行
  - 虽然跟switch中的break长得一样,但是各有各的功能

- continue
  - 跳过本次循环,继续下次循环的执行

- 死循环
  - 无法通过自身控制结束的循环
  - 特点:消耗大量的内存,浪费资源,程序崩溃
  - 但是,可以利用死循环的思路,解决不知道循环次数的问题
    - 记得在合适的时候,通过*控制关键字**停止

循环的嵌套案例

循环的嵌套,可以解决,多行多列的数据结构问题
        // 外层循环控制行
        // 内层循环控制列

1. 杨辉三角形

// 页面打印出:
    // *
    // **
    // ***

    // 1.直接逐个字符打印

    // document.write("*");
    // document.write("
");
// document.write("*"); // document.write("*"); // document.write("
");
// document.write("*"); // document.write("*"); // document.write("*"); // document.write("
");
// 2.循环的意义,节省操作,减少代码冗余 // for(var i=0;i<1;i++){ // document.write("*"); // } // document.write("
");
// for(var i=0;i<2;i++){ // document.write("*"); // } // document.write("
");
// for(var i=0;i<3;i++){ // document.write("*"); // } // document.write("
");
// 3.利用变量,将代码改造成完全一致 // var j=1; // for(var i=0;i // document.write("*"); // } // document.write("
");
// j++; // for(var i=0;i // document.write("*"); // } // document.write("
");
// j++; // for(var i=0;i // document.write("*"); // } // document.write("
");
// j++; // 4.进一步减少代码冗余 for(var j=1;j<=3;j++){ for(var i=0;i<j;i++){ document.write("*"); } document.write("
"
); }

2. 九九乘法表

    for(var j=1;j<=9;j++){
        for(var i=1;i<=j;i++){
            document.write(i + "*" + j + "=" + i*j +"  ");
        }
        document.write("
"
); }

3. 多个数字阶乘的和

// 1~10的阶乘和

    // 先计算每个数字的阶乘

    // 然后再累加

    // 1 +          每行都需要单独计算一个乘积
    // 1*2 +        每行都需要单独计算一个乘积
    // 1*2*3 +      每行都需要单独计算一个乘积
    // 1*2*3*4 +    每行都需要单独计算一个乘积
    // .....

    // 外层循环控制行
    // 内层循环控制列
    var sum = 0;        // 用来记录最终的和,所以只需要一个即可
    for(var i=1;i<=10;i++){     // 控制行的时候,需要单独的变量
        var cj = 1;             // 根据行的切换,单独声明,这是个容器,用来记录每一行数字的乘积
        for(var j=1;j<=i;j++){
            cj = cj * j;
        }
        // console.log(cj);
        sum = sum + cj;         // 内层循环执行结束,拿到每个数字的乘积,累加到sum中
    }
    console.log(sum);

4. 等腰三角形

 //   ***
    //  *****
    // *******

    //    *
    //   ***
    //  *****
    // *******

    //   ***
    //   ***
    //   ***
    //   ***

    // 1.分结构
        // 两个三角形一个矩形组成
        // 只要打出一个三角形,两个三角形都搞定了
        // 矩形更不在话下
    // 2.分三角形
        // aaa*
        // aa** *
        // a*** **
        // **** ***

        // 2.1倒三角
        // aaa
        // aa
        // a

        // 2.2正三角
        // *
        // **
        // ***
        // ****

        // 2.3正三角
        // 
        // *
        // **
        // ***

    // 3.逐个打印
        // document.write("a")
        // document.write("a")
        // document.write("a")

        // document.write("*")

        // document.write("
") // document.write("a") // document.write("a") // document.write("*") // document.write("*") // document.write("*") // document.write("
") // document.write("a") // document.write("*") // document.write("*") // document.write("*") // document.write("*") // document.write("*") // document.write("
") // 4.减少代码冗余 // for(var i=0;i<3;i++){ // document.write("a") // } // for(var i=0;i<1;i++){ // document.write("*") // } // for(var i=0;i<0;i++){ // document.write("*") // } // document.write("
") // for(var i=0;i<2;i++){ // document.write("a") // } // for(var i=0;i<2;i++){ // document.write("*") // } // for(var i=0;i<1;i++){ // document.write("*") // } // document.write("
") // for(var i=0;i<1;i++){ // document.write("a") // } // for(var i=0;i<3;i++){ // document.write("*") // } // for(var i=0;i<2;i++){ // document.write("*") // } // document.write("
") // 5.利用变量,将代码改成一致 // var j=0; // for(var i=3;i>j;i--){ // document.write("a"); // } // for(var i=0;i"); // j++; // for(var i=3;i>j;i--){ // document.write("a") // } // for(var i=0;i") // j++ // 6.最终的代码简化 for(var j=0;j<6;j++){ for(var i=6;i>j;i--){ document.write("a"); } for(var i=0;i");

函数

1.函数的概念

- 函数:由用户或自身控制,可以实现某个功能的 代码段(很多代码)

- 函数的特点:
  - 忽略细节:在**使用过程**中,只需要关注其实现的功能,而不需要关注其内部原理
  - 重复使用:多次使用
  - 选择使用:按需使用

2.创建函数

函数从哪来

- 内置(系统提供,公司发的)
  - `parseInt()`
  - `parseFloat()`
  - `alert()`

- 自定义(自己写,自己给自己做)
  - 声明式创建函数
    - 需要配合关键字:`function`
    - 语法:
       function 函数名(){}
       
      赋值式创建函数
      需要配合关键字:`var``function`
          var 变量名 = function(){}
          函数其实就是一个变量,只不过内部存的是一段代码,这段代码还被一个容器包裹了

3.执行函数

  • 函数名()

    • 只要函数名后面有小括号,必然会立即执行当前函数
    • 固定语法
  • 通过事件执行

    • 执行无名函数
        元素.事件 = function(){}
    
    • 执行有名函数
        元素.事件 = 函数名
    
    • 无名函数配合有名函数,使用较多
        元素.事件 = function(){
            函数|变量名()
        }
    

4.函数分类

  • 根据写法分类

    • 有名函数:

      • 正常函数
      • 使用频率最多的函数
      • 声明式和赋值式创建的函数
          function 函数名(){}
          var 变量名 = function(){}
      
      • 执行
        • 常规执行:函数名或变量名()
    • 无名函数:

      • 非正常函数
      • 没有名字
          function(){}
      
      • 不允许直接存在于代码空间中,否则会报错

      • 使用场景:

        1. 作为变量的存在(赋值式创建函数时的

              var 变量名 = function(){}
          
          • 执行:变量名()
        2. 作为事件处理函数执行

          btn.onclick = function(){
              // 当事件被触发时要执行的内容...
          }
          
    • 匿名函数:

    • 利用匿名函数解决全局耗性能,局部不方便的问题

      ;(function(){

      })()

5.函数的参数

- 什么是参数
  - 根据用户传入不同的参数,选择执行函数中不同的功能
- 参数的分类:
  - 发:实参:函数执行时的参数
  - 收:形参:函数定义时的参数
  - 实参和形参的关系,赋值的关系,形参相当于变量,实参相当于值,一对一

- 数量对应关系
  - 参数可以有很多个,语法上没有数量限制,但是行业有习惯,自定义函数,如非特殊需要,尽量不要超过3个
  - 实参和形参数量一致:
    - 按照顺序,一一对应
  - 实参多:
    - 没有形参接收,通过形参找不到
    - 在函数内部有个神秘的空间(arguments),这个空间会将所有的实参全部保存,不论有没有被接收
    - arguments是个对象类型的数据(类数组的数据)
      - 长度(个数),表示接收到了几个实参
        - `arguments.length`
      - 索引(序号,编号),表示数组内部的数据位置,索引从0开始
        - `arguments[索引]`
  - 形参多:
    - 多出来的形参是undefined
    - 形参其实就是一个变量,实参是赋值,如果实参不够,表示没有赋值,undefined
    
     /* 函数名.length     形参的数量 */
        /* arguments.length 实参的数量*/

6.函数的返回值

- 返回值概念
  - 函数自身的处理数据或执行结果,需要被二次使用或其他程序调用时,需要将数据返回出来
- 如何拿到返回值
  - 关键字:`return 要返回的数据`
- return的功能
  - 可以让函数返回指定的**值**
  - 可以立即结束当前函数
    - 一个函数中可以写无数个,但是只能执行一次return,一次只能返回一个数据,不允许返回多个数据
- 返回到哪
  - 返回到函数的执行语句
    - 函数名(),既是在执行函数,也是在拿返回值
- **注意**
  - 一个函数如果没有return,那么返回的是undefined;如果有return,那么返回值就是return后面的值
- 可以返回哪些数据
  - 所有数据
    - 当返回函数时,形成了闭包(后期概念)
- 什么时候需要返回值?
  - 功能性函数(打印表格,改颜色)
    - 可以有,但是没有必要有返回值
  - 处理数据的函数(补零,计算器,计算圆的面积)
    - 一般都有,返回数据

7.补充:事件分类

1.鼠标类

/ 鼠标类:
            // 单击:click
            // 双击:dblclick
            // 按下:mousedown
            // 抬起:mouseup
            // 移动:mousemove
            // 进入:mouseover / mouseenter
            // 离开:mouseout / mouseleave
            // 右键:contextmenu

2.键盘类

// 键盘类:
            // 按下:keydown
            // 抬起:keyup
            // 按下并抬起:keypress

3.表单类

// 表单类:
            // 获取焦点:focus
            // 失去焦点:blur
            // 输入:input
            // 内容改变:change
            // 提交事件:submit
            // 重置事件:reset

4.网页特色类

  // 网页的特色事件
        // 浏览器类:
            // 加载:load
            // 滚动:scroll
            // 改变大小:resize

5.注意

 // 所有事件的绑定方式都是一致的
        // 目前学习了一种:on绑定,赋值式绑定
            // 元素.on事件名 = function(){}

8.作用域

- 谁作用的哪个区域
  - 谁:数据(变量)
  - 哪个:如何划分,函数就是一个区域

- 全局
  - 不属于任何一个函数
  - 全局作用域中的变量,叫全局变量
  - 全局作用域中的函数,叫全局函数
  - 生命周期:一直存在
  - 耗性能,但是方便
    - 少用全局

- 局部
  - 任何一个函数内部都是局部作用域
  - 局部作用域中的变量,叫局部变量
  - 局部作用域中的函数,叫局部函数
  - 生命周期:朝生暮死
  - 节省内存,不方便

  - 函数的参数(形参):是当前函数的局部变量

- 匿名函数
  - 利用匿名函数解决全局耗性能,局部不方便的问题
  - `(function(){})()`

9.变量的读写规则

- 当全局和局部的变量名重复时
- 读的规则:
  - 向上级作用域查找,找到了,就使用,同时,停止查找;找到顶级作用域,都没有,**报错**

- 写(设置,赋值)的规则:
  - 向上级作用域查找,找到了,就写入,同时,停止查找;找到顶级作用域,都没有,**会默认在顶级作用域声明这个变量,然后使用**

- 注意:声明变量时,必须加声明关键字,不加声明关键字,可能能执行,但是不规范

JS的编译和执行

- 偏原理
  - 可以不懂原理,只需要注意现象,可以正常写代码
  - 懂原理,帮助你提升代码性能,优化;方便面试
- js是一门解释性语言
  - 预先编译,再执行
  - 先通读全文,在解释含义
    - 找到var和function,做出对应提升
- 编译时
  - **提升**
- 执行时
  - 按照逻辑,结构正常执行

变量和函数会提升

1.var的提升

- var的提升

  - 提前声明,=号的位置赋值

 
    console.log(a);     // undefined
    var a = 10;
    console.log(a);     // 10

    - ↑↑↑↑等价于↓↓↓↓

    var a;
    console.log(a);     // undefined
    a = 10;
    console.log(a);     // 10
 /* window 和 全局作用域  */
        /* (1) 默认全局作用域中所有的(var)变量和函数都会默认称为window的成员。 */
        /* (2) 存在全局变量污染的问题,我们声明的全局变量可能在window中已经存在,而且它们有特殊的意义,会造成覆盖的问题。 */

2.function的函数提升

- 整体提升
- 即提前声明,有提前赋值

- 如果var遇到function

  1. 赋值式创建函数 
     var fn = fucntion(){}
    - 提升的是var,不是function


  2. 变量和函数重名
     var a = "hello";
     function a(){}
     - var提升的更高,所以fnction占便宜,生效的是function

函数的高级应用-递归

  • 在函数内部执行自己
  • 递归就是函数自己调用自己
  • 递归类似于循环
  • 递归函数要注意停止/返回,否则会造成死递归
  • 递归比较消耗性能,尽量少用
  // 递:
        // fn(5) = fn(5-1) + 1
        // fn(4) = fn(4-1) + 1
        // fn(3) = fn(3-1) + 1
        // fn(2) = fn(2-1) + 1
        // fn(1) = fn(1-1) + 1
        // fn(0) = 0

   // 归:
        // fn(5) = 4 + 1
        // fn(4) = 3 + 1
        // fn(3) = 2 + 1
        // fn(2) = 1 + 1
        // fn(1) = 0 + 1
        // fn(0) = 0
        
        function fn(a){
        //     if(a === 1){
        //         return 1;
        //     }else{
        //         return a * fn(a-1);
        //     }
        // }
        // console.log(fn(10));

构造函数的原理

/* 构造函数创建对象:过程(细节) */
        function Person(name, age) {
            /* (1) 新创建空对象 */
            /* let  o = {} o.__proto__ -> Object.prototype */

            /* (2) 设置(修改)该对象的原型对象 */
            /* o.__proto__ = Person.prototype */

            /* (3) 把 o 绑定给 this */
            /* this = o */

            /* (4) 通过 this 来给实例对象添加属性或者是方法 */
            this.name = name || "张三";
            this.age = age || 18;

            /* (5) 默认总是会把内部新创建的实例对象返回 */
            // return this;

            /* 注意点: */
            /* 如果没有写 return 语句,那么默认总是会把内部心创建的实例对象返回 */
            /* 如果主动写 return 语句 */
            /* A:return 值类型的数据 忽略*/
            /* B: return 引用类型的数据 替换(覆盖) */
            return [1, 2, 3, 4];
            // return {};
            // return 123;
        }

        let p = new Person("老王")
        console.log(p);

        /* 需求:要求自己实现函数 mockNew(Person,"老王",44); */
        function mockNew(constructor) {
            let args = Array.from(arguments).slice(1);
            // console.log("args", args)
            let o = {};
            o.__proto__ = constructor.prototype;
            let result = constructor.call(o, ...args); /* 修改函数内部的 this */
            return result instanceof Object ? result : o;
        }

        let p1 = mockNew(Person, "老王", 44);
        console.log("p1", p1);

对象

1. 什么是对象

- 其实就是一种**事物的描述**,在程序中对象是一种数据类型
  - 描述水杯:
    - 材质:塑料
    - 容量:1000ml
    - 颜色:白色
    - 形状:圆形
    - 功能:容器
  - 把以上信息,组合打包用来表示一件事物,之后,就叫对象

- 程序中的对象主要用来:存储信息

2. 创建对象

- 字面量
 var o1 = {}

- 构造函数
  var o2 = new Object();

3. 对象的意义和本质

- 意义(功能)
  - 存储数据(信息)
  - 编程(面向对象编程)
- 本质
  - 键值对

4. 对象的操作

1.点语法

- 点语法(.)

  - 对象.属性名
      var o = {};
  - 读
      console.log(o.name);

  - 写
      o.name = "Admin";
      o.age = 18;
      o.sex = "男";
      o.sayHello = function(){
          console.log("你好,我叫:" + obj.name);
      }

- 字面量创建对象时,可以很方便的立即写入初始属性
  var obj = {
      name: "Admin",
      age: 18,
      sex: "男",
      sayHello:function(){
          console.log("你好,我叫:" + obj.name);
      }
  };

- 注意:**任何两个对象都不相等**

  - 如果相等了,意味着就是一个对象

2.中括号语法

用于不确定的属性名,当属性名是变量时
    // 操作:
    var obj = {
        name:"admin",
        age:18,
        sex:"男"
    };
    // 点语法:.
    // 只能访问固定的属性名,不能使用变量作为属性名
    console.log(obj.name);
    obj.name = "admin";
    console.log(obj.name);

    // 中括号语法:[]
    // 属性名为变量时,属性名为不确定的值时
    var attr = "name";
    console.log(obj[attr]);

    // 属性名为具体的值,不是变量时,也可以用[]语法,但是这个值必须用引号包裹
    console.log(obj["name"]);
    console.log(obj['age']);
    console.log(obj['sex']);

3.对象遍历for-in

for(var i in obj){
     i就是obj中的所有属性名
    思考:如何根据属性名获取属性值
     提示:中括号语法
}
var obj = {
        a:10,
        b:20,
        c:30,
        name:"admin",
        age:18,
        sex:"男",
        d:function(){},
        e:true,
        f:null,
        g:{
            a:"hello"
        }
    }
    // for-in不是循环,是一个遍历语句,遍历对象
    // 枚举对象所有的属性,根据枚举出的属性,获取属性值
for(var i in obj){
    
    console.log(obj[i]);
}


  // 小提示:数组也是对象
    // for-in可以遍历对象,可不可以遍历数组?
    // var arr = ["hello",4,5,6,"world"];
    // for(var i in arr){
    //     // console.log(i);
    //     console.log(arr[i]);
    // }
    

    // 意味着数组的遍历,也多了一种方式,但是,建议,for-in一般用来遍历非数组对象

数组

1. 什么是数组?

- 数据的组合,一组数据
- 当需要操作多个数据时,可以给数据打包,数组
  - 数组其实也是对象,只是存储数据的形式不一样,但是类型是一样的

2. 创建数组

- 字面量
  var arr1 = [];

- 构造函数
  var arr2 = new Array();

- 区别
- 当字面量内只有一个数值型数据时,表示一个数据
- 当构造函数内只有一个数值型数据时,表示长度
- 注意区别,按需选择,顺手	

3.数组 的操作

1.数组索引

- 数组是一个有序数据的集合
- 有序的序号,索引
-0开始
- 到哪结束
  - 根据长度,得出,到length-1
  - 数组的第一个:数组[0]
  - 数组的最后一个:数组[数组.length-1]
-- 增加数组的长度
  - 给不存在的位置赋值
  arr[arr.length] = "哈哈哈"

-- 缩短数组的长度
  // 原有长度是5
  arr.length = 3;

-******
  - 找到对应索引的数据,赋值(覆盖)
  arr[要修改数据的索引] = "四";

-******
  - 根据索引直接获取
  arr[指定位置的索引]
https://www.jianshu.com/p/7e160067a06c

2.数组方法

//1.push
push方法
        1.功能:最后的位置新增
        2.参数:要增加的数据,可以是多个
        3.返回值:数组增加之后的长度
        4.是否修改原数组:是
    var res = arr.push("aaa","bbb")
    console.log(res);

//2.unshift
    unshift方法
        1.功能:开始的位置新增
        2.参数:要增加的数据,可以是多个
        3.返回值:数组增加之后的长度
        4.是否修改原数组:是
    var res = arr.unshift("aaa","bbb")
    console.log(res);

//3.pop
    pop方法
        1.功能:删除最后一位
        2.参数:无
        3.返回值:删除的数据
        4.是否修改原数组:是
    var res = arr.pop()
    console.log(res);

//4.shift
    shift方法
        1.功能:删除第一位
        2.参数:无
        3.返回值:删除的数据
        4.是否修改原数组:是
    var res = arr.shift()
    console.log(res);

//5.join
    join方法
        1.功能:将数组转成字符
        2.参数:转成字符之后,每个数据之间的间隔符
        3.返回值:转换结果
        4.是否修改原数组:否
    var res = arr.join("");
    console.log(res);
    console.log(typeof res);
    字符的长度还没讲
    
//6.splice
    splice方法
        1.功能:删除并替换指定位置的数据
        2.参数:
            两个:1起始索引,2个数,不插入数据,只删除
            两个以上:1起始索引,2个数,删除,并,后面都是要插入的数据
        3.返回值:删除了的数据
        4.是否修改原数组:是
    var res = arr.splice(2,1);       // 从第2个位置开始删除1个
    var res = arr.splice(2,1,"aaa","bb","ccc");   // 从第2个位置开始删除1个后,插入后面的数据
    var res = arr.splice(2,0,"aaa","bb","ccc");   // 从第2个位置开始删除0个后,插入后面的数据
    var res = arr.splice(2,2);   // 从第2个位置开始删除2个
    console.log(res);

//7.slice
    slice方法
        1.功能:截取并拷贝出子数组
        2.参数:
            一个:表示从指定位置开始截取,到结束
            两个:表示从指定位置到结束位置的前一个
        3.返回值:截取并拷贝出来的新数组
        4.是否修改原数组:否
    var res = arr.slice(1,4);
    console.log(res);

    console.log(arr);


-----------------------

    var arr = ["hello",3,4,5,"world"];
    console.log(arr);

//8.concat
    concat方法
        1.功能:将数据合并到新数组并返回
        2.参数:任何数据,如果是数组的话,会被展开再合并进去
        3.返回值:合并之后的新数组
        4.是否修改原数组:否
    var res = arr.concat([4,5,6]);
    console.log(res);
 
//9.reverse
    reverse方法
        1.功能:翻转数据
        2.参数:无
        3.返回值:翻转之后的数组,就是修改之后的原数组
        4.是否修改原数组:是
    var res = arr.reverse();
    console.log(res);
    console.log(res == arr);     // true

    console.log(arr);

-------------------
    var arr = [7,4,328,6,1048,9];
    console.log(arr);

//10.sort
    sort方法
        1.功能:排序;默认排序规则是字符的比较规则,升序
        2.参数:函数:function(m,n){return n-m;}  m-n数值升序,n-m数值降序
        3.返回值:排序之后的数组
        4.是否修改原数组:是

    var res = arr.sort(function(m,n){
        return n-m;
    });

4.数组分类

    数值数组
    var arr = [3,4,5,7,2,3];

    字符数组
    var arr = ["a","b","c"];

    布尔数组
    var arr = [true,false];

    对象数组(json数组)
    var objArr = [{name:"admin"},{name:"root"},{name:"liyang"}]

    数组数组(二维数组)
    var twoArr = [[3,5,6,7,4],["a",true,"b"],["hello","world"]];

    访问方式(操作方式)

    JSON数组
        逐层解析

        var objArr = [{name:"admin"},{name:"root"},{name:"liyang"}];
        指定数据
        console.log(objArr[1].name);        // root

        所有数据
        for(var i=0;i<objArr.length;i++){
            console.log(objArr[i].name);
        }

     二维数组
        var twoArr = [[3,5,6,7,4],["a",true,"b"],["hello","world"]];
        // 指定数据
        // console.log(twoArr[2][0]);

        // 所有数据
        for(var i=0;i<twoArr.length;i++){
            // console.log(twoArr[i])
            for(var j=0;j<twoArr[i].length;j++){
                console.log(twoArr[i][j]);
            }
        }

5.冒泡排序

// 手动数组排序
    // 实际用处不太大
        // 因为数组有自己的排序方式
    // 但是,逻辑思路很重要
        // 数组的排序对应了排序算法
    
    // 简单的排序
   // var arr = [9,8,7,6,5];
  //  console.log(arr);
    // 不用sort
    // 自己写
    // 升序
    // 接下来我表示的是索引(下标)
    // 0-1,1-2,2-3,3-4
    // [8,7,6,5,9]
    // // 比完之后,排了什么,排了最后一个必然是最大值
    // 0-1,1-2,2-3
    // [7,6,5,8,9]
    // // 比完之后,排了什么,排了倒数第二个必然是倒数第二大的值
    // 0-1,1-2
    // [6,5,7,8,9]
    // // 比完之后,排了什么,排了倒数第三个必然是倒数第三大的值
    // 0-1
    // [5,6,7,8,9]
    // // 比完之后,排了什么,排了倒数第四个必然是倒数第四大的值

    // 以下结构是比较次数和哪些索引进行比较
    // 0-1,1-2,2-3,3-4
    // 0-1,1-2,2-3
    // 0-1,1-2
    // 0-1

    // 多行多列
    // 循环的嵌套
    // 外层循环控制行
    // 内层循环控制列
    var arr = [9,8,7,6,5];
    console.log(arr);
    
for(var i=0;iar[j+1]){
                 var ls=arr[j];
                 arr[j]=arr[j+1];
                 arr[j+1]=ls;
             }
    }
}
console.log(arr);

6.选择排序


    // // 思路:拿第一位和后面所有作比较,找到最小的,放在第一位
    // 0-1,0-2,0-3,0-4
    // [5,8,9,6,7]
    // // 第一轮结束后,第一个位置必然是最小的

    // // 思路:拿第二位和后面所有作比较,找到最小的,放在第二位
    // 1-2,1-3,1-4
    // [5,6,9,8,7]
    // // 第二轮结束后,第二个位置必然是第二小的

    // // 思路:拿第三位和后面所有作比较,找到最小的,放在第三位
    // 2-3,2-4
    // [5,6,7,8,9]
    // // 第三轮结束后,第三个位置必然是第三小的

    // // 思路:拿第四位和后面所有作比较,找到最小的,放在第四位
    // 3-4
    // [5,6,7,8,9]
    // // 第四轮结束后,第四个位置必然是第四小的

    // 一共有5个数据,确定了前四个,最后一个不需要比了	
// 以下结构是比较次数和哪些索引进行比较
    // 0-1,0-2,0-3,0-4
    // 1-2,1-3,1-4
    // 2-3,1-4
    // 3-4
     for(var i=0;i arr[j]){
                // ls里面的数据如果大了,换成小的
                // 因为在找最小的
                ls = arr[j];
                // 如果ls里面的数据换了,lsIndex也跟着换
                lsIndex = j;
            }
        }
        // ls必然已经是最小的了
        // 交换数组中的数据
        
        // 先把真正的第一位拿走,否则会被覆盖
        // 放到真正的最小值的位置
        arr[lsIndex] = arr[i];
        // 再把ls变量中,最小的值,放在第一位
        arr[i] = ls;
    }


    // 按照从左到右的顺序,依次取出每个数据,后后面所有数据做比较,找到最小值,交换
    // 选择排序
  var arr = [9,8,5,6,7];
    console.log(arr);

    for(var i=0;iarr[j]){
               ls=a[j];
               lsIndex=j;
               }
        }
        arr[lsIndex]=arr[i];
        arr[j]=ls;
    }

字符串

  • 只要被引号包括起来就是字符串,所有数据都可以转成字符串,只要被引号包裹即可
  • 引号:单引号’,双引号"------------反引号`

1.创建字符

- 字面量
  var str = "";
  var str = '';
  var str = ``;

- 构造函数
  var str = new String();

- 构造函数创建的字符,类型是object,但是可以使用正常的字符操作

2.字符的操作

- 字符和数组一定要区分开来,压根就不是一回事,仅仅是操作类似
- 索引和长度
  - length
  - 下标,序号,编号
  - 只能读,不能写
  
   var str = "hello world";
    console.log(str);
    // 所有的字符,都可以叫字符串,每个字符的索引
    // console.log(str[0]);
    // console.log(str[1]);
    // console.log(str[2]);
    // console.log(str.length);

    // for(var i=0;i

3.字符方法

  var str = "hello world";

//1.indexof  	
	indexOf()
        功能:根据指定子串,从左向右查询字符,查询索引
        参数:1要查询的子串,2从哪个索引开始查询,可选
        返回值:索引 或 -1(没有查找到)
        是否改变原字符:否
    var res = str.indexOf("w");
    var res = str.indexOf("a");
    var res = str.indexOf("w",7);
    var res = str.indexOf("l",4);
    var res = str.indexOf("l",2);
    console.log(res);

//2.slice
    slice()
        功能:截取
        参数:1开始的位置,2结束的位置(不包括),可选
        返回值:截取到的子串
        是否改变原字符:否
    var res = str.slice(2,7);
    var res = str.slice(2);
    console.log(res);

//3.substr
    substr()
        功能:截取
        参数:1开始的位置,2个数,可选
        返回值:截取到的子串
        是否改变原字符:否
    var res = str.substr(2,7);
    var res = str.substr(2);
    console.log(res);

//4,substring
    substring()
        功能:截取
        参数:1开始的位置,2结束的位置(不包括),可选
        返回值:截取到的子串
        是否改变原字符:否
    var res = str.substring(2,7);
    var res = str.substring(2);
    console.log(res);

//5.split
    split()
        功能:分割字符成数组
        参数:按照什么字符分割
        返回值:分割后的数组
        是否改变原字符:否
    var res = str.split("o");
    var str = "2020.4.30 14:30:50"
    var str = "2020|4|30"
    var res = str.split(".");
    console.log(res);

//6.charAt
    charAt()
        功能:根据索引取字符
        参数:索引
        返回值:取到的字符,没有取到,就是空字符:""
        是否改变原字符:否
    var res = str.charAt(4);
    var res = str.charAt(24);
    console.log(res);

//7.lastInexof
    lastIndexOf()
        功能:从右向左查询字符,返回索引
        参数:要走查找的子串
        返回值:索引,或-1
        是否改变原字符:否
    var res = str.lastIndexOf("l");
    console.log(res);
    
//8.concat
    concat()
        功能:合并字符
        参数:要合并的字符
        返回值:合并之后的字符
        是否改变原字符:否
    var res = str.concat("admin");
    console.log(res);

//9.toUpperCase
    toUpperCase()
        功能:转大写
        参数:无
        返回值:转换之后的字符
        是否改变原字符:否
    var res = str.toUpperCase();
    console.log(res);

//10.toLowerCase
    toLowerCase()
        功能:转小写
        参数:无
        返回值:转换之后的字符
        是否改变原字符:否
    var str = "HELLO 你好";
    var res = str.toLowerCase();
    console.log(res);

//11.repalce
    replace()
        功能:替换.  后续敏感词过滤
        参数:1老字符,2新字符
        返回值:替换之后的字符
        是否改变原字符:否
    var res = str.replace("o","啊");
    res = res.replace("o","啊");
    console.log(res);
//12.triem
作用:清除字符串前后的1个或者多个空格

3. 字符集的介绍(了解)

  • 字符集的概念,稍作了解
  • 转换方法必须要会 ***************
    • 字符转U编码
      • 要转换的字符.charCodeAt()
    • U编码转字符
      • String.fromCharCode(U编码)

栈堆

内存
- 用来运行数据的控件,内部有区分了不同的存储方式
  - 栈:一般用来存储变量名和地址,空间小,稳定,不可修改
  - 堆:一般用来存储数据,空间大,可被修改
  - 关系:一一对应,多对一,不能一对多
  - 存取规则:
    - 栈:先进后出:杯子
    - 堆:先进先出:掉底的杯子

数据类型的分类

数据类型的分类
     数据类型的存储形式:js中的数据类型的分类,只有两种。
    // 改变新的不影响老的:基本(一般)类型(值传递的数据)
        // 除了对象,函数
    // 改变新的影响老的:引用(复杂)类型(引用传递的数据)
        // 对象,函数
- 基本类型(值传递)
  - 数据就在栈中
  - 不存在深浅拷贝,默认就是深拷贝
- 引用类型(引用传递的数据)
  - 栈中只保存了一个地址,地址指向堆中的某个数据
  - 存在深浅拷贝,默认是浅拷贝
    - 目前会有一些影响
  - 如何实现深拷贝?
  //浅拷贝
      /* 对象的拷贝 */
        // let o1 = {
        //     name: "zs",
        //     age: 18,
        //     car: {
        //         id: 666,
        //         color: "red",
        //     }
        // };

        // let o2 = {};

        /* 浅拷贝:拷贝的时候只会拷贝一层,可能存在共享的关系 */
        /* 深拷贝:拷贝的时候逐层拷贝,不存在共享的关系 */

        /* 浅拷贝实现方式: */
        /* (1) 遍历的方式逐个拷贝 */
        // for (let key in o1) {
        //     o2[key] = o1[key];
        // }
        // o1.car.id = 888;
        // console.log(o1, o2, o1.car === o2.car);

        /* (2) Object.assign() 拷贝合并对象*/
        /* Object.assign(target,source1,source2,source3...) */
        /* 把source1 source2 source3... 对象成员都添加到target上面 */
        // Object.assign(o2, o1, {
        //     address: "广州"
        // });

        // o1.car.id = 9999;
        // console.log(o1, o2);


 /* 深拷贝 */
        /* (1) 利用 JSON.parse(JSON.stringify()) */
        /* 问题1:因为JSON字符串中没有undefined 和 函数,所以如果拷贝的对象内部包含了undefined 值或者是函数 */
        /* 问题2:如果拷贝的是正则表达式,那么拷贝完后得到的仅仅是{} */

        /* (2) 封装函数deepCopy 递归 */
        let o1 = {
            name: "zs",
            age: 18,
            car: {
                id: 666,
                color: "red",
            },
            className: undefined,
            show() {},
            reg: /abc/,
        };

        /*JSON.stringify()  把对象转换为JSON字符串 (序列化) */
        /*JSON.parse()      把JSON字符串转换为对象 (反序列化) */
        /* JSON字符串主要用来进行网络通信  是数据的表示和传输的格式(XML | JSON)。 */
        /* JavaScript提供JSON 对象,ES5提供(ECMAScript) */
        // let json = JSON.stringify(o1);
        // console.log(json);
        // let obj = JSON.parse(json);
        // console.log(obj);

        let o2 = JSON.parse(JSON.stringify(o1));
        console.log(o2);
        o1.car.color = "#000";
        console.log(o1, o2)


        let deepClone = (val, wm = new WeakMap) => {
            if (val == null) return val;
            if (typeof val !== "object") return val;
            if (val instanceof Date) return new Date(val);
            if (val instanceof RegExp) return new RegExp(val);
            if (wm.has(val)) return wm.get(val);

            let _instance = new val.constructor;
            wm.set(val, _instance);

            for (let key in val) {
                if (val.hasOwnProperty(key)) _instance[key] = deepClone(val[key], wm);
            }
            return _instance;
        }

全局对象window和this

1.widow

window

- 全局对象,所有的全局内容,都可以在window身上找到
- 一般情况下,window可以被省略

2.this

this

- 函数内的关键字
- 表示当前函数的执行上下文(执行对象)
- **谁执行了this所在的函数,this就是谁**
- 应用慢慢来,先记,规律,先测,规律

ES5的新增

- 什么是ES5,ECMAScript5
- ES的版本更新,向下兼容,老版本并没有全部废弃,新版本只是在老版本的基础上新增

1. 严格模式"use strict"

- 严格模式:更严格的开发模式
  - js的代码规范非常不严格,所以严格模式的意义是:
    - 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
    - 消除代码运行的一些不安全之处,保证代码运行的安全;
    - 提高编译器效率,增加运行速度;
    - 为未来新版本的Javascript做好铺垫

  - 严格模式的开启方式
    - 在作用域开始的位置,加上一行字符串:"use strict";
      - 局部严格
        - 安全,但不方便,麻烦
      - 全局严格
        - 不安全
      - 配合匿名函数
        - 利用父子作用域,本质是局部,但实现了全局的方便
#####
  - 严格模式开启之后的变更
    1. 声明变量必须加关键字(var)
    2. 不允许参数名重复
    3. 没有明确隶属对象的函数中的this(执行了)指向undefined
    4. arguments的内容不会被形参的改变而影响
    5. arguments.callee被禁止使用

- 记忆性知识点,改了就改了,没有商量的余地


2. 新增的数组的方法

 
    forEach
    功能:遍历数组
    参数:函数,这个函数自带三个形参,1数据,2索引,3数组自身
    返回值:undefined
    是否修改原数据:否
    var res = arr.forEach(function(val,idx,self){
        console.log(val,idx,self);
    })
    console.log(res);

    map
    功能:1遍历数组,2修改数据
    参数:函数,这个函数自带三个形参,1数据,2索引,3数组自身
    返回值:数组,中的数据是:每次遍历到数组中数据时的返回值
    是否修改原数据:否
    var res = arr.map(function(val,idx,self){
        console.log(val,idx,self);
        return val *= 1.3;
    })
    console.log(res);

    filter
    功能:1遍历数组,2过滤数据
    参数:函数,这个函数自带三个形参,1数据,2索引,3数组自身
    返回值:数组,中的数据是:每次遍历到数组中数据时的返回值,为true时的值
    是否修改原数据:否
    var res = arr.filter(function(val,idx,self){
        console.log(val,idx,self);
        return typeof val === "number";
    })
    console.log(res);

    some
    功能:1遍历数组,2检测所有数据(只要有一个返回true,就是true,同时停止遍历)
    参数:函数,这个函数自带三个形参,1数据,2索引,3数组自身
    返回值:布尔值,遍历数组中数据时,只要有一个返回true,就是true,同时停止遍历
    是否修改原数据:否
    var res = arr.some(function(val,idx,self){
        console.log(val,idx,self);
        return val === 4;
    })
    console.log(res);

    every
    功能:1遍历数组,2检测所有数据(必须所有返回true,才是true)
    参数:函数,这个函数自带三个形参,1数据,2索引,3数组自身
    返回值:布尔值,遍历数组中数据时,必须所有返回true,才是true,只要有一个false,就是false,停止遍历
    是否修改原数据:否
    var res = arr.every(function(val,idx,self){
        console.log(val,idx,self);
        return true;
    })
    console.log(res);

    reduce
    功能:1遍历数组,2归并
    参数:
        1.函数,这个函数自带四个形参,1上一个值,2数据,3索引,4数组自身
        2.指定默认情况下      的   上一个值↑;如果没有指定,上一个值,默认找数组的第一个数据
    返回值:最后一个遍历的返回值
    是否修改原数据:否
    var res = arr.reduce(function(prev,val,idx,self){
        console.log(prev,val,idx,self);
        return "返回给了下次执行时的prev";
    },"prev的默认值,如果没有,prev找数组的第一个数据")
    console.log(res);

    案例:利用reduce实现数组中所有数据的累加
    var arr = [2,3,4,5,6,7];
    var res = arr.reduce(function(prev,val,idx,self){
        return prev + val;
    })
    console.log(res);
  • 数组的常用方法大全:https://www.jianshu.com/p/7e160067a06c

如何调bug

思想:调错思想

工具:打断点

    1.程序有报错,结果没出来
        1.观察报错
        2.根据报错找到指定行号
        3.观察哪错了
            3.1不一定是报错行号的代码有问题,有可能是之前的问题,在此行暴露
                3.1.1向上查找来源
                3.1.2定位真正的问题所在
            3.2找来源
        4.修改正确

    2.程序有报错,但结果出来了,可是不对
        1.观察报错
        2.根据报错找到指定行号
        3.观察哪错了
        4.找到最终结果
        5.向前反推
        6.观察数据的来源
        7.根据数据的来源,计算规则
        8.观察程序的执行过程和代码是否正确
        9.修改正确

    3.程序无报错,但结果出来了,可是不对
        1.找到最终结果
        2.向前反推
        3.观察数据的来源
        4.根据数据的来源,计算规则
        5.观察程序的执行过程和代码是否正确
        6.修改正确

    4.程序无报错,结果没出来

        1.重新写:
            1.1至少可以将思路捋一遍
            1.2再重新写

        2.反推:要求对当前程序的了解程度非常高
            ...


            观察工具:
        判断当前位置的代码是否执行:console.log(1);
            一定要在报错行号上面打印,因为报错会阻塞程序执行
        判断当前变量或数据是否正常:console.log(变量或数据)
            一定要在报错行号上面打印,因为报错会阻塞程序执行
    
    逐行执行代码:
        打断点,让代码逐行执行
            1.关键字:debugger
            2.利用浏览器的控制台


    仅仅是让代码逐行执行,并不能告诉我们哪里错了,只是看到执行过程
    根据自身对当前程序的了解,判断程序的执行

对象分类

js的对象的分类

- 内置对象:
  - ES提供可以直接被使用的对象,已经是一个具体的实例了,不需要创建
  - Math对象,数学对象
- 本地对象:
  - 其实只是ES提供的一种类方法,需要被实例化之后,才能使用
  - 需要被new执行之后,才能使用
  - String,Object,Array,Number,Boolean,RegExp,Date
- 宿主对象:
  - 由浏览器或W3C提供的运行ES的环境,所带的对象
    - BOM:window,DOM:document
   

1:delete

补充关键字:delete,用来删除自定义对象的属性

- delete
  - `delete 对象.属性名`;
- 只能删除自定义对象的属性,且一次只能删一个属性
- 建议:少用

2:Math对象

Math数学对象,不需要数学知识支撑,使用这个对象直接得到想要的结果
- Math 是 js 的一个内置对象,提供了一堆的方法帮助我们操作数字
- 属性
  - Math.PI
    - 圆周率
- 方法
  - Math.random()
    - 随机数,随机产生0-1之间的数,不包括0,1
  - Math.round()
    - 取最近的整数
  - Math.ceil()
    - 向上取整
  - Math.floor()
    - 向下取整
  - Math.max()
    - 参数是多个数字,求最大数字
  - Math.min()
    - 参数是多个数字,求最小数字
  - Math.pow(底数,指数)
    - 求幂
  - Math.sqrt()
    - 开根号
  - Math.abs()
    - 求绝对值
  - Math.sin(弧度)
    - 正弦
  - Math.cos(弧度)
    - 余弦
  - .....
    - console.log(Math);

3:得到范围随机数

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

进制的转换

1:十进制转其他

- 十进制是日常使用进制,是数值型数据
- 其他进制,如:十六进制中含有abcdef,所以需要用字符的形式表示
- 其实就是将数值转成字符
  - num.toString(需要转成的进制)
var num = 234;
console.log(num);

var er = num.toString(2);
console.log(er);

var ba = num.toString(8);
console.log(ba);

var sl = num.toString(16);
console.log(sl);

2:其他转十进制

- 其他都使用字符表示
- 其实就是将字符转成数值
  - parseInt(要转换的数据, 作为哪个进制转换)
var str = "101010";
//第一个参数是需要转的字符,第二个参数是告诉你是 什么进制 转 十进制
console.log(parseInt(str, 2));      // 42
console.log(parseInt(str, 8));      // 33288
console.log(parseInt(str, 16));     // 1052688
console.log(parseInt(str, 10));     // 101010
console.log(parseInt(str));         // 101010

3:其他转其他

- 利用十进制做过渡

- u编码:得到的直接就是十进制,被系统自动转了
- 颜色值

日期对象

Date日期对象,用来获取当前系统日期

是本地对象,本地对象的特点是不能直接使用,这仅仅是个类方法(类似于函数),需要new执行后才能使用

创建日期对象

系统的默认日期

var d=new Date();

1:日期对象

var d=new Date();
var year = d.getFullYear();
var month = d.getMonth();       // 0~11
var myDate = d.getDate();
var day = d.getDay();           // 0~6
var hours = d.getHours();
var minutes = d.getMinutes();
var seconds = d.getSeconds();
var millS = d.getMilliseconds();

2:设置日期对象

- 需求:计算北京奥运会距离今天过去了多少天?
- 过去的日期怎么拿?设置指定日期
- 如何设置
  - 直接设置
    - 简单粗暴,但必须整体设置
    - `new Date(要设置的数据)`

2.1.通过字符设置

字符:一个参数,直接设置,不允许日期溢出,否则会无效的日期,没有设置的部分,为0 月份1-12
var d = new Date("2008.8.8");
var d = new Date("2008/8/8");
var d = new Date("2008-8-8");
var d = new Date("2008,8,8");
var d = new Date("2008 8 8");
var d = new Date("2008.14.8 8:8");
var d = new Date("2008.8.8 8:8");

2.2.通过数值设置

数值:多个参数,直接设置,月份从0开始,日期溢出,向前进1,没有设置的部分,为0 月份0-11
var d = new Date(2008,8,8);
var d = new Date(2008,7,8);
var d = new Date(2008,15,8);
var d = new Date(2008,15,8);
var d = new Date(2008,15,8,20,8,8);

2.3.通过方法设置

通过方法设置,繁琐,但可以针对性设置,没有设置的部分,采取当前日期,月份从0开始,溢出部分,向前进1
d.setFullYear(2008);
d.setMonth(14);
d.setDate(8);

d.setHours(8);
d.setMinutes(8);
d.setSeconds(8);
d.setMilliseconds(666);

2.4.时间戳设置

还可以独立设置时间戳
d.setTime(1970.....到要设置的日期的毫秒数)
  		/* d.getTime() */
        /* d.valueOf() */
        /* Date.now() */

2.5.set系列设置

单独设置,采取日期对象的系列方法:set系列

    没有设置的部分,采取当前日期,月份从0开始,溢出部分,向前进1

    var d = new Date();
    console.log(d)

    d.setFullYear(2008);
    d.setMonth(14);
    d.setDate(8);
    
    d.setHours(8);
    d.setMinutes(8);
    d.setSeconds(8);
    d.setMilliseconds(666);

    console.log(d)
    
    console.log(d.getMilliseconds());


    通过时间戳直接设置日期
    var d = new Date();
    d.setTime(1000000000000);

    console.log(d);

BOM

1. 概念
   - BOM:浏览器对象模型,是个模型,这个模型由一个对象window来体现(具象)
   - 作用:
     - 提供了ES和页面的运行环境
     - 浏览器的一些规定,首先需要在ES的基础之上设置,但是最终生效时,以浏览器的标准为主
       - 浏览器的规范没有涉及到的范围,使用ES的规范
       - 宪法:ES,地方法规:浏览器的规定
       - 兼容:
         - 不是兼容问题的兼容问题:
           - alert(1);
           - 功能一致,没有兼容
           - 每个浏览器显示的样式都不一样
2. window的具体功能和作用
   - window:窗口
   - window提供的窗口 或 窗口与窗口之间的操作
   - 因为所有内容都运行在浏览器中,所以,window也是**浏览器的js**中的顶层(大boos)对象
     - 所有的全局变量和函数,都属于window
     - window在使用过程中可以省略
     - 注意,严格模式下,全局函数如果省略window,会导致全局函数中的this指向undefined

方法(内置全局函数)

1.1.弹出框三兄弟

- 弹出信息框:alert()  // 弹出信息框
- 弹出对话框:prompt() // 弹出对话框   返回值:确定:输入的信息;取消:null
- 弹出选择框:confirm() //弹出选择框  返回值:确定:true 取消:false
- 注意原理,将来会使用html+css+js模拟

1.2.open和close

- open()
- close()
- 注意:
  - 尽量配合行为适用,不要直接执行
  - 而且某些浏览器还有兼容性
  
  // 有兼容,某些浏览器不支持
    // open();        // 打开指定地址的新窗口
    // close();       // 关闭当前窗口
    // 火狐浏览器要求:不允许关闭非脚本(js)打开的页面
    // javascript就是一个脚本语言
    
    // document.onclick = function(){
    //     open("http://www.baidu.com")
    // }
    // document.onclick = function(){
    //     if(confirm("你真的要关闭当前页面吗?")){
    //         close();
    //     }
    // }


1.3.计时器

1. - 计时器(定时器)(重复执行)
     -- setInterval()
       - 功能:每隔参数2的时间,执行一次参数1的函数
       - 参数:参数1:函数;参数2:毫秒数
       - 返回值:当前计时器的唯一标志,是个数值,只有一个作用:用来清除当前计时器!!!
       - 是否改变原数据:否
       var t = setInterval(function(){
           console.log("hello");
       }, 1000);

     -- clearInterval()
       - 功能:用来关闭计时器
       - 参数:要关闭的计时器的返回值 所在的变量!切记,变量!
       - 返回值:无
       - 是否改变:否

       document.onclick = function(){
           clearInterval( t );
       };

   - 延时器(延迟,一次)
     -- setTimeout()
       - 功能:延迟 参数2的时间,执行一次参数1的函数
       - 参数:参数1:函数;参数2:毫秒数
       - 返回值:当前延时器的唯一标志,是个数值,只有一个作用:用来清除当前延时器!!!
       - 是否改变原数据:否
       var t = setTimeout(function(){
           console.log("hello");
       }, 5000);


     -- 功能:用来关闭延时器
       - 参数:要关闭的延时器的返回值 所在的变量!切记,变量!
       - 返回值:无
       - 是否改变:否
       clearTimeout()

内置全局事件

- window浏览器事件
  - load:加载
    - window.onload不仅是页面的加载,还在等待资源的加载
    - document.onload仅仅是页面的加载
  - resize:改变大小
  - scroll:滚动
- 语法:
  - 事件源.on事件名 = 事件处理函数
  - 如:btn.onclick = function(){}
  - 如:window.onlaod = funciton(){}
  - 如:onlaod = funciton(){}
  
    // 获取页面尺寸:宽,高
    // 可视区域的大小,可以配合resize使用
 		 //     console.log(document.documentElement.clientWidth)
 		 //     console.log(document.documentElement.clientHeight)
  
  
   // 获取滚动条,滚动的距离(滚走的距离),可以配合scroll使用
        // console.log(document.documentElement.scrollTop)
        // console.log(document.documentElement.scrollLeft)

子对象(内置对象)

history:历史记录

- 控制浏览器的**前进后退刷新**等于历史记录相关的功能
- 不方便在代码中临时测试,所以在浏览器打开其他页面测试
- 属性:
  - 历史记录的个数:history.length
- 方法:
  - 前进:history.forward()
  - 后退:history.back()
  - 前进或后退指定的步数:history.go(num);   num为正前进,为负后退,0刷新

location:地址url

- 控制浏览器的**地址栏的内容**
- 不方便在代码中临时测试,所以在浏览器打开其他页面测试
- 属性:
  - 既可以获取又可以设置:当前页面的地址,设置之后,页面会自动跳转
    - js实现跳转的方式:
    - location.href
    
  - 既可以获取也可以设置:当前页面地址中向后台发送的数据部分(?后的部分),页面也会跳转
    - location.search
    
  - 既可以获取也可以设置:当前页面地址的锚点连接(哈希值,#后的部分),页面也会跳转
    - location.hash
- 方法:
  - 重新加载当前地址,意味着刷新了
    - location.reload()
    
  - 跳转方法,必须传参,空字符刷新,指定地址会跳转
    - location.assign("")

navigator:浏览器信息

- 以下测试是在Mac OS X 10_15_4系统的Chrome/81.0.4044.129浏览器进行的测试
- navigator.userAgent:浏览器信息
  - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
- navigator.appName:浏览器名字(废弃)
  - "Netscape"
- navigator.appVersion:浏览器版本
  - "5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
- navigator.appCodeName:浏览器内核
  - "Mozilla"
- navigator.platform:运行系统
  - "MacIntel"

screen:屏幕尺寸

  • 显示器的分辨率

frames:框架

需要配合iframe标签使用

<iframe src="http://www.baidu.com" width="500" height="300" style="border:solid 2px black;">iframe>
<iframe src="http://www.qq.com" width="500" height="300" style="border:solid 2px black;">iframe>