javascript笔记知识(可能有错误、欢迎指正)

常用的浏览器

  • webkit内核(v8)引擎

    • 谷歌chrome
    • safari
    • opera(v14)
    • 国产浏览器
    • 手机浏览器
  • Gecko

    • 火狐Firefox
  • presto

    • opera
  • Trident

    • IE
    • IE EDGE(开始采用双内核)

JS做客户端语言

  • 按照相关语法来操作页面中的元素,有时还要操作浏览器
  • ECMAScript3/5/6…:JS的语法规范
  • DOM(文档对象模型):提供一些JS的属性和方法,用来操作页面中的DOM元素
  • BOM(浏览器对象模型):提供一些JS的属性和方法,用来操作浏览器的BOM元素

JS中的变量 variable

变量:可变的量,在编程语言中,就是一个名字,用来存储和代表不同值的东西

//ES3
var a = 12;
a = 13;
console.log(a);//输出的是a,值为13

//ES6
let b = 100;
b = 200;

const c = 1000;
c = 2000;//报错,因为const创建的值不能被修改(可以理解为常量)
=========================================
``**但是**``如果const定义的对象是引用数据类型,则const只能保证指针不变而修改引用类型的值还是可以变化
=========================================

// const person = {
     name : '老王',
     sex : '男'
 }
 
 person.name = '老李'
 
 console.log(person.name)//输出老李

person = {
   name : 'test',
   sex : '男'
}//则会报错

//创建函数也相当于创建变量
function fn(){}
//创建类也相当于创建变量
class A{}
//ES6的模块导入也相当于创建变量
import B from './B.js'
//Symbol 创建唯一值
let n = Symbol(100);
let m = Symbol(100);
n==m;//会返回false

JS中的命名规范

  • 严格区分大小写
  • 使用数字、字母、下划线、$,数字不能开头
let $box//一般用jquery获取以$开头
let _box//一般公共变量都是_开头
  • 驼峰命名法:首字母小写,其余每个单词首字母大写(命名尽可能语义化)
  • 不能使用关键字和保留字

JS中常用的数据类型

  • 基本数据类型
    • 数字number
      常规数字和NAN
    • 字符串string
      所有用单引号、双引号、反引号包起来的都是字符串
    • 布尔型Boolean
      true和false
    • 空对象指针null
    • 未定义类型undefined
  • 引用数据类型
    • 对象数据类型object
      • {}普通对象
      • []数组对象
      • /^$/正则对象
      • Math数学函数对象
      • 日期对象
    • 函数数据类型function

number数字类型

包含:常规数字、NaN

NaN

not a number:不是一个数,但是隶属于数字类型
NaN和任何值(包括自己)相比较,都不相等,NaN!=NaN,所以我们不能用相等的方式判断是否为有效数字

isNaN

检测一个值是否为有效数字,如果不是有效数字则返回true,否则返回false

在使用isNaN来检测时,会首先验证检测的值是否为数字类型,如果不是,先基于Number()这个方法,把值转化为数字类型,然后再检测
ex:console.log(isNAN('10'))//返回false,先Number('10')变成10,isNaN(10)返回false
ex1:console.log(isNaN('AA'))//先进行Number('AA')变成NaN,然后isNaN(NaN),结果返回true

使用Number()来转换

  • 字符串转数字

console.log(Number(‘12.5’))//结果为12.5
console.log(Number(‘12.5px’))//结果为NaN
console.log(Number(‘12.5.0’))//结果为NaN
console.log(Number(’’))//空串结果返回0,注意空串不等同于空格

  • 布尔转数字

console.log(Number(true))//结果为1
console.log(Number(false))//结果为0
console.log(Number(isNaN(true)))//结果也为0,过程则是先判断isNaN(true),将true用Number(true)变为1,然后isNaN(1),返回false,结果就是Number(false)返回0
console.log(isNan(true))//结果返回false

  • 空指针类型转数字

console.log(Number(null))//结果为0

  • 未定义类型转数字

console.log(Number(undefined))//结果为NaN

  • 引用数据类型转数字//把引用数据类型转化为数字采用toString方法先转化为字符串,然后再转化为数字

console.log(Number({}))//结果为NaN,过程先是({}).toString()转化为"[object Object]",然后再Number("[object Object]"),返回NaN
console.log(Number([]))//结果为0,过程也是先将数组使用toString()转化为"",然后Number(""),结果返回0
console.log(Number([10]))//结果为10
console.log(Number([1,2]))//结果为NaN

Number()底层机制总结

Number()是按照浏览器从底层机制,把其他数据类型转换为数字
字符串:看是否包含非有效数字字符,包含其结果就是NaN,其中""结果为0
布尔:true->1 false->0
null->0
undefined->NaN
引用类型值都要先转换为字符串,再转换为数字

  • {} /正则/ 函数等->NaN
  • []->""->0
  • [“12”]->“12”->12
  • [12,13]->“12,13”->NaN

使用parseInt()和parseFloat()

  • 他们都是转化数字类型,但是专门针对字符串进行查找,规则是从左到右依次查找有效数字,直到遇到非有效数字字符,则停止查找(不管后面是否还有有效数字,都不再查找)

parseInt/Float()转换机制

它们都是遵循按照字符串从左到右查找的机制找到有效数字字符(所以传递的值一定是字符串,如果不是字符串也一定要转换为字符串然后再查找)
parseInt(undefined)->parseInt(“undefined”)->NaN
parseInt("")->NaN(因为没有找到有效数字,如果是Number()方法则会返回0)

使用==进行比较

  • ‘10’==10//结果返回true,将字符串’10’转换为数字10

String字符串数字类型

所有用单引号、双引号、反引号包起来的都是字符串

把其它类型值转换为字符串

  • [val].toString()//注意null和undefined不能直接toString,普通对象{}.toString不是直接加引号,而是变成"[object Object]"
  • 字符串拼接
    console.log(‘10’+10)//输出结果为1010
    console.log(‘10’-10)//输出结果为0
    console.log(‘10px’-10)//输出结果为NaN,'10px’会被浏览器底层使用Number()方法将’10px’转换为NaN,再进行运算
    ex:
    let a = 10 + null + true + [] + undefined+ ‘你好’ + null + [] + 10 + false
    console.log(a)
    //10 + null ->10
    //10 + true ->11
    //11 + [] ->11 + ‘’ ->‘11’
    //‘11’ + undefined -> ‘11undefined’
    //‘11undefined’ + ‘你好’ -> ‘11undefined你好’
    //‘11undefined你好’ + null -> ‘11undefined你好null’
    //‘11undefined你好null’ + [] -> ‘11undefined你好null’
    //‘11undefined你好null’+ 10 -> ‘11undefined你好null10’
    //‘11undefined你好null10’ + false -> ‘‘11undefined你好null10false’’

boolean布尔数据类型

只有两个值,true和false

其他数据类型转换为boolean

除了0、NaN、’’、null、undefined五个值转为false,其余都为true(没有任何特殊情况)

转换方法

  • Boolean(val)
  • !/!!
  • 条件判断
if(1){
   console.log("你好");
 }//1转换boolean类型为true,则输出

 if('3px' + 3){
   console.log("你好");
 }//'3px' + 3 进行字符串拼接,变为'3px3',然后再转换为Boolean值为true,最后输出

 if('3px - 3'){
   console.log("你好");
 }//'3px' - 3 进行四则运算,结果为NaN,再转换为Boolean为false,结果则是不输出
**`ex:`**
console.log(Boolean(0))//false
console.log(Boolean(''))//false
console.log(Boolean(' '))//true
console.log(Boolean(null))//false
console.log(Boolean(undefined))//false
console.log(Boolean([]))//true
console.log(Boolean([10]))//true
console.log(Boolean(-1))//true
console.log(!1)//结果为false,先转为Boolean,再取反
console.log(!!1)//结果为true,!!取反再取反,等于没取反,只是转为Boolean

null和undefined

都表示没有

  • null一般都是意料之中(开始不知道数值,手动设置为null,后期再给予赋值)
let num = null;//定义了一个变量,赋值null
  • undefined一般都是意料之外的
let num;//定义了一个变量没有赋值,则默认undefined

object对象数据类型-普通对象

{[key]:[value],…}任何一个对象都是由零到多组键值对(属性名:属性值)组成的,并且属性名不能重复
获取对象的方法:

  • 对象.属性名/对象[属性名]
let person = {
  name:'小王',
  age:'20',
  sex:'boy',
  height:'185cm',
  1:'100分'
} //person.name/person['name']都可以获取到name这个属性

  • 如果获取的属性名不存在,则默认为undefined
console.log(person.weight)//结果为undefined
  • 如果一个对象内的属性名为数字,则不能使用对象.属性名来获取
console.log(person['1'])//输出100分
console.log(person.1)//SyntaxError语法错误
  • 添加属性值
person.girlFriend = '小玉'
console.log(person.girlFriend)//输出小玉
  • 删除属性值
    • 假删除(属性还在,值为空):
    person.name = 'null'//给person内的name属性置为空
    
    • 真删除(属性都没了)
    delete person['1']//给person内这个1属性删除
    

object对象数据类型-数组对象

数组是特殊的对象数据类型

let ary = [10,20,'你好',true]//相当于下面这样
let ary = {
  0:10,
  1:20,
  2:'你好',
  3:true,
  length:4
}//所以我们在数组中写的内容都为属性值,而数组的属性名从0开始依次递增

索引数组

console.log(ary.length-1)//索引数组最后一个元素内容
console.log(ary[0])//索引数组第一个元素内容

添加数组内容

ary[ary.length] = 5;//在数组末尾添加一个内容为5的属性值,数组长度自增
console.log(ary);

基本类型和引用类型区别

基本类型按值操作
引用类型按引用地址操作
ex:

	let a =12;
    let b = a;
    b = 13;
    console.log(a);//输出12

    let n = {
        name:"boy"
    }

    let m = n;
    m.name = "girl";
    console.log(n.name);//输出girl

JS中的数据类型检测

  • typeof [val]:用来检测数据类型的运算符,它不是函数
    • 基于typeof检测出来的结果

    1.首先是一个字符串
    2.字符串中包含对应类型

    • typeof局限性

    1.typeof null => “object”,但是null并不是对象
    2.基于typeof无法细分出当前值是普通对象还是数组对象等,因为只要是对象数据类型,返回的结果都是"object"
    ex:

    console.log(typeof typeof typeof [])//输出结果为"string",最里面的typeof []返回结果为"object",外层的typeof检测为"string"
    
  • instanceof:用来检测当前实例是否属于某个类的实例
  • constructor:基于构造函数检测数据类型(也是基于类的方法)
  • Object.prototype.toString.call():检测数据类型最好的方法

JS中的操作语句:判断、循环

判断

条件成立做什么?条件不成立做什么?

  • if/elseif/else
  • 三元运算符

条件?条件成立处理的事情:条件不成立处理的事情
如果处理的事情比较多,用括号包裹,每一件事用逗号分隔
如果不需要处理事情,则用null或者undefined占位

let a = 10;
a >=0 && a<20 ? (a++,console.log(a)):null
  • switch case

一个变量在不同值情况下的不同操作
在每种case结束时,最好加上break,不加break后面条件不管是否成立都会执行,直到遇到break为止
每种case情况的比较都是用的===“绝对相等”,而ifelse的情况比较使用的是==“相等”
=========================================================

  • =比较

==:相等(如果两边的数据类型不相同,则会转化为相同的数据类型,再进行比较)
=:绝对相等(如果两边类型不相同,肯定不相等,不会默认转换数据类型),项目中为了保证业务严谨性,推荐使用=

双等号==:

(1)如果两个值类型相同,再进行三个等号(===)的比较

(2)如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:

1)如果一个是null,一个是undefined,那么相等

2)如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较

三等号===:

(1)如果类型不同,就一定不相等

(2)如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)

(3)如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。

(4)如果两个值都是true,或是false,那么相等

(5)如果两个值都引用同一个对象或是函数,那么相等,否则不相等

(6)如果两个值都是null,或是undefined,那么相等

=======================================================

let b= 1;
switch(b){
  case 1:
    console.log('执行条件为1的事件');
    break;
  case 2:
    console.log('执行条件为2的事件');
    break;
  case 3:
    console.log('执行条件为3的事件');
    break;
  default:
    console.log('上述条件都不满足时执行的事件');
}//输出结果为'执行条件为1的事件'

//当不同的条件需要做相同的事情时就可以利用不加break来实现
switch(num){
  case 1:
  case 2:
    num++;
    break;
  default:
  console.log(num);
}//这段代码就能够让当num=1和num=2时,都能执行num++这个条件

i++与++i的区别(i++与i+=1以及i=i+1之间的区别)

i++是先拿值进行操作,最后再自增
++i是先将该值自增,最后再拿自增后的值进行计算
i++是纯粹的进行数学计算,而i+=1以及i=i+1如果i为字符串则会进行拼接

**ex:**

let i="10";
i=i+1;// => i="101"
i+=1// => i="101"
i++// => i="11"

i–与--i的区别

区别与i++与++i一样,但是i-=1与i=i-1没有区别,因为减法操作只有数值运算

简单的DOM元素操作

传统操作DOM的方式实现业务需求
1.想操作谁就先获取谁
2.给某元素绑定某事件
3.事件触发时候做些什么事情

  • document.getElementById([ID]):在整个文档中,通过元素的ID获取到当前这个元素对象
  • 元素对象.onxxx=function(){}:代表事件绑定,xxx事件类型(click/mouseover/mousedown/keydown…)
  • 元素对象.style.xxx=xxx:修改元素某个样式值
    ex:



    
    鼠标点击



    
购物车
<=script> let box = document.getElementById("shopbtn");//获取外部盒子点击的目标 let detail =document.getElementById("content");//将显示目标赋给变量detail box.onclick = function(){ let n = detail.style.display;//将detail的样式值获取赋给变量n if(n==="none"){ //判断n当前的值为none还是block detail.style.display="block"; box.style.borderBottomColor = "white"; }else{ detail.style.display="none"; box.style.borderBottomColor = "red"; } }

循环

重复做某些事情

  • for循环
    • 1.创建初始循环值
    • 2.设置(验证)循环执行的条件
    • 3.条件成立执行循环体中的操作
    • 4.当前循环结束执行步长累计操作
    • 5.循环嵌套循环时外循环执行一次,内循环执行全部
      ex1:
for (int i = 0; i < 4; i++ )
  {
    System.out.println("i==>"+i);
    for (int j = 0; j < 3; j++ )
    {
      if( j==1){
        continue;
      }
      System.out.println(j);
    }
  }

//输出结果为:
i==>0
0
2
i==>1
0
2
i==>2
0
2
i==>3
0
2

ex2:

for (var i=10;i>4;i-=2){
  if(i<7){
    i++;
  }else{
    i--;
  }
}
console.log(i);
//第一次:i=10判断i>4?,结果成立执行循环体操作,i=10判断i<7?,不成立执行i--,此时i=9,最后执行i-=2,i=7;
//第二次:i=7判断i>4?,结果成立执行循环体操作,i=7判断i<7?,不成立执行i--,此时i=6,最后执行i-=2,i=4;
//第三次:i=4判断i>4?,结果不成立退出循环输出结果最终i=4

循环中的两个关键词:

  • continue:结束当前这轮循环(continue后面的代码不再执行),继续执行下一轮循环
    -break:强制结束整个循环(break后面的代码也不再执行),整个循环啥也不干直接结束
    ex3:
//question:一共输出几个i,分别是多少?
for(var i=0;i<10;i++){
  if(i>=2){
    i+=2;
    continue;
  }
  if(i>=6){
    i--;
    break;
  }
  i++;
  console.log(i);
}
console.log(i);
//i=0,判断i<10?,成立判断i>=2?,不成立,判断i>=6?,不成立,执行i++,输出i为1,最后循环自增变为2.
//i=2,判断i<10?,成立判断i>=2?,成立,执行i+=2,i=4,遇见continue后面代码不执行,循环自增1,i=5.
//i=5,判断i<10?,成立判断i>=2?,成立,执行i+=2,i=7,遇见continue后面代码不执行,循环自增1,i=8.
//i=8,判断i<10?,成立判断i>=2?,成立,执行i+=2,i=10,遇见continue后面代码不执行,循环自增1,i=11.
//i=11,判断i<10?,不成立,循环结束,执行最后一个输出语句,输出i=11;
//一共输出两个i,第一个i=1,第二个i=11.
  • for in循环
  • for of循环
  • while循环
  • do while循环

堆栈内存指向问题

box.style.color="red";
//下面的几种方式是否都可以修改元素样式
//第一种方式
let a = box.style;
a.color="red";//可以
//第二种方式
let b = box.style.color;
b = "red";//不可以

javascript笔记知识(可能有错误、欢迎指正)_第1张图片

  • docment.getElementByID方式获取的元素是对象数据类型的值
    //我们可以通过console.log(typeof box)检测出是object类型
  • 基于.dir可以看到一个对象的详细信息,本质上也是一个对象,包含对象的属性名和属性值
    • id:操作元素的ID值
    • className:操作元素的class类样式值
    • innerHTML:操作元素的内容(可以识别标签)
    • innerText:和innerHTML的区别是无法识别标签
    • style:操作元素的行内样式
    • tagName:获取元素的标签名(一般大写)

      //我们可以通过console.dir(box)来查看一个元素的所有属性

奇偶行变色




    
    奇偶行变色
    


    
  • 我是第1个li
  • 我是第2个li
  • 我是第3个li
  • 我是第4个li
  • 我是第5个li
<=script> // let liBox=document.getElementById("liBox"); let newsList=liBox.getElementsByTagName("li"); //1.实现奇偶行变色 //循环获取每一个li,判断每个li奇偶行,让其拥有不同的颜色 for(let i=0;i

函数 function

函数就是一个方法或者一个功能体,函数实际上就是把实现某个功能的代码放到一起进行封装,以后想要操作实现该功能,只需要把函数执行即可=>“封装”,减少代码的冗余,提高代码的重复使用率,也就是高内聚,低耦合

洗衣机就是一个函数,生产洗衣机就是封装一个函数,生产的时候不知道用户在洗衣服的时候会放什么水、衣服、洗衣液等,我们需要提供出入口(提供的入口在函数中叫形参,执行中具体放入的东西叫做实参),洗完衣服需要拿出来,洗衣机提供一个出口(函数中叫做返回值:把函数处理的结果能够返回给外面使用)

  • 创建函数
    • 形参
    • 返回值
  • 执行函数
    • 实参
  • arguments
  • 函数底层运行机制

  • javascript笔记知识(可能有错误、欢迎指正)_第2张图片

创建函数

//es5老方式
function [函数名]([形参变量1],[形参变量2]...){
  //函数体:基于JS实现的功能;
  return [处理后的结果];
}

//执行函数
[函数名]([实参1]...);

ex:

//定义一个函数,求两个数的和,然后将该值乘以10再除以2,将结果返回输出
function sum(n,m){
  //初始化形参
  if(n === undefined){
    n = 0;
  }
  if(typeof m === "undefined"){
    m = 0;
  }//跟上面初始化方式一样,一般采用这种
  let res = n+m;
  res *= 10;
  res /= 2;
  console.log(res)
}
sum(1,2);//调用函数,传递两个实参1和2

函数返回值问题

函数如果不设置返回值,则外部调用函数则会默认undefined
返回值return返回的一定是值
函数体中遇见return不一定有返回值,也可能是后面代码不再执行

<=script>
        function sum(m,n){
            //初始化变量
            if(typeof m==="undefined"){
                m=0;
            }
            if(typeof n === "undefined"){
                n=0;
            }
            let res = m+n;
            return res;//return返回的一定是值,如果没有返回值,函数外面调用结果则为undefined
        }
        //console.log(res);=>Uncaught ReferenceError: res is not defined
        let a=sum(10,20);//外面想要调用该函数返回值则需要定义一个变量来接受返回值
        console.log(a);
    

匿名函数

匿名函数之表达式:把一个匿名函数本身赋给其它东西,这种函数一般不是手动触发执行,而是靠其它程序触发执行
ex:

document.body.onclick=function(){}
setTimeout(function(){},1000)//设置一个定时器,1000ms后执行匿名函数

匿名函数之自执行函数:创建完一个匿名函数后,紧接着就在后面加个小括号立刻执行该函数
ex:

(function(n){})(100)//函数内的n值为100

选项卡练习




    
    选项卡



    
  • 读书
  • 编程
  • 运动
读书使我快乐
编程使我难受
运动使我健康
<=script> //获取ID为selBox的DIV var selBox=document.getElementById("selBox"); //获取ID为selBox的DIV下的3个DIV,将他们赋给selBoxList var selBoxList=selBox.getElementsByTagName("div"); //获取ID为selNav的UL var selNav=document.getElementById("selNav"); //获取ID为selNav的UL下的3个LI,将他们赋给selNavList var selNavList=selNav.getElementsByTagName("li"); //循环三个LI,给每个LI绑定点击事件 //selNavList[i]是当前循环下我们需要操作的那个li,例如selNavList[0]则是第一个li,内容为读书 //============以下方式无法实现=============================== // for(var i=0;i

常用的输出方式

  • console.log
  • console.dir

dir输出一个对象的详细键值对信息

  • console.table

把一个多维json数组在控制台按照表格方式输出

  • 多维数组
    • [10,12]一维数组
    • [10,{name:“xxx”}]二维数组
    • [12,[{xxx:“xxx”}]]三维数组

浏览器窗口弹窗

  • alert(弹出框)
  • confirm(确认选择型弹框)
  • prompt(在confirm基础上多了一个输入框)

三种结果输出方式必先经过toString转换为字符串
三种方式都会阻断当前JS代码的执行

js放在head和body尾部区别

放在头部如果JS中代码需要获取元素来操作元素,则会获取不到,如果非要放在头部可以使用window.οnlοad=function(){},但是window方法只能用一次,JQ中也有一个类似的方法$(document).ready(function(){})


<=script>
window.onload=function(){
  JS代码
}


对象的进一步理解

var name=10;
var obj ={
name:“小王”
};
console.log(obj.name);//输出小王
console.log(obj[“name”]);//输出小王
console.log(obj[name]);//相当于obj[10] => 输出undefined

var name =“小王”
var obj={
name:name,
name,//es6中的简化,相当于name:name
}

for in 来遍历对象中的属性名

var obj={
name:“小王”,
age:“18”,
1:10,
2:20,
3:30
}
for(var key in obj){
console.log(“属性名:”+key+“属性值:”+obj[key])
}key是用来输出当前obj中的所有属性名,并且从属性名为最小的数字开始,而obj[key]是输出所有的属性值

自定义属性的深层理解

前期把一些值存储到自定义的属性上,后期需要用到的时候,直接从属性上获取这些值即可

隔行变色,滑过时变色,而且还能保持原本颜色




    
    隔行变色,并且离开还原本色



    
  • 这是第1个li
  • 这是第2个li
  • 这是第3个li
  • 这是第4个li
  • 这是第5个li
<=script> let box = document.getElementById("box"); let liList=box.getElementsByTagName("li"); //循环遍历每个li for(var i=0;i

arguments

1.类数组集合,集合中存储着所有函数执行时,传递的实参信息
2.不论是否设置形参,arguments都存在
3.不论是否设置实参,arguments都存在

任意数求和

1.传递的实参个数不确定
2.传递的值是否为有效数字不确定
3.需要把传递的有效数字求和并输出结果

**ex:**


初识箭头函数(存在Es6语法规范中)



``**注意:**``箭头函数中没有arguments,但是可以用...变量名(剩余运算符来获取传递的实参集合),它与arguments不同在于它是数组,而arguments是类数组,那么以后就能用数组的处理方式来进行后续操作



思考:
let sum=(…arg)=>{
return eval(arg.join(’+’));
}
sum(1,2,3,4)//10
Math.max(sum(1,2,3))//6
Math.max(sum([1,2,3]))//3
sum([1,2,3])//3
sum(1,2,3)//6
sum([1])//1
sum([1,2])//2

JS中的数学函数Math

Math数学函数:它本身不是一个函数,而是一个对象,对象中存储了很多操作数学的属性方法,因此被称为数学函数

Math中常用的属性和方法

Math.abs([number value]) 取一个数的绝对值,如果里面不是一个数则会基于Number()这个方法将它转化为数字
Math.ceil/floor([number value]) 向上(取整一定比原数大)/向下(取整一定必原数小)取整,如果里面不是一个数则会基于Number()这个方法将它转化为数字
Math.round([number value]) 四舍五入法(如果是负数.5则会舍去),如果里面不是一个数则会基于Number()这个方法将它转化为数字
Math.max/min([val1],[val2]…) 获取一堆数中的最大值/最小值

**思考题:**求Math.max/min([10,20,30,40,50])

Math.sqrt([number value]) 给一个数开方(负数不能开方)
Math.pow([val],[val]) 给一个数计算幂次
Math.random() 获取0-1之间随机小数

  • Math.round(Math.random())//随机获取0或1
  • Math.round(Math.random()*(m-n)+n)//随机输出n-m的整数

数组

    let ary=[12,23,34,45];
    console.log(typeof ary);=>object
    console.dir(ary);
      ary={
        0:12,
        1:23,
        2:34,
        3:45,
        length:4
      }
      数字作为索引
      length代表数组长度
      ary[0] 代表根据索引获取指定项内容
      ary.length 获取数组长度
      ary.length-1 获取最后一项的索引

数组中的常用方法

  • 按以下内容学习数组操作
    • 方法的作用和含义
    • 方法的实参(类型和含义)
    • 方法的返回值
    • 原来的数组是否会发生改变

1.实现数组增删改的方法(这一部分方法都会修改原有数组)

  • push: 向数组末尾添加内容
    • @params
      多个任意类型
    • return
      新增后数组的长度
    let ary =[10,20];
    let res=ary.push(30,'AA',{name:'小王'}); => 给数组末尾添加内容
    ary[ary.length]=40; => 基于原生JS向数组最后一项添加内容
    console.log(ary,res); => 数组长度从2变为5,res=5 ary=[10,20,30,'AA',{name:'小王'}]
  • unshift: 向数组开头添加内容
    • @params
      多个任意类型
    • return
      新增后数组的长度
    let ary =[10,20];
    let res=ary.unshift(30,'AA',{name:'小王'}); => 给数组开头添加内容
    ary=[50...ary]; => 基于原生ES6展开运算符,把原有数组克隆一份,在新数组创建第一项,其余内容使用原有数组内容,也算实现了向开头添加
    console.log(ary,res); => 数组长度从2变为5,res=5 ary=[30,'AA',{name:'小王'},10,20,]
  • shift: 删除数组中的第一项
    • @params
      没有
    • return
      删除的那一项
    let ary =[10,20,30,40];
    let res=ary.shift(); => 删除数组开头内容
    delete ary[0]; => 基于原生JS中的delete,把数组当作普通对象,确实可以删除某一项,但是不会影响数组本身结构特点(length长度不会发生变化),真实项目中不能使用这种方法删除
    console.log(ary,res); => res=10 ary=[20,30,40]
  • pop: 删除数组中的最后一项
    • @params
      没有
    • return
      删除的那一项
    let ary =[10,20,30,40];
    let res=ary.pop(); => 删除数组最后一项
    ary.length--; => 基于原生JS,让数组删除一位(默认删除最后一位)
    console.log(ary,res); => res=40 ary=[10,20,30]
  • splice: 实现数组的增加、删除、修改
    • 删除部分
    • @params
      n,m 都是数字,从索引n开始删除m个元素(m不写则一直删除到最后一个)
    • return
      将删除部分内容用新数组存储返回
    let ary =[10,20,30,40,50,60,70,80,90];
    let res=ary.splice(2,4); => 删除数组索引从2开始,删除四个内容(如果只有一个数字,则从它开始一直删到末尾)
    console.log(ary,res); => 数组长度从9变为5,res=[30,40,50,60] ary=[10,20,70,80,90]
    ary.splice(ary.length-1) => 删除最后一项
    ary.splice(0,1) => 删除第一项
  • splice: 实现数组的增加、删除、修改
    • 增加、修改部分
    • @params
      n,m,x 都是数字,从索引n开始删除m个元素,用x占据原先删除的位置(修改)
      n,0,x 都是数字,从索引n开始一个都不删,把x放到索引n前面(添加)
    • return
      将删除部分内容用新数组存储返回
    let ary =[10,20,30,40,50,60,70,80,90];
    let res=ary.splice(2,4); => 删除数组索引从2开始,删除四个内容(如果只有一个数字,则从它开始一直删到末尾)
    console.log(ary,res); => 数组长度从9变为5,res=[30,40,50,60] ary=[10,20,70,80,90]

    //实现修改
    ary.splice(2,4,'老王','老李'); => res=[70,80,90] ary=[10,20,'老王','老李']

    //实现添加
    ary.splice(2,0,'老陈'); => res=[] ary=[10,20,'老陈','老王','老李']

    //向数组末尾添加
    ary.splice(ary.length,0,'老雷'); => res=[] ary=[10,20,'老陈','老王','老李','老雷']

    //向数组开头添加
    ary.splice(0,0,'老牛'); => res=[] ary=['老牛',10,20,'老陈','老王','老李','老雷']

数组的查询和拼接(此组学习方法原数组不会改变)

  • slice: 实现数组的查询
    • @params
      n,m 都是数字,从索引n开始找到索引为m的这一项(不包含m这一项)
    • return
      将找到的部分内容用新数组存储返回
    let ary=[10,20,30,40,50];
    let res=ary.slice(1,3);
    console.log(res,ary) => res=[20,30] ary=[10,20,30,40,50]

    => m不写(找到末尾)
    ary.slice(1) => ary=[20,30,40,50]

    => 数组克隆(浅克隆,参数0不写也可以)
    res=ary.slice(0) => res=[10,20,30,40,50]

思考:
1.如果n/m为负数会咋样?
2.如果n>m会咋样?
3.如果是小数会咋样?
4.如果是非有效数字会咋样?
5.如果m或者n的值比最大索引还要大会咋样?

解答:1.暂时没发现规律
2.n>m 则找不到
3.小数则向下取整
4.如果n为非有效数字,m为有效数字,则n默认为0.若m为非有效数字,则找不到
5.如果n的索引比数组大,则找不到,如果m的索引比数组大,则从n索引开始找完整个数组

  • concat: 实现数组的拼接
    • @params
      多个任意类型值
    • return
      拼接后的新数组,原数组不变
    let ary1=[10,20,30,40];
    let ar2=[40,50,60,70];
    let res=ary1.concat('小陈',ary2);
    console.log(res); => res=[10,20,30,40,'小陈',40,50,60,70];

把数组转换为字符串(原数组不变)

  • toString(): 把数组转换为字符串
    • @params
    • return
      转换后的字符串,每一项用逗号分隔
    let ary=[10,20,30];
    let res=ary.toString();
    console.log(res); => res='10,20,30'
  • join(): 把数组转换为字符串
    • @params
      指定分隔符(分隔符为字符串类型,不指定默认逗号)
    • return
      转换后的字符串(原数组不变)
    let ary=[10,20,30];
    let res=ary.join(); => res="10,20,30"
    res=ary.join(""); => res="102030"
    res=ary.join("|"); => res="10|20|30"
    res=ary.join("+"); => res="10+20+30"
    console.log(eval(res)); => 60 eval把字符串变成JS表达式来执行

检测数组中是否包含某一项

  • indexOf(x,y)/lastIndexOf(x,y): 检测当前项x在数组中第一次/最后一次出现位置的索引值
    • @params
      x为要检测的这一项(x不是索引值),y为开始查找位置的索引
    • return
      这一项位置出现的索引值(一定是一个数字),如果没有这一项,则返回结果-1,原来数组不变;
    let ary=[1,2,3,1,2,3];
    console.log(ary.indexOf(2)); => 1
    console.log(ary.lastIndexOf(3)); => 5

    检测数组中是否包含"小陈"
    if(ary.indexOf("小陈")===-1){
        不包含
    }else{
        包含
    }
  • includes(): 检测当前项在数组中是否出现过
    • @params
      要检测的这一项
    • return
      出现返回true,没有出现返回false
let ary=[1,2,3];
let res=ary.includes(4); => res=false

数组的排序或者排列(原数组发生改变)

  • reverse(): 把数组倒过来排列
    • @params
    • return
      排列后的新数组,原数组发生改变
let ary=[1,2,3];
let res=ary.reverse(); => res=[3,2,1] ary=[3,2,1]
  • sort(): 实现数组的排序(从小打大)
    • @params
      可有可无,也可以是个函数(**注意:**如果不传递参数,只能排序10以下的(不包括10),默认按照每一项第一个字符来排)
    • return
      排列后的新数组,原数组发生改变
    let ary=[7,8,5,2,4,6]
    let res=ary.sort(); => res=[2,4,5,6,7,8]
    let ary1=[12,15,9,28,10,22];
    res=ary1.sort(); => res=[10,12,15,22,28,9]

    => 如果要实现降序或者升序则需要在sort内写入函数
    let res=ary1.sort(function(a,b){
      return a-b; => 实现升序res=[9,10,12,15,22,28]
      return b-a; => 实现降序 res=[28,22,15,12,10,9]
    });

    => 改写为箭头函数
    let res=ary1.sort((a,b)=>a-b); res=[9,10,12,15,22,28];
    let res=ary1.sort((a,b)=>b-a); res=[28,22,15,12,10,9];

遍历数组的方法(不会改变原数组)

  • forEach()

  • map()

  • filter()

  • find()

  • reduce()

  • some()

  • every()

  • forEach(): 遍历数组中的每一项

    • @params
      回调函数
    • return
      没有返回值,原数组不发生改变
  => 基于for循环解决数组遍历问题
    let ary=['我','是','老','王','啊'];
    for(let i=0;i使用forEach()
  let ary=['我','是','老','王','啊'];
  ary.forEach((item,index)=>{
    console.log('索引:'+index+'内容是:'+item);  =>箭头函数中的参数第一个为索引内容(不管取什么名字),第二个为索引(不管取什么名字)
  });

数组去重

=> 方法一:
1.建立一个新数组,遍历原数组的每一项
2.如果新数组中没有这一项就将它添加进新数组

    let ary=[1,3,2,4,2,3,3,4,1,2,2,1,3,4,4];
    let newAry=[];
    for(let i=0;i 循环原数组中每一项给item
      if(newAry.includes(item)){
        continue; => 如果新数组中包含这一项就跳过当前循环;
      }
      newAry.push(item);
    }
    console.log(newAry);


优化方法一(IE6、7、8不兼容):
    let newAry=[];
    ary.forEach((item)=>{
      if(newAry.includes(item)) return;
      newAry.push(item);
    });
    newAry.sort((a,b)=>{
      return a-b;
    });
    console.log(newAry);

=> 方案二(在原数组中操作,如何解决数组塌陷):
1.先分别拿出数组的每一项A
2.用这一项A和后面的每一项依次进行比较,如果遇到和当前项A相同的,则在原来的数组中把这一项删除

        let ary=[1,3,2,4,2,3,3,4,1,2,2,1,3,4,4];
        for(let i=0;ii=0 item=1 j=1 compare=3 1!==3
            =>i=0 item=1 j=2 compare=2 1!==2
            =>i=0 item=1 j=3 compare=4 1!==4
            =>i=0 item=1 j=4 compare=2 1!==2
            =>i=0 item=1 j=5 compare=2 1!==3
            =>i=0 item=1 j=6 compare=2 1!==3
            =>i=0 item=1 j=7 compare=2 1!==4
            =>i=0 item=1 j=8 compare=2 1===1 ary.splice(8,1) ary=[1,3,2,4,2,3,3,4,(1),2,2,1,3,4,4] j-- =>j=7 j++ =>j=8
            =>i=0 item=1 j=8 compare=2 1!==2
            =>...
            for(let j=i+1;i 因为每当删除时,后面元素的索引会向前移动一位,所以为了保证后续操作不变,则将索引先减少再增加
                
            }
        }
    }
        ary.sort((a,b)=>{
        return a-b;
        });
        console.log(ary);

=> 方案三(创建一个对象,如果数组中含有对象,则不能使用这种方法):

    let ary=[1,3,2,4,2,3,3,4,1,2,2,1,3,4,4];
    let obj={};
    => 循环数组中的每一项,把每一项向数组中进行存储(item:item)(数组中的内容作为对象obj的属性名和属性值)
    for(let i=0;i i=0 ary[0]=1 item=1 obj[1]==undefined obj[1]=1;
      => i=1 ary[1]=3 item=3 obj[3]==undefined obj[3]=3;
      => i=2 ary[2]=2 item=2 obj[2]==undefined obj[2]=2;
      => i=3 ary[3]=4 item=4 obj[4]==undefined obj[4]=4;
      => i=4 ary[4]=2 item=2 obj[2]!==undefined splice(4,1) ary=[1,3,2,4,(2),3,3,4,1,2,2,1,3,4,4] i-- =>i=3 i++=>i=4;
      => i=4 ary[4]=3 item=3 obj[3]!==undefined splice(4,1) ary=[1,3,2,4,(3),3,4,1,2,2,1,3,4,4] i-- =>i=3 i++=>i=4;
      => i=4 ...
      let item=ary[i];
      if(obj[item]!==undefined){
        =>每一次存储前进行判断,如果obj中存在这项,则需要删除
        ary.splice(i,1);
        i--;=>解决数组塌陷问题
        continue;
      }
      obj[item]=item;
}

=> 方案三(优化性能)

    let ary=[1,3,2,4,2,3,3,4,1,2,2,1,3,4,4];
    let obj={};
    => 循环数组中的每一项,把每一项向数组中进行存储(item:item)
    for(let i=0;i 把要删除的那项跟最后一项互换位置,然后把最后一项删除
        ary.length--;
        i--;
        continue;
      }
      obj[item]=item;

=> 写成函数

/*
 *  unique: 实现数组去重方法
 *  @params
 *    ary [Array] 要去重的数组
 *  @return
 *    去重后的数组
 *  by cly on 2021.5.3
 */

    function unique(ary){
      let obj={};
      for(let i=0;i

=> 方法四(Set去重,基于ES6,该方法可以实现快速去重)

    let let ary=[1,3,2,4,2,3,3,4,1,2,2,1,3,4,4];
    ary=[...new Set(ary)];
    console.log(ary);

字符串中常用方法

所有用单引号、双引号、反引号包裹的都是字符串

    let str='xiaochen'
    str.length => 字符串长度
    str[0] => 获取索引为0的(第一个)字符
    str[str.length-1] => 获取最后一个字符str.length-1最后一项索引

    => 循环输出字符串中的每一个字符
    let str="xiaowang"
    for(let i=0;i
  • charAt(): 根据索引获取指定位置的字符

    • @params
      n [number] 获取字符指定的索引
    • return
      返回查找的字符
      找不到返回的是空字符串而不是undefined
  • charCodeAt(): 获取指定字符的ASCII码值(Unicode编码值)

    • @params
      n [number] 获取字符指定的索引
    • return
      返回编码值
    let str='xiaoxiao'
    console.log(str.charAt(0)); => 'x'
    console.log(str[0]); => 'x'
    console.log(str.charAt(10000)); => ''
    console.log(str[10000]); => undefined
    console.log(str.charCodeAt(0)); => 120;
    console.log(String.fromCharCode(121)); => 'y'
  • substr(n,m): 字符串的截取(在原来的字符串中查找到自己想要的)

    • @params
      n为字符串的索引值,从字符串索引为n开始截取m个字符串,如果不写m则截取到末尾(类似splice)
    • return
  • substring(n,m): 字符串的截取

    • @params
      n,m都为字符串的索引值,从字符串索引为n开始截取到索引为m(不含m,类似数组中的slice)
    • return
  • slice(n,m): 字符串的截取

    • @params
      n,m都为字符串的索引值,从字符串索引为n开始截取到索引为m(不含m),但是slice可以支持负数作为索引,前两种方法都不行(负数索引我们可以按照str.length+负索引的方式来查找)
    • return
    let str='xiaoxiao'
    console.log(str.substr(2,3)); => 'aox'
    console.log(str.substring(2,3)); => 'a'
    console.log(str.slice(-4,-1)); => 'xia' str.length=8 str.slice(8-4,8-1);
  • indexOf(x,y)/lastIndexOf(x,y): 获取x字符在字符串中首次/最后一次出现位置的索引
    • @params
      x为需要查找的字符,y为开始查找位置的索引
    • return
      返回找到位置的索引,如果没有找到则返回-1
    let str='xiaoxiaonihaowoshixiaochen';
    console.log(str.indexOf('a')); => 2
    console.log(str.lastIndexOf('o'); => 21
    console.log(str.includes('@')); => false
    console.log(str.indexOf('@')); => -1
    console.log(str.indexOf('ni')); =>8
    console.log(str.indexOf('niho')); => -1
    console.log(str.indexOf('x',5)); => 18 查找从索引为5后面首次出现字符为'x'的索引
  • toUpperCase()/toLowerCase(): 字母转大写/小写
    • @params
    • return
      转换后大写/小写的结果
    let str='xiaoxiao'
    console.log(str.toUpperCase()); => XIAOXIAO
    console.log(str.toLowerCase()); => xiaoxiao

    => 实现第一个字母大写,其余字母小写
    str=str.substr(0,1).toUpperCase()+str.substr(1); => Xiaoxiao
  • split([分隔符]): 把字符串按照指定的分隔符拆分成数组(与数组中的join相对应)
    • @params
    • return
      转换后的结果
    => 把竖线分隔符变为逗号分隔符
    let str='music|movie|eat|sport';
    let ary=str.split("|"); => ["music", "movie", "eat", "sport"]
    str=ary.join(","); => "music,movie,eat,sport"
    console.log(str);
  • replace(老字符,新字符): 实现字符串的替换(经常伴随正则一起使用)
    • @params
    • return
      转换后的结果
let str='小陈@小王@小雷';
str=str.replace('@','-'); => "小陈-小王@小雷" 在不使用正则表达式的情况下,执行一次replace只能替换一次字符
str=str.replace(/@/g,'-'); => "小陈-小王-小雷" /@/g,为正则表达式,g代表全局

实现一些常用的需求

时间字符串的处理

let time='2021-5-14 11:43:20'
=> 变为自己需要的格式,例如:
'2021年5月25日 11时43分20秒'

方案一,一直replace
time = time.replace('-','年').replace('-','月').replace(' ','日 ').replace(':','时').replace(':','分')+'秒';
console.log(time);

方案二,获取值的方法
let n=time.indexOf('-'); =>获取到字符串'-'首次出现的索引,将它赋给变量n
let m=time.lastIndexOf('-'); =>获取到字符串'-'最后一次出现的索引,将它赋给变量m
let x=time.indexOf(' '); =>获取到字符串' '首次出现的索引,将它赋给变量x
let y=time.indexOf(':'); =>获取到字符串':'首次出现的索引,将它赋给变量y
let z=time.lastIndexOf(':'); =>获取到字符串':'最后一次出现的索引,将它赋给变量z
let year=time.substring(0,n); =>从索引0开始,截取到索引为n的项(不包含n),获取到年
let month=time.substring(n+1,m); =>从索引n的后一项开始,截取到索引为m的项(不包含m),获取到月
let day=time.substring(m+1,x); =>从索引m的后一项开始,截取到索引为x的项(不包含x),获取到日


通过split一项项的拆分
let n=time.split(' '); =>通过字符串' ',把整个时间拆分成两个数组["2021-5-14", "11:43:20"]
let m=n[0].split('-'); =>通过字符串'-',把数组中第一个内容又拆成数组["2021", "5", "14"]
let x=n[1].split(':'); =>通过字符串':',把数组中第二个内容又拆成数组["11", "43", "20"]

通过正则表达式直接全拆分
let ary=time.split(/(?: |-|:)/g); =>ary=["2021", "5", "14", "11", "43", "20"]
time=addZero(ary[0])+'年'+addZero(ary[1])+'月'+addZero(ary[2])+'日'+' '+addZero(ary[3])+'时'+addZero(ary[4])+'分'+addZero(ary[5])+'秒'

=>如果时间不足两位数,则需要在前面补个0
function addZero(val){
  if(val.length<2){
    val='0'+val;
  }
  return val;
}

实现一个方法jueryURLparameter(获取一个URL地址问号后面传递的参数信息)

得到一个对象,里面包含{
lx:‘1’,
name:‘xiaoyu’,
age:‘18’,
HASH:‘box1’
}

let url1='https://sports.qq.com/kbsweb/game.htm?lx=1&name=xiaoyu&age=18#box1';
let url2='https://sports.qq.com/kbsweb/game.htm?mid=100000:55077655#box2';
let result={};

let askIndex=url1.indexOf('?'); =>获取到?首次出现的索引
let wellIndex=url.indexOf('#'); =>获取到#首次出现的索引
let askText=url1.subString(askIndex+1,wellIndex); =>获取到?到#之间的内容(不包含两端),lx=1&name=xiaoyu&age=18
let wellText=url1.subString(wellIndex+1) =>获取到#后面的内容(不包含两端),box1

let askAry=askText.split('&'); =>["lx=1", "name=xiaoyu", "age=18"]
askAry.forEach((item)=>{
  let n=item.split('=');
  //console.log(n) ["lx", "1"]["name", "xiaoyu"]["age", "18"]
  let key=n[0];
  let value=n[1];
  //console.log(key,value); lx 1 name xiaoyu age 18
  result[key]=value;
});
result['HASH']=wellText;

将queryURLparameter封装成一个方法

function queryURLParams(url){
  let askIndex=url.indexOf('?'),
      wellIndex=url.indexOf('#'),
      askText='',
      wellText='',
      result={};
  if(wellIndex===-1){
    wellIndex=url.length; =>没有#则让索引为整个url的长度
  }else{
    wellText=url.substring(wellIndex+1); =>将#索引后面内容截取给wellText
    result['HASH']=wellText;
  }
  if(askIndex!==-1){
    askText=url.substring(askIndex+1,wellIndex); =>截取从索引为?后一项开始,到索引为#(不包含这项)
    let askAry=askText.split('&'); =>将从?到#截取的内容按&分成数组内容
    askAry.forEach((item)=>{
    let n=item.split('='); =>将每个item分别以=划分
    result[n[0]]=n[1]; =>n[0]为上面划分的属性名 n[1]为上面划分的属性值
    });
  }
  return result;
}

let paramsObj=queryURLParams(url1); =>{HASH: "box1", lx: "1", name: "xiaoyu", age: "18"}

实现比较简单的四位随机验证码(数字+字母)


    
    

日期对象的基本操作

let time = new Date(); => 获取当前客户端(本地电脑)时间,用户可以自行修改,不能作为参考依据

标准日期对象中提供了一些属性和方法,供我们操作日期信息

getFullYear() 获取年
getMonth() 获取月
getDate() 获取日
getDay() 获取星期(0-6 星期日-星期六)
getHours() 获取小时
getMinutes() 获取分
getSeconds() 获取秒
getMilliseconds() 获取毫秒
getTime() 获取当前日期距离1970/1/1 00:00:00 这个日期之间的毫秒差
toLocaleDateString() 获取年月日
toLocaleString() 获取完整的年月日,时分秒

简单的小时钟案例


    
    小时钟案例
    


    
/引入script let clockBox=document.getElementById('clockBox'); //封装一个日期位数不为两位补0的方法 function addZero(val){ val=Number(val); return val<10?'0'+val:val; } //封装一个自定义时间格式的方法 function queryTimeDate(){ let time= new Date(), year=time.getFullYear(), month=time.getMonth()+1, day=time.getDate(), week=time.getDay(), hour=time.getHours(), minute=time.getMinutes(), second=time.getSeconds(); let weekAry=['天','一','二','三','四','五','六']; let result=year+'年'+addZero(month)+'月'+addZero(day)+'日'+' '+'星期'+weekAry[week]+' '+addZero(hour)+'时'+addZero(minute)+'分'+addZero(second)+'秒'; clockBox.innerHTML=result; } queryTimeDate();//执行上述函数 setInterval(queryTimeDate, 1000);//定时器,1秒执行一次方法

续基于日期对象处理时间

        let time='2021-5-16 12:0:0'
        //位数不够补0
        function addZero(val){
            val=Number(val);
            return val<10?'0'+val:val;
        }
        //把时间字符串变为标准格式
        function handleDate(time){
            time=time.replace(/-/g,'/');
            time=new Date(time);
            let year=time.getFullYear(),
            month=addZero(time.getMonth()+1),
            day=addZero(time.getDate()),
            hour=addZero(time.getHours()),
            minute=addZero(time.getMinutes()),
            second=addZero(time.getSeconds());
            return year+'年'+month+'月'+day+'日'+' '+hour+'时'+minute+'分'+second+'秒'
        }
        time=handleDate(time);
        console.log(time);

DOM基础操作

document.getElementById() 指定在文档中,基于元素的ID或者这个元素对象
[context].getElementsByTagName() 在指定上下文中,通过标签名获取一组元素集合
[context].getElementsByClassName() 在指定上下文中,通过样式类名获取一组元素集合(不兼容IE6-8)
document.getElementsByName() 在整个文档中,通过标签的Name属性值获取一组节点集合(在IE中只有表单元素的Name才能识别到,所以我们一般只用于表单元素的处理)
document.head/document.body/document.documentElement 获取页面的head/body/整个页面
[context].querySelector([selector]) 在指定上下文中,通过选择器获取到指定的元素对象(不兼容IE6-8)
[context].querySelectorAll([selector]) 在指定上下文中,通过选择器获取到指定的元素集合(不兼容IE6-8)

元素选择器:

let box=document.querySelector('#box'); => 获取到ID为box的对象
let links=box.querySelectorAll('a'); => 获取到box下的所有a标签

JS中的节点和描述节点之间关系的属性

节点node (页面所有内容都是由节点组成)
节点集合nodeList (getElementsByName/querySelectorAll获取的都是节点集合)

元素节点(元素标签)

  • nodeType:1
  • nodeName:大写的标签
  • nodeValue:null

文本节点
nodeType:3
nodeName:’#text’
nodeValue:文本内容
注释节点
nodeType:8
nodeName:’#commen’
nodeValue:注释内容
文档节点(document)
nodeType:9
nodeName:’#document’
nodeValue:null

描述这些节点之间的关系的属性(对DOM操作的一种补充)

childNodes:获取所有的子节点 =>所有儿子
children:获取所有元素的子节点(子元素标签集合) => 所有元素儿子
parent:获取父亲节点
firstChild:获取第一个子节点(啥节点类型都有可能) => 大儿子
lastChild:获取最后一个子节点(啥节点类型都有可能) => 小儿子
firstElementChild/lastElementChild:获取第一个/最后一个元素子节点(不兼容IE6-8) 大元素哥哥/弟弟
previousSibling:获取上一个哥哥节点(啥节点类型都有可能) => 上一个哥哥
nextSibling:获取下一个弟弟节点(啥节点类型都有可能) => 下一个弟弟
previousElementSibling/nextElementSibling:获取哥哥/弟弟元素节点(不兼容IE6-8) => 上一个元素哥哥/下一个元素弟弟


        
  • git和npm操作
  • 面向对象和原型:研究插件源码,自己写插件
  • 闭包作用域:堆栈内存处理
  • ES6:从入门到放弃
  • 同步异步编程及核心:微任务、宏任务、时间循环
  • DOM及其事件模型
  • jQuery及源码分析
  • 设计模式:发布订阅、单例、Promise、PromiseA+
  • AJAX及跨域解决方案:封装一款超牛X的AJAX库
  • 一入HTTP深似海
  • 性能安全优化
  • VUE全家桶:vue-cli\vue\vue-router\vuex\vue element...
  • vue和vuex的核心源码
  • Recat全家桶:create-react-app、antd、antdpro、react、react-dom、react-native、react-router-dom、redux、react-redux、dva、redux-saga、mobx...
  • react核心原理
  • redux源码和中间件编写
  • webpack
  • node和express
  • type script
  • ...
script> let box=document.getElementById('box'); //标准浏览器(非IE6-8)中会把空格和换行当作文本节点处理 console.log(box.childNodes); //但是IE6-8下使用children会把注释也当作元素节点 console.log(box.children); //=================================================== //找所有为元素儿子的节点方法 function queryChildren(context){ //获取到所有的孩子节点 let res=[], nodeList=context.childNodes; //遍历整个孩子节点,如果是元素节点就放进数组中 for(i=0;i #text 因为它的上一个哥哥是空格 console.log(prev(vueBox.previousSibling)); //=>
  • 性能安全优化
  • 成功找到 //找上一个为元素哥哥的方法 function prev(context){ //找到第一个哥哥,然后判断它是否是元素哥哥,不是则找哥哥的哥哥 var pre=context.previousSibling; while(pre.nodeType!==1){ pre=pre.previousSibling; } return pre; }

    在JS中动态增删改元素

    createElement 创建元素对象
    createTextNode 创建文本对象
    appendChild 把元素添加到指定容器的末尾 语法:容器.appendChild(元素)
    insertBefore 把元素添加到指定容器中指定元素的前面 语法:容器.insertBefore([新增元素],[指定元素])
    cloneNode(true/false) 克隆元素或者节点(深克隆:连子元素都克隆/浅克隆:不克隆子元素)
    removeChild 移除容器中的某个元素

    自定义属性的另一种方式(这种方式是把自定义属性放到元素结构上面)

    setAttribute 设置元素的自定义属性信息
    getAttribute 获取元素的自定义属性信息
    removeAttribute 移除元素的自定义属性信息

    ============两种自定义属性方式======================
    
        
        



    GIT版本控制系统

    • 版本控制器
      • 1.记录历史版本信息(记录每一次修改的记录)

    分布式和svn集中式的区别

    javascript笔记知识(可能有错误、欢迎指正)_第3张图片
    javascript笔记知识(可能有错误、欢迎指正)_第4张图片

    git的工作原理

    • 工作区 :我们能看到的,并用来写代码的区域
    • 暂存区 :临时存储用的区域
    • 历史区 :生成历史版本
    • 中央仓库 :协同开发存放整个代码的区域
    • 流程 :工作区->暂存区->历史区->中央仓库( git add-A、git commit -m’备注信息’ 、git pull origin master(先从仓库内拉取) git push origin master(再提交到仓库))

    git的全局配置

    • 第一次安装完成git后,我们在全局环境下配置基本信息(告诉git我是谁)
    • $ git config -l :查看配置信息
    • $ git config --global -l :查看全局配置信息
    • 第一次添加全局 : git config --global user.name ‘xxx’ 、git config --global user.email ‘[email protected]

    创建仓库完成版本控制

    • $ git init =>会生成一个隐藏的.git文件(不能删除,因为这里面含有暂存区和历史区的一些其他信息,如果删除了就不是一个完整的git仓库)

    在本地编写完代码后(在工作区中),把一些文件提交到暂存区中

    • $ git add xxx :把某一个文件或者文件夹提交到暂存区
    • $ git add . :把当前仓库内所有最新修改的文件都提交到暂存区
    • $ git add -A :同上
    • $ git status :查看当前文件夹内的状态(红色代表有文件在工作区,绿色代表在暂存区,看不见内容说明所有修改的信息文件都已经提交到了历史区)

    把暂存区的内容提交到历史区中

    • $ git commit -m’描述信息,本次提交内容的一个描述’

    查看历史版本信息

    • $ git log : 查看历史信息
    • $ git reflog :查看包含历史回滚信息

    git的整个操作流程

    javascript笔记知识(可能有错误、欢迎指正)_第5张图片

    本地仓库连接到远程仓库(建立本地仓库和远程仓库的链接)

    • $ git remote -v :查看本地仓库和哪些远程仓库保持链接
    • $ git remote add origin [git远程仓库地址] :让本地仓库和远程仓库建立一个连接,origin是随便取的一个链接名字,可以改成任意你想要的名字,只不过一般使用该名字
    • $ git remote rm origin :删除关联信息
    • $ git pull origin master :提交前最好先拉取,看看有没有新内容
    • $ git push origin master :把本地代码提交到远程仓库(需要输入github账户和密码)
    • $ git clone [git远程仓库地址] [别名,可以不设置,默认是仓库名]
      // => 真实项目开发流程为:1.组长或者负责人先建立中央仓库 2.小组成员基于git clone把远程仓库以及默认的内容克隆到本地(解决了三个事情: 1.初始化一个本地仓库git init 2.和对应的远程仓库保持关联git remote add 3.把远程仓库默认内容拉取到本地git pull) 3.每个小组成员完成自己的程序后,基于git add .和get commit -m把本地内容提交到历史区,最后通过git push上传到中央仓库(上传前最好先git pull看看有没有别人添加的新内容)

    NPM(node package manger)

    NODE模块管理工具,根据npm我们可以快速安装,卸载我们所需要的资源(例如:jQuery、Vue、Vue-router…)
    node -v :查看node版本
    npm -v :查看npm版本

    基于npm进行模块管理

    https://www.npmjs.com/ 基于npm是从npmjs.com平台上下载安装
    npm install xxx :把模块安装在当前项目中(node_modules)
    npm install xxx -g :把模块安装在全局环境中
    npm i [email protected] :安装指定版本号的模块
    npm view xxx versions > xxx.version.json :查看某个模块的版本信息(输出到指定JSON文件中)
    npm init -y :初始化当前项目的配置清单(项目文件夹的名字不能包含大写字母、中文、特殊符号,创建成功后会在当前项目中生成一个package.json的文件清单,dependencies:生产依赖模块:项目开发和部署都需要,devDependencies:开发依赖模块:只有在开发的时候需要,scripts:配置本地可执行的命令)
    npm i xxx --save :把模块保存到清单生产依赖中
    npm i xxx --save-dev :把模块保存到清单开发依赖中
    npm install :跑环境,按照清单安装所需模块
    npm root -g :查看全局安装模块目录
    npm uninstall xxx :
    npm uninstall xxx -g :卸载安装过的模块

    整个项目流程(新项目的开始)

    1.创建一个项目文件夹
    2.把它作为一个新仓库进行代码管理(可以基于$ git clone把远程仓库克隆下来即可)
    3.初始化模块配置清单package.json($ npm init -y)
    4.安装所需的模块($ npm i jquery bootstrap@3 less)[@3代表安装bootstrap3代中最后一个版本]
    5.开始编写代码
    6.开发中:可能需要在本地配置命令去完成一些功能(例如LESS文件编译,此时需要配置npm可执行命令,在配置清单package.json中找到scripts,添加你需要的编译代码)

    "scripts": {
        "lesscly": "lessc css/index.less css/index.min.css -x" //这段作用就是编译index.less这个文件,将它转换为可执行的css,编译的时候执行npm run lesscly即可执行该命令
      }
    

    7.开发过程中我们需要基于git把文件进行管理:生产对应的历史版本,提交到暂存区、历史区、远程仓库的时候,项目中很多文件是无需上传的,例如node_modules、ideas…,我们生产一个gitignore忽略文件
    8.由于每次git提交的时候,我们都不去提交node_modules,所以团队协作开发中,每当我们拉下来程序后,都需要’跑环境’,执行:$ npm install,按照项目中的package.json中的依赖信息,把缺失的模块都安装一遍

    变量提升

    当浏览器开辟出供代码执行的栈内存后,然后代码并没有自上而下执行,而是做了很多事情,其中一件事情就是把当前作用域中所有带var和function关键字进行提前声明和定义(这就是变量提升机制)

    • 带var的只是提前声明(declare) var a;//如果只声明,没有赋值,默认为undefined
    • 带function的不仅声明,还定义了(defined) //定义实际上就是赋值,准确来说就是让变量和某个值关联起来
    console.log(a);//=> a=undefined
    var a=12;
    var b=a;
    b=13;
    console.log(a);//=> a=12
    
    
    console.log(sum(10,20));//=>30
    function sum(n,m){
      return n+m;
    }
    
    //函数表达式方式,由于使用var创建sum,变量提升阶段只会声明变量,不会赋值,因此函数没有值是无法执行(真实项目中,这种方式更常用,因为这种操作更严谨)
    console.log(sum(10,20));//=>报错 Uncaught TypeError: sum is not a function
    var sum=function(n,m){
      return n+m;
    }
    

    javascript笔记知识(可能有错误、欢迎指正)_第6张图片

    带var和不带var的区别

    /* => 在全局作用域下的区别
     *
     * 不带var的,相当于给全局对象window设置了一个属性a
     * window.a=13;
     */
    a=13;
    console.log(a); //=> 13
    
    /*
     * 栈内存变量存储空间
     *      b  
     * 带var的:在全局作用域下声明了一个变量b(全局变量),但是在全局下声明的变量也同样相当于给window添加了一个对应的属性(只有全局作用域具备这个特点)
     */
    
     var b=14;
     console.log(b);//=> 14
     console.log(window.b);//=> 14
    

    let/const和var的区别

    1.let和const不存在变量提升机制

    创建变量的六种方式中,只有var和function有变量提升,let、const、import、class都不存在变量提升机制
    2.var允许被重复声明,而let不能重复声明
    在相同的作用域/执行上下文中,如果使用var和function关键词声明变量并进行重复声明,是不会有影响的(声明第一次后,之后再遇到就不会再重复声明了)
    但是使用let或者const就不行,浏览器会校验当前作用域是否已经存在这个变量,如果存在,再次声明就会报错

    => 在浏览器开辟栈内存供代码自上而下执行前,不仅有变量提升操作,还有很多其他操作,其中还有个操作叫'词法解析'或叫'词法检测',作用就是检测当前即将执行的代码是否会存在'语法'错误,如果出现错误,代码将不会执行,直接报告错误
      console.log(1); =>由于下面语法错误,所以不输出
      let a=12;
      console.log(a); =>由于下面语法错误,所以不输出
      let a=13;  =>Uncaught SyntaxError: Identifier 'a' has already been declared
      console.log(a); =>由于上面面语法错误,所以不输出
    
    
      console.log(1); =>这里的1能够输出
      console.log(b); =>Uncaught ReferenceError: Cannot access 'a' before initialization
      let b=12;
    
      =>所谓的重复是指,不管之前是通过什么办法,只要当前栈内存中存在这个变量,我们使用let/const再次声明这个变量的时候就是语法错误(在词法解析阶段,而不是在代码执行阶段)
      console.log(a);
      var a=12;
      let a=13; =>Uncaught SyntaxError: Identifier 'a' has already been declared
      console.log(a);
    
    
    fn();
    function fn(){console.log(1);};
    fn();
    function fn(){console.log(2);};
    fn();
    var fn=function(){console.log(3);};
    fn();
    function fn(){console.log(4);};
    fn();
    function fn(){console.log(5);};
    fn();
    

    javascript笔记知识(可能有错误、欢迎指正)_第7张图片

    /* 全局作用域
     * 1.变量提升阶段.不管if中条件是否成立,都要先执行变量提升这个阶段
     *    var a =>此时的a为undefined,同时创建了个全局变量a,同时也给window一个属性值window.a
     * 2.执行代码阶段
     *   [property] in [object]:检测当前属性是否属于这个对象,返回boolean型结果
     */
    console.log(a); => undefined
    if(!('a' in window)){   => 'a' in window===true  !true===false
      var a = 14;
    }
    console.log(a); => undefined
    
    /* 全局作用域
     * 1.变量提升阶段
     *    但是'函数'如果在'条件判断'中,不管条件成立与否,提升变量阶段只会提升变量而'不会赋值',在老版本浏览器中会提升也会赋值,但是在新版本浏览器中,为了兼容ES6严谨的语法规范,条件中的函数在变量提升阶段只会提前声明,而不会赋值
     * 2.执行代码阶段
     *   
     */
    //fn(); => Uncaught TypeError: fn is not a function,因为fn在if判断中,提升变量阶段没赋值,因此此时的fn()相当于只是声明的一个变量,而不是一个函数,然后我们把它注释掉,执行下面代码
    if('fn' in window){  
      fn();   => 'fn' in window===true 条件成立,进来的第一件事就是给fn赋值,然后再执行代码,因此这个fn()能够输出,内容为你好!
      function fn(){
        console.log('你好!');
      }
    }
    fn(); => 你好!
    

    自执行函数

    形如(fn([形参]){})([实参]),也可以+fn([形参]){}([实参])、fn([形参]){}([实参])、!fn([形参]){}([实参])、-fn([形参]){}([实参]),前面加()、、+、-只是为了语法不报错,没有别的特殊意思
    自执行函数本身由于没有函数名,因此不进行变量提升

    f=function(){return ture;} => 全局变量,window.f=...
    g=function(){return false;} => 全局变量,window.g=...
    ~function(){  => 自执行函数
      /*
       * 函数执行会形成一个私有作用域
       * 1.变量提升 function g(由于在if条件中,因此只是提升变量,而没有赋值)
       * 2.代码执行
      /
      if(g()&&[]==![]){   => Uncaught TypeError: g is not a function 由于此时g()还不是一个函数,因此条件不成立,所以会报错,后面代码将不再执行
        f=function(){return false;}
        function g(){return true;}
      }
    }();
    console.log(f());
    console.log(g());
    

    let能解决typeof检测时出现暂时性死区的问题)(let(const、class、import)比var(function)更严谨)

    console.log(a); =>Uncaught ReferenceError: a is not defined
    
    console.log(typeof a); =>'undefined'这是浏览器的一个BUG,因为a没有定义,应该报错
    
    console.log(typeof a); =>Uncaught ReferenceError: Cannot access 'a' before initialization
    let a;
    

    私有作用域下的变量提升

    console.log(a,b);
    var a=12;
    var b=12;
    function fn(){
      console.log(a,b);
      var a=b=13;
      console.log(a,b);
    }
    fn();
    console.log(a,b);
    

    javascript笔记知识(可能有错误、欢迎指正)_第8张图片

    console.log(a,b,c);
    var a=12,
        b=13,
        c=14;
    function fn(a){
      console.log(a,b,c);
      a=100;
      c=200;
      console.log(a,b,c);
    }
    b=fn(10);
    console.log(a,b,c);
    

    javascript笔记知识(可能有错误、欢迎指正)_第9张图片

    全局变量和私有变量

    如果全局变量和私有变量指向同一个地址,那么它们就有关系(全局变量和私有变量毫不相干的说法是错误的)

    var ary=[12,23];
    function fn(ary){
      console.log(ary);
      ary[0]=100;
      ary=[100];
      ary[0]=0;
      console.log(ary);
    }
    fn(ary);
    console.log(ary);
    

    javascript笔记知识(可能有错误、欢迎指正)_第10张图片

    作用域和作用域链

    从函数创建开始,作用域就已经制定好了
    当前函数是在哪个作用域(N)下创建的,那么函数执行形成的作用域(M)的上级作用域就是N,和函数在哪执行没有关系,只和创建的地方有关

    var n=1;
    function fn(){
      var n=2;
      function f(){
        n--;
        console.log(n);
      }
      f();
      return f;
    }
    var x=fn();
    x();
    console.log(n);
    

    javascript笔记知识(可能有错误、欢迎指正)_第11张图片

    闭包作用域

    • 创建函数

      • 开辟一个堆内存,把函数里面的代码当作字符串存储进去
      • 把堆内存的地址赋给函数名或者变量名
      • 函数在哪创建,那么执行的时候所查找的上级作用域就是谁
    • 函数执行

      • 形成一个全新的私有栈内存(执行一次形成一个,多个之间也不会产生影响)
      • 形参赋值&变量提升
      • 代码执行(把所属堆内存中的代码字符串拿出来一行行执行)
      • 作用域链机制:遇到一个变量,首先看它是否为私有变量(形参和在私有栈内存中声明的变量叫私有变量),是私有的就操作自己的变量即可,不是私有的就按上级作用域查找,一直找到全局作用域为止
      • 闭包的保护机制:私有变量和外界变量没有必然关系,可以理解为被私有栈内存保护起来了
    • 堆栈内存释放问题

      • 函数执行就会形成一个栈内存(从内存中分配一块空间),如果内存都不销毁释放,很容易就会导致栈内存溢出(内存爆满,电脑卡死),堆内存的释放是学习JS的核心知识之一
    • 堆内存释放

      • 创建一个引用类型数据值,就会产生一个堆内存,如果当前创建的堆内存不被其它东西占用了(浏览器会在空闲的时候,查找每一个内存的引用情况,不被占用的都会回收释放掉),则会释放
      let obj ={
        name:'小王';
      }
      let oop=obj;
      => 此时obj和oop都占用着对象的堆内存,想要释放堆内存,需要手动解除变量和值的关联(null空对象指针)
      obj=null;
      oop=null;
      
    • 栈内存

      • 打开浏览器形成的全局作用域就是栈内存
      • 手动执行函数形成的私有作用域就是栈内存
      • 基于ES6中的let/const形成的块作用域也是栈内存
    • 栈内存释放

      • 全局栈内存:在浏览器页面关闭或者刷新时会销毁
      • 私有栈内存:一般情况下,函数只要执行完成,私有栈内存就会被销毁释放掉,但是一旦栈内存中某个东西(一般都是堆地址)被私有作用域以外的事物给占用了,则当前栈内存不能立即被释放销毁,市面上认为的闭包:函数执行形成的不能被销毁的私有栈内存,这样的才是闭包.也有的认为只要形成私有栈内存,来保护里面的变量,这个机制就是闭包机制
      function x(){
        return function(){...}
      }
      let f=x();
      
    • 闭包的两大作用

      • 保护(私有变量和外界没有必然联系)
      • 保存(形成不能销毁的栈内存,里面的私有变量等信息也被保存下来)
    var i=5;
    function fn(i){
      return function(n){
        console.log(n+(++i));
      }
    }
    var f=fn(1);
    f(2);
    fn(3)(4);
    fn(5)(6);
    f(7);
    console.log(i);
    

    javascript笔记知识(可能有错误、欢迎指正)_第12张图片

    var i=20;
    function fn(){
        i-=2;
        return function (n){
            console.log((++i)-n);
        }
    }
    var f=fn();
    f(1);
    f(2);
    fn()(3);
    fn()(4);
    f(5);
    console.log(i);
    

    javascript笔记知识(可能有错误、欢迎指正)_第13张图片

    var a=9;
    function fn(){
    	a=0;
    	return function(b){
    		return b+a++;
    	}
    }
    var f=fn();
    console.log(f(5));
    console.log(fn()(5));
    console.log(f(5));
    console.log(a);
    

    javascript笔记知识(可能有错误、欢迎指正)_第14张图片

    详细解释闭包的两个作用(保护和保存)

    从性能的角度上讲,我们真实项目中应该减少对闭包的使用(因为闭包会产生不释放的栈内存,过多使用就会导致内存溢出或者降低性能)

    • 保护

      • 1.JQuery(JQ)前端非常经典的类库:提供了大量的方法供开发人员使用=>同时为了防止全局变量的污染(解释:导入JQ之后,它里面有大量的方法,如果这些方法不保护起来,用户编写的方法很容易和JQ中的方法名字产生冲突,产生冲突可以理解为全局变量污染),JQ中的方法和变量需要用闭包保护起来
    • 保存

    this问题

    函数执行的主体(不是上下文):意思是谁把函数执行,那么执行主体就是谁,this非常的不好理解,因此以后遇见this,想一句话:“你以为你以为的就是你以为的”
    给元素的某个事件绑定方法,当事件触发方法执行的时候,方法中的this是当前操作的元素本身
    如何确定主题:当方法前如果有点,则this为方法前的内容,没有点,this则为window或者undefined(严格和非严格的区别)

    var name='老王';
    function fn(){
      console.log(this.name);
    };
    var obj={
      name:'老李',
      fn:fn
    };
    obj.fn();  => obj执行的,则this为obj,输出老李
    fn();  => 非严格模式下,fn()为window.fn(),则this为window,输出老王
    
    var fullName='language';
    var obj={
      fullName:'javascript',
      prop:{
        getFullName:function(){
            return this.fullName;
        }
      }
    };
    console.log(obj.prop.getFullName()); =>this:obj.prop  return obj.prop.fullName  undefined
    var test=obj.prop.getFullName;  
    console.log(test());  =>this:window  return window.fullName  language
    
    var num=10;
    var obj={num:20};
    obj.fn=(function(num){
      this.num=num*3;
      num++;
      return function(n){
        this.num+=n;
        num++;
        console.log(num);
      }
    })(obj.num);
    var fn=obj.fn;
    fn(5);
    obj.fn(10);
    console.log(num,obj.num);
    

    javascript笔记知识(可能有错误、欢迎指正)_第15张图片

    在赋值情况下逻辑或与非的意义

    A || B :先验证A的值真假情况,若A为真,则将A的值返回,否则返回B的值
    A && B :先验证A的值真假情况,若A为真,则将B的值返回,否则返回A的值
    &&的优先级高于||

    var foo='hello';
    (function(foo){
      console.log(foo);  => 私有变量hello
      var foo=foo||'world'; hello||world => 返回私有变量hello 
      console.log(foo);  => 私有变量hello
    })(foo); => 这里是把全局变量下的foo的值'hello'当作实参传递给函数的形参
    console.log(foo);  => 全局变量hello
    

    构造原型模式(正统面向对象)

    自己能够创造出自定义类和对应的实例,构建起一套完整的面向对象模型

    function CreatePerson(name,age){
      this.name=name;
      this.age=age;
    }
    
    
    => CreatePerson('张三',18) 这里是普通函数执行
    
    
    let person1=new CreatePerson('李四',20);
    => new CreatePerson()执行和普通函数执行的联系
    => 1.new这种执行方式叫做'构造函数执行模式',此时的CreatePerson不仅仅是一个函数名,被称为'类',而返回的结果(赋值给person1的)是一个对象,我们称之为'实例',而函数体中出现的this都是这个实例
    

    javascript笔记知识(可能有错误、欢迎指正)_第16张图片

    instanceof:用来检测某个实例是否属于这个类

    实例 instanceof类 属于返回true,否则返回false
    局限性:1.要求检测的实例必须是对象数据类型,基本数据类型是无法基于它检测出来
    typeof能检测基本数据类型,但是引用数据类型无法具体区分,null检测为object等,刚好与instanceof互补

    字面量创建方式(也是number类的实例,也可以调取内置的共有方法)

    let n=10;
    console.log(n.toFixed(2));
    console.log(typeof n);   => 'number'
    

    构造函数创建模式(创建出来的实例是对象类型的)

    let m=new Number('10');
    console.log(typeof m);  => 'object'
    console.log(m.toFixed(2));
    
    => 和实例有关的操作一定是this.xxx=xxx,因为this是当前类创造出来的实例
    => 私有变量和实例没有必然联系
    
    function Fn(n){
      let m=10;
      this.total=n+m;
      this.say=function(){
        console.log(this.total);
      };
    }
    let f1=new Fn(10);
    let f2=new Fn(20);
    let f3=new Fn;
    console.log(f1.m);
    console.log(f2.n);
    console.log(f1.total);
    f2.say();
    console.log(f1===f2);
    

    javascript笔记知识(可能有错误、欢迎指正)_第17张图片

    原型及原型链模式

    => 执行步骤(new执行也会把类当作普通函数执行,当然也有类执行一面)
    => 1.创建一个私有栈内存
    => 2.形参赋值&变量提升
    => 3.浏览器创建一个对象出来(这个对象就是当前类的一个新实例),并且让当前函数中的this指向这个实例对象(构造函数模式中,方法中的this是当前类的实例)
    => 4.代码执行
    => 5.在我们不自己设置return返回情况下,浏览器会把当前创建的实例对象默认返回
    
    
    function Fn(){
      this.x=100;
      this.y=200;
    }
    let f1=new Fn();
    let f2=new Fn();
    

    javascript笔记知识(可能有错误、欢迎指正)_第18张图片

    1.每一个函数数据类型的值,都有一个天生自带的属性:prototype(原型),这个属性的属性值是一个对象(‘用来存储实例公用属性和方法’)
    函数数据类型

    • 普通函数
    • 类(自定义类和内置类)

    2.在prototype这个对象中,有一个天生自带的属性constructor,这个属性存储的是当前函数本身

    Fn.prototype.constructor===Fn  => true
    

    3.每一个对象数据类型的值,也有一个天生自带的属性:proto,这个属性指向’所属类的原型prototype’
    对象数据类型

    • 普通对象、数组、正则、Math、日期、类数组等
    • 实例也是对象数据类型的值
    • 函数的原型prototype属性的值也是对象数据类型
    • 函数也是对象数据类型的值

    原型链查找机制

    1.先找自己私有属性,有则调取使用,没有就继续找
    2.基于__proto__找所属类原型上的方法(Fn.prototype),如果还没找到则继续基于__proto__往上找,直到找到Object.prototype为止

    javascript笔记知识(可能有错误、欢迎指正)_第19张图片

    hasOwnProperty

    'hasOwnProperty’用来检测某个属性名是否为是否为当前对象的私有属性
    'in’用来检测这个属性是否属于某个对象(不管是私有属性还是公有属性,只要是它的属性,结果就为true)

    let ary=[1,2,3];
    console.log('0' in ary); => true
    console.log('push' in ary); => true
    console.log(ary.hasOwnProperty('0')); => true
    console.log(ary.hasOwnProperty('push')); => false push是公有属性而不是私有属性
    
    console.log(Array.prototype.hasOwnProperty('push')); => true 公有还是私有得看是相对谁来的  自己堆中有的就是私有的属性,需要基于__proto__(__proto__这个属性在IE浏览器中不能使用,edge除外)查找的就是公有的
    

    封装一个检测公有属性的方法

    =>扩展一个hasPubProperty方法用来检测属性是否为公有
    
            =>在object上绑定一个hasPubProperty的方法
            Object.prototype.hasPubProperty=function(prop){
                =>首先得先判断传入的属性是否为number、string、boolean等
                let propAry=['number','string','boolean'];
                let resProp=typeof prop;
                if(!propAry.includes(resProp)){
                    return false;
                }
    
                =>再判断传入的属性是否为该对象的属性
                let x=prop in this;
    
                =>再判断传入的属性是否为该对象的私有属性
                let y=this.hasOwnProperty(prop);
    
                =>如果满足是该对象的属性且不是私有属性则返回true,否则返回false
    
                if(x===true&&y===false){
                    return true;
                }else{
                    return false;
                }
            };
    
            console.log([].hasPubProperty('push')); //true
    

    扩展内置类方法(之前的数组去重把他写到类的原型上))

    !function (){
      function myUnique(){
      //创建一个空对象,用来接收循环数组中的内容
      let obj={};
    
      //循环遍历传入的数组,定义一个item将传入数组中的内容作为属性名和属性值
      for(let i=0;i

    Function中的call\apply\bind

    它们都是原型上提供的三个公有方法
    每一个函数都可以调用这些方法
    这些方法都是用来改变函数中的this指向

    call

    window.name='WINDOW'
    let obj={name:'OBJ'};
    function(){
      console.log(this.name);
    }
    fn() =>  this:window(严格模式下是undefined,非严格模式下是window)
    
    fn.call(obj); => this:obj
    

    语法:函数.call([context],[params1]…)
    函数基于原型链查找时,找到Function.prototype.call这个方法,并且把它执行,在call方法执行的时候,完成了以下一些操作

    • 让当前函数执行
    • 把函数中的this指向改为第一个传递给call的实参
    • 把传递给call的余下实参当作参数信息传递给当前函数
    • 如果执行call方法时,没有传递任何参数,则函数中的this指向在非严格模式下为window,严格模式下为undefined

    自己写一个基于原生JS实现内置call方法(跟浏览器自带的有差别)

    ~function(){
        function call(context){
          context = context || window;
            let args=[],
            result;
            for(let i=1;i

    apply

    和call方法一样,都是把函数执行,并且改变里面this的指向,唯一区别就是传递的参数形式不同
    call是一个个传递,apply则是传递一个数组

    bind

    和call和apply一样,也是用来改变this的指向,只不过bind改变指向时并没有执行该函数

    ES6基础语法

    let/const(它们之间的区别)

    let与var的区别:let不存在变量提升(当前作用域中,不能在let声明前使用)
    同一个作用域中,let不能重复声明
    let解决了typeof暂时性死区
    全局作用域中,使用let声明变量不会给window添加属性
    let会存在块级作用域(除对象外的大括号都可以被看作块级私有作用域)

    箭头函数(es6中新添加的创建函数方式)

    const fn=([形参])=>{
      //函数体
    }
    fn([实参])
    

    箭头函数的创建都是函数表达式的方式,所以不存在变量提升,函数只能在创建完成后被执行
    形参只有一个,则小括号可以不加
    大括号内只有一条语句且为return,则可以省略大括号
    箭头函数没有arguments,但是可以基于剩余运算符(…)获取实参集合,而且ES6中支持给形参设定默认值
    箭头函数中没有自己的this,它的this都是自己所处的执行上下文中的this

    ES6中的解构赋值

    让左侧出现和右侧值相同的结构,以此快速获取到我们需要的内容(项目中用的比较多的是对数组和对象的解构赋值)

    const ary=[10,20,30,40,50];
    const [n,m,...x]=ary;  => ...x拓展运算符:把剩下的内容存储到x数组中,但是这个表达式只能出现在最后
    console.log(n,m,x); => 10 20 [30,40,50]
    ======================================================
    
    const ary=[10,[20,30,[40,50]]];
    const [n,[,,[,m]]]=ary;
    console.log(n,m); => 10 50
    ======================================================
    
    const obj={
      name:'老王',
      age:18,
      sex:'boy',
      friends:['老李','老刘','老陈','老张']
    }
    
    const {name,age,xingbie}=obj
    console.log(name,age,xingbie)  => 老王 18 undefined  对象的结构赋值中,左边的属性名必须跟右边保持一样(顺序可以随意)才能取到对应的值,如果非要自己定义属性名可以sex:xingbie
    
    const {name,sex:xingbie,age}=obj; 
    console.log(name,xingbie,age) => 老王 boy 18
    ======================================================
    const {
      name,
      friends:[firstFrineds]
    }=obj;  
    console.log(name,firstFrineds) => 老王 老李
    ======================================================
    
    const data={
      'code':0,
      'data':{
          'today':'2021-07-02',
          'data':[{
              'date':'2021-07-01',
              'number':'10',
              'weekday':'\u661f\u671f\u4e09'
          },{
              'date':'2021-07-02',
              'number':'20',
              'weekday':'\u661f\u671f\u56db'
          }]
      }
    }
    
    const {
      code,
      data:{
        today,
        data:calenderData
      }
    }=data;
    
    console.log(code,today,calenderData); => 0 "2021-07-02" 后面对象中的两条数据
    
    calenderData.forEach((item)=>{
      let weekday=item.weekday,
      date=item.date;
      console.log(weekday,date);
    })
    

    ES6中的’…’

    拓展运算符(多用在解构赋值中)
    展开运算符(多用在传递实参中)
    剩余运算符(多用在接收实参中)

    => 拓展运算符
    const ary=[10,20,30];
    const [n,...m]=ary;
    console.log(n,m) => 10 [20,30]
    
    
    => 展开运算符
    const ary=[10,20,30];
    let min=Math.min(...ary);  =>相当于Math.min(10,20,30)
    
    => 数组克隆(浅克隆)
    const cloneAry=ary.slice(0);
    
    const cloneAry=[...ary]
    
    => 对象克隆(浅克隆)
    const obj={
      name:'老王',
      age:18,
      sex:'男'
    }
    
    const cloneObj={...obj,age:20,sex:'女'}
    console.log(cloneObj); => {name: "老王", age: 20, sex: "女"}
    
    
    => 剩余运算符
    const fn=(n,...m)=>{
      //n:10   ...m:[20,30]
    }
    fn(10,20,30);
    

    ES6中的class创建类

    => 传统方式创建类
    function Fn(){
      this.x=100;
    }
    Fn.prototype.getX=function(){
      console.log(this.x);
    }
    var f1=new Fn();
    f1.getX();
    
    // 也可以当作普通函数执行
    Fn();
    
    // 还可以把Fn当作普通的对象
    Fn.queryX=function(){}
    Fn.queryX();
    
    
    =>ES6中class类创建
     class Fn{
       constructor(n,m){
         this.x
       }
    
       getX{
         console.log(this.x)
       }  //=> 写在原型上的公有方法
    
       static queryX(){};//=> 前面加上static,把当前Fn当做普通对象设置键值对
       static z=100;
       y=100; //给实例设置私有属性
     }
    

    ES6中的模板字符串

    反引号括起来${}

    let year=2021,
    month=07,
    day=02;
    
    let res=`今天是${year}年${month}月${day}日`;
    console.log(res);  =>今天是2021年7月2日
    

    类数组借用数组的方法

    => 实现一个求和方法
    function sum(){
      const ary=[];
      let total=null;
      ary.forEach.call(arguments,(item)=>{   //将数组中的this指向arguments
        total=total+item; 
      })
    
      return total;
    }
    
    sum(10,20,30,40); //100
    

    客户端与服务端的交互

    当用户在浏览器窗口的地址栏输入网址,到最后看到页面,中间都经历的哪些东西?

    • 1.URL地址解析
    • 2.DNS域名解析
    • 3.和服务端建立TCP连接
    • 4.把客户端信息传递给服务端(发送HTTP请求)
    • 5.服务器得到并处理请求(HTTP响应内容)
    • 6.服务端渲染服务器返回的内容
    • 7.和服务器断开TCP连接

    URI/URL/URN

    URL(Uniform Resource Locator): 统一资源定位符,根据地址找到相对应的资源
    URI(Uniform Resource Identifier): 统一资源标识符,URN和URL是URI的子集
    URN(Uniform Resource Name): 统一资源名称,一般指国际上通用的(标准的)一些名字(例如:国际统一发版的编号)

    一个完整的URL应该包含的内容

    https://www.bilibili.com/video/BV1rV411n72v?p=294&spm_id_from=pageDriver

    • 协议(http://)

      • http 超文本传输协议,除了传递文本,还可以传递媒体资源文件(或者流文件)以及XML格式数据
      • https 更加安全的http,一般涉及支付的网站都会用该协议(s:ssl加密传输)
      • ftp 文件传输协议,一般应用于把本地文件上传到服务器端上
    • 域名(www.bilibili.com)

      • 顶级域名 qq.com
      • 一级域名 www.qq.com
      • 二级域名 sports.qq.com
      • 三级域名 kbs.sports.qq.com
      • .com 国际域名
      • .cn 中文域名
      • .com.cn
      • .edu 教育
      • .gov 政府
      • .io 博客
      • .org 组织
      • .net 系统类
    • 端口号(:80):端口号的取值范围0-65535,用端口号来区分同一台服务器上的不同项目

      • http 默认端口号为:80
      • https 默认端口号为:443
      • ftp 默认端口号为:21
      • 如果项目采用的就是默认端口号,我们在书写地址的时候,不用加端口号,浏览器在发送请求的时候会默认帮我们加上
    • 请求资源路径名称(/video/BV1rV411n72v)

      • 默认的路径或者名称(xxx.stu/不指定资源名,服务器会找默认的资源,一般默认资源名是default.html、index.html等,这些都可以在服务器端自己配置)
      • 注意伪URL的地址处理(URL重写技术是为了增加SEO搜索引擎的优化,动态的网址一般不能放在搜索引擎搜索,所以我们要把动态网址静态化,此时需要重写URL)
        https://item.jd.hk/2688449.html => https://item.jd.hk/index.php?id=2688449
    • 问号传参信息(?p=294&spm_id_from=pageDriver)

      • 客户端想把信息传递给服务端有很多种方式
        • URL地址问号传参
        • 请求报文传输(请求头和请求主体)
      • 也可以不同页面之间信息交互,例如从列表到详情
    • HASH值(#…)

      • 也能充当信息传输的方式
      • 锚点定位
      • 基于HASH实现路由管控(不同的HASH值,展示不同的组件和模块)

      DNS服务器域名解析

      DNS服务器:域名解析服务器,在服务器上存储着域名 <=> 服务器外网IP 的相关记录
      而我们发送请求的时候,所谓的DNS解析,其实就是根据域名,在DNS服务器上查找对应服务器的外网IP

      DNS优化

      • DNS缓存(一般浏览器在第一次解析后,默认建立缓存,时间很短,只有大概一分钟)
      • 减少DNS解析次数(一个网站中我们需要发送请求的域名和服务器尽可能的减少即可)
      • DNS预获取(dns-prefetch):在页面加载开始的时候,就把当前页面中需要访问的其它域名(服务器)的信息进行提前DNS解析,以后加载到具体内容部分就可以不用解析了

      建立TCP连接(三次握手)

      HTTP报文

      • 请求报文:所有经过传输协议,客户端传递给服务端的内容,都被称为请求报文
      • 起始行
      • 请求首部
      • 请求主体
      • 响应报文:所有经过传输协议,服务器返回给客户端的内容,都被称为响应报文
      • HTTP状态码
      • 响应首部
      • 响应主体
      • HTTP报文:请求报文+响应报文

      HTTP状态码

      1-5开头,三位数字

      • 200 OK:成功
      • 201 CREATED:一般用于告诉服务器创建一个新文件,最后服务器创建成功后返回的状态码
      • 204 NO CONTENT:对于某些请求(例如:PUT或DELETE),服务器不想处理,可以返回空内容,并且用204状态码告知
      • 301 Moved Permanently:永久重定向(永久转移)
      • 302 Moved Temporary:临时转移,很早以前基本用302来实现,但是现在主要用307来做
      • 304 Not Modified:设置http的协商缓存
      • 307 Temporary Redirect:临时重定向,主要用于服务器的负载均衡等
      • 400 Bad Request:传递给服务器的参数错误
      • 401 Unauthorized:无权限访问
      • 404 Not Found:请求地址错误
      • 500 Internet Server Error:未知服务器错误
      • 503 Service Unavailable:服务器超负荷

    AJAX的四个步骤

    1.创建AJAX实例
    let xhr=XMLHttpRequest //=> IE6以下用的是 new ActiveXObject()

    2.打开URL(配置发送请求的信息)
    method:http请求方式
    URL:请求地址(API接口地址)
    ASYNC:设置同步或者异步,默认true是异步,false是同步
    user-name:传递给服务器的用户名
    user-pass:传递给服务器的密码
    xhr.open(‘GET’,’./json/xxx.json’,true)

    3.监听ajax状态,在状态为X的时候,获取服务器响应的内容
    Ajax状态码: 0 1 2 3 4

    4.发送请求(send中放的是请求主体的内容)

    http请求方式

    • get系列请求(从服务器上拿的多,给的少)
      • get
      • delete:一般用于告诉服务器,从服务器上删除信息
      • head:只想获取响应头的内容,告诉服务器响应主体的内容不想要
      • options:试探性请求,发个请求给服务器看看能不能接收到,能不能返回
    • post系列请求(从服务器上拿的少,给的多)
      • post
      • put(和delete对应):告诉服务器把我给的信息存储到服务器上(一般用于文件和大型数据内容)

    真实项目中最好使用对应的请求(语义化)

    get系列一般用于从服务器获取信息,post一般用于给服务器发送信息,但是两个都能把信息传递给服务器和从服务器上获取信息,只是两者谁多谁少而已

    客户端是如何把信息传递给服务端?

    • 问号传参 xhr.open(‘GET’,’./getdata?xxx=xxx&xxx=xxx’)
    • 设置请求头 xhr.setRequestHeader([key],[value])
    • 设置请求主体 xhr.send(主体内容)

    服务端是如何把信息返回给客户端?

    • 设置响应头
    • 设置响应主体(大部分信息是通过响应主体返回的)

    get系列和post系列本质区别
    get系列传递给服务器的方式一般为:问号传参
    post系列传递给服务器的方式一般为:设置请求主体
    1.get传递给服务器的内容比post少,因为URL有最大长度限制(IE:2k google:4-8K),超出的部分被浏览器截取了
    2.get会产生缓存(这个缓存是不可控的),因为请求的地址(尤其是问号传递的信息),浏览器有时候会认为你要和上次请求的信息一样,就会拿取上一次的信息,因此我们需要去除这个缓存,方法是在地址后面加随机数
    3.get相比post不安全(虽然两个都不安全):get基于问号传参,容易被URL劫持,而post是请求主体,不容易被篡改

    AJAX状态码

    获取状态码:xhr.readyState

    • UNSEND 0 :未发送(创建一个xhr,初始状态为0)
    • OPEND 1 :已经打开(执行了xhr.open)
    • HEADERS_RECEIVED 2 :响应头信息已经返回给客户端(发送请求后,服务器会依次返回响应头和响应主体的信息)
    • LOADING 3 :等待服务器返回响应主体信息
    • DONE 4 :响应主体信息已经返回给客户端

    Ajax中的7个方法

    • abort()中断请求信息
    • getAllResponseHeaders()获取所有响应头信息
    • getResponseHeader()获取响应头信息
    • open()
    • send()
    • setRequestHeader()设置请求头信息
    • overrideMimeType()重改Mime类型

    Promise

    let promiseExample=new Promise((resolve,reject)=>{
      //这里一般存放我们要操作的异步任务,任务成功执行resolve函数,失败执行reject函数
    })
    
    => promise的第一个参数必须传递一个函数,我们把它称作executor
    => 当我们new一个promise的实例对象的时候就会把这个executor执行
    => promise不仅会把executor执行,还会给executor传递两个参数,resolve函数和reject函数
    => resolve函数代表promise处理的异步事件是成功的,然后把promise的状态改为fulfilled
    => reject函数代表promise处理的异步事件是失败的,然后把promise的状态改为rejected
    => promise.prototype上有三个常用方法,分别为then,catch,finally
    => then是在执行异步操作完成之前,往事件池中添加两个方法(成功和失败需要做的事情),等到异步操作完成时,再根据resolve或者reject去执行对应的事件,catch代表捕获错误,finally是不论成功还是失败他都会执行里面自己设置的事情
    
    

    let promiseExamp=new Promise((resolve,reject)=>{
    setTimeOut(()=>{
    let ran=Math.random();
    ran<0.5?reject(ran):resolve(ran)
    },1000)
    })

    promiseExamp.then((success)=>{
    console.log(‘成功’+success)
    },(error)=>{
    console.log(‘失败’+error)
    })

    promiseExamp.catch((message)=>{
    console.log(‘失败’+message)
    })

    promiseExamp.finally(()=>{
    console.log(‘不论成功失败我都会出现!’)
    })

    你可能感兴趣的:(javascript,html,es6,ajax)