JavaScript ES5-11整合 方便检索

2022-04-10  本文已影响0人  Zindex

简介

ECMAScript是JavaScript的标准,JavaScript实现了ECMAScript,ECMAScript规范了JavaScript。

JavaScript体系

  1. 语言核心
  2. DOM
  3. BOM

JavaScript书写位置

body里面写script标签,例如:

<body>
    <script>
        alert('hello, world');
    </script>
</body>

或者

<script src=" .js" type="text/javascript" charset="utf-8"></script>

认识输出、输入语句

alert( ) 语句 弹窗
console.log( ) 语句 控制台输出

比如:

alert('haha');

alert( ) 是内置函数,函数就是功能的封装,调用函数需要使用小括号。
'haha' 是函数的参数,因为是字符串,所以要用引号包裹。

console.log('test');

console是js的内置对象,通过 打点 可以调用它内置的 log 方法。
所谓方法就是对象能够调用的函数。

var input = prompt('请输入内容');

输入语句 prompt( ) 函数,弹出框输入,输入的内容会转为字符串类型。

学会处理报错

常见两个错误:
Uncaught SyntaxError: Invalid or unexpected token
未捕获的语法错误:不合法或错误的符号。
Uncaught ReferenceError: a is not defined
未捕获的引用错误:a没有被定义。
函数名拼写错误也会引发 ReferenceError。

控制台

控制台也是一个REPL环境,可以使用它临时测试。
REPL:read 读 eval 执行 print 打印 loop 循环

变量

变量能储存计算结果 或 表示一个值;
变量不是数值本身,只是储存值的容器。

定义变量

var 关键字, 定义变量后,就可以使用了,可以用等号赋值。如果没有负值,默认值为undefined。可以同时声明多个变量,用逗号隔开。

var a;
console.log(a);         //此时打印undefined
a = 7;
var b,
      c,
      d;

变量的合法命名

变量命名可以用多种风格,例如:

  1. 小驼峰命名法:首字母小写,后面每个单词头字母大写。
  2. C风格命名法:每个单词用下划线分隔。

变量命名符合JS标识符命名规则,变量、函数、类名、对象的属性等都要遵守。

标识符命名规则

  1. 只能由字母、数字、下划线、美元符号$组成,不能以数字开头。
  2. 不能是关键字或保留字,例如float等。
  3. 变量名大小写敏感,例如a和A不同。

全局变量声明提升

全局预编译

  1. 生成AO对象。
  2. 找变量声明,将变量名作为GO的属性名,值为undefined。
  3. 在函数体里找函数声明。

数据类型

  1. 基本数据类型:number、string、boolean、undefined、null
  2. 复杂数据类型:Array、function、object 等...

typeof 运算符

基本数据类型

number 数字类型

所有数字不分大小、不分整数小数、不分正负,都是数字类型。
零点几的0可以省略。
科学计数法:3e7 => 3*10⁷。

不同进制的数字
二进制数值以 0b 开头:0b10 => 2;
八进制数值以 0 开头:017 => 15;
十六进制数值以 0x 开头。

一个特殊的数字类型值NaN

一个特殊的数字类型值Infinity

string 字符串类型

字符串的length属性:表示字符串的长度。

字符串的常用方法:

方法名 功能
charAt( ) 得到指定位置字符
substring( ) 提取子串
substr 提取子串
slice 提取子串
toUpperCase 将字符串变为大写
toLowerCase 将字符串变为小写
indexOf 检索字符串

charAt( )方法

substring( )方法
sub 子,string字符串。
substring(a,b) 得到从 a 开始到 b 结束(不包括 b 处)的子串。
如果省略第二个参数,返回的子串会一直到字符串的结尾。
参数会自动从小排到大,比如 (5,3) 会自动变为 (3,5)。(慎用,可读性差)

substr( )方法
substr(a,b) 得到从a开始的长度为b的子串。
a可以是负数,表示倒数位置。b不能是负数,否则会返回空串。

slice( )方法
slice(a,b) 得到从a开始到b结束(不包括b处)的子串。
a 可以是负数,表示倒数,倒数的下标从-1开始。
参数 a 必须小于参数 b。

toUpperCase( )转为大写,toLowerCase( )转为小写

indexOf( )方法
返回某个指定的字符串值再字符串中首次出现的位置(索引值)。
如果要检索的字符串没有出现,则返回-1。
传入的参数是字符串,例如'abcdef'.indexOf('de') 返回3。
此方法还可以传入第二个参数,代表开始检索、搜索的索引位置。

boolean 布尔类型

小知识:布尔(Boole.George)英国19世纪数学家及逻辑学家。
布尔类型 值只有两个:true 和 false ,分别表示真假。

undefined 类型

undefinded 又是值,又是一种类型。
undefined 是全局对象上的一个属性,window.undefined
不可写 writable: false,比如window.undefined = 1无效。
不可配置 configurable: false,比如delete window.undefined无效。
不可枚举 enumerable: flase ,比如

for(var k in window){
    if(k === undefined){
        console.log(k);
    }
}
不打印内容

不可重新定义 cannot redefine property: undefined;
系统会给一个未赋值的变量自动赋值为undefined,类型也是undefined 。
函数内部不写返回值的时候,返回的也是一个undefined。
在函数内部可以把undefined当作一个变量名来用,但尽量别用。例如:

function test(){
    var undefined = 1;
    console.log(undefined);
}
test();
会打印1

void(0) 会返回undefined

null

null是基本数据类型的值,typeof返回类型是object。
代表空、空对象。
可用于对象销毁,数组销毁,事件清空。

显式类型转换

其他类型 转 数字

  1. 使用 Number( ) 函数
    字符串 → 数字:纯数字字符串能变为数字,不是纯数字的转为NaN,空串转为0.
    布尔值 → 数字:true转为1,false转为0.
    undefined → 数字:undefined转为NaN。
    null → 数字:null转为0。
  2. 使用 parseInt( ) 函数。
    用在字符串上时:将自动截断第一个非数字字符之后的所有字符,点也算,不会四舍五入。
  3. 使用 parseFloat( ) 函数。
    将字符串转为浮点数,只会识别到第一个点。

其他类型 转 字符串

  1. 使用 String( ) 函数,变为长得相同的字符串。
  2. 使用 toString( ) 方法,数字使用 toString 要加括号,例如 (7).toString();

其他类型 转 布尔值

使用 Boolean( ) 函数。
数字→布尔:除了0和NaN是false,其他true。
字符串→布尔:空窗false,其他true。
undefined和null:转成布尔值都是false。

表达式 和 运算符 和 隐式类型转换

表达式

表达式,由运算元和运算符构成,并产生运算结果的语法结构。
表达式的种类有:算术、关系、逻辑、赋值、综合。

算术运算符

符号 功能
+ 加法、正数、连字符
- 减法、负数
* 乘法
/ 除法
% 取余

取余,也叫求模运算,用百分号 % 表示,小数模大数得小数。
乘除余运算优先级高于加减运算。

算术运算符的隐式类型转换

如果参与数学运算的值不是数值型,会自动转换为数值型然后运算,加号例外因为有拼串功能。其本质是内部调用 Number( ) 函数,例如:

true + true;
//Number(true) + Number(true)  然后返回一个2

+'1'        //会返回一个数字1
3*'2天'      //会返回NaN

算术运算符中浮点运算问题IEEE754

在JavaScript中,有些浮点运算不是很精准,因为采用的是IEEE754二进制浮点数算术标准,会产生“精度丢失”问题。
解决方法:可以用数字的 toFixed() 方法保留指定的小数位数,类型会变成字符串。例如:
(3.141592654).toFixed(2)

其他运算的Math方法

function getRandom(min,max){
    return parseInt(Math.random()*(max-min+1))+min;
}

关系运算符

符号 意义
> 大于
< 小于
>= 大于或等于
<= 小于或等于
== 等于
!= 不等于
=== 全等于
!== 不全等于

进行关系运算符运算候的结果返回值是boolean布尔值。
除了全等和不全等以外,其他关系运算符都会隐式转换两边值为数字类型。
例如:

1 == '1';                       //返回true
undefined == null;       //这是个例外,undefined转数字是NaN的,但返回true
undefined === null;     //返回false    
NaN == NaN;             //返回false,NaN不自等,所以要用isNaN( ) 函数判断

没有数学书写上的连比3<=16<=15,要连比只能用逻辑运算符

逻辑运算符

符号 意义
!
&&
||

! 非

&& 与运算

找假返回假,如果没有假则返回最后一个真。(都真就真)

|| 或运算

找真返回真,如果没有真则返回最后一个假。(有真就真)

逻辑运算符优先级:非>与>或

赋值表达式

赋值运算符

符号 意义
= 赋值
+= 快速赋值
-= 快速赋值
*= 快速赋值
/= 快速赋值
%= 快速赋值
++ 自增运算
-- 自减运算

赋值运算也会产生值,例如:

var a;
console.log(a=7);
//a=7会返回7,所以会打印7而不是a=7。

所以可以连续使用赋值运算(实际开发禁止使用),例如:

var a,b,c;
a=b=c=7;
//先c=7返回7,再b= c=7 7,再a= b=c=7 7

快速赋值:+= 、-= 、*= 、 /= 、%=

在原数值基础上进一步运算。

自增自减:++、--

自身基础上加一或减一。
a++和++a区别:a++先用再加,++a先加再用。例如:

var n1=1,n2=2;
var n3 = (n1++)+n2
//此时n3等于3

综合表达式

综合运算顺序:非运算>数学运算>关系运算>逻辑运算 (非数关逻)

流程控制语句

条件语句:if、if elseif else、switch、三元运算符。
循环语句:for、while、break和continue、do while。

条件语句

if语句

if(条件){
    语句块1,当条件为真时执行
}else{
    语句块2,当条件为假时执行
}

if语句执行流程图

graph TD
A[程序执行] -->| | B(执行)
B --> C{测试条件}
C -->|条件1| D[语句块1]
C -->|条件2| E[语句块2]

else if 否则如果
else if( ) 条件分支“暗含”不符合之前的所有条件。

if( 测试表达式1){
    当测试表达式1为true时执行
}else if(测试表达式2){
    当测试表达式2为true时执行
}else{
    所有表达式为false时执行
}

if 语句算法题

第一题: 判断水仙花数。

如何拆位?

  1. 数学方法:百位是原数值除以100取整。十位是原数字除以10取整,再与10求模。个位是原数字与10求模
  2. 字符串方法:直接将原数字变为字符串,然后使用charAt( )方法。

第二题: 游乐园门票计算。

年龄大于等于10 年龄小于10岁
平日 300 140
周末 500 210

switch 语句

用途:当一个变量被分类讨论的情形。

  1. switch( ) 括号内一般是一个变量名,这个变量被分类讨论。
  2. case表示情况,后面直接跟一个值,程序会将这个值依次与switch括号内的变量匹配,如果全等,则执行case后的语句,多条case可以共用一个语句体。
  3. default 表示默认情况。
  4. break 跳出switch语句。

三元运算符

条件表达式 ? 表达式1 : 表达式2

当条件为真执行1,为假执行2。

循环语句

for 循环语句

for的小括号有三个表达式:
表达式1:for循环内可用的变量,循环变量一般定为i,意为iterator迭代器。
表达式2:只要这条返回true,就会继续执行后面的语句块。
表达式3:一般用来更新循环变量。
for循环执行机理:

for(var i=1①; i<=10②; i++④){
    console.log(i)③;
}
//①→②→③→④

也就是说,for循环小括号内的第三条语句,是先执行中括号内的语句块,再执行的。

for循环算法题

  1. 累加器
  2. 1到100哪个数除3余1,除4余2,除5余3.(穷举法)

while 循环语句

是一种“不定范围”循环。先不指定循环开始和结束范围。只要满足测试条件,就一直执行循环体。

while(测试条件){
    循环执行的语句块;
}

循环注意: 当循环条件终止值与更新的循环体内的值不是同一个时,当要求的值是循环值时,注意“出一错误”

循环语句中的break和continue

执行 break; 会立即终止循环。
执行 continue; 会跳过循环中的一个迭代,跳过其中一次循环,这一次循环体内的continue后面的其他语句将跳过执行一次。

do while 循环

先执行一次循环体,再判断测试条件是否满足。

do{
    循环执行的语句块(会先执行一次);
}while(循环执行条件)

while循环算法题 猜数字

流程语句经典算法题

可以先用伪代码写下解决思路,再翻译成js语法。

累加器与累乘器

  1. 计算 3/2 + 4/3 + 5/4 + 6/5 + ...... + (n+1)/n 的结果
  2. 由用户输入数字n,计算n的阶乘。for(var i=n; i>=1; i--){result*=i;} result要从1开始
  3. 圆周率可以由莱布尼茨级数公式计算,用户输入n,计算圆周率Π。
    Π/2= 1 + 1/3 + 1*2/3*5 + 1*2*3/3*5*7 + 1*2*...n/3*5*...(2n-1)

穷举法

  1. 100以内,能整除3也能整除5的数。
  2. 输入一个数,在控制台显示这个数的全部约数。
  3. 寻找全部水仙花数。(每个数位的立方和等于它本身)。

综合算法题

  1. 寻找100以内的质数。(用到给外层for循环套label,然后用continue label)
  2. 鸡兔同笼,35头,94足。

数组

Array ,存储一组值。

如何定义数组

  1. 使用方括号: var arr = ['A','B','C'];
  2. 使用 new Array 构造函数:var arr = new Array('A','B','C');
    当new Array函数只传入一个数字时,则定义一个长度为n的数组。

数组的每一项都有下标,下标从0开始。
使用 数组变量 接方括号中写下标 的形式,可访问数组项,例如:a[0];
当下标越界,会返回undefined而不会报错。

数组的length属性表示长度,数组的最后一项的下标时length-1

数组是可读可写的,如果更改的项下标越界,则会创造这项。

数组的遍历:

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

数组的不停写入,可用while循环(数组自增):

var arr = [];
while( ){
    arr[arr.length]=
}

判断是不是数组

数组用typeof 检测结果是object,可用 Array.isArray( ); 方法检测。

数组相关遍历算法题

  1. 求数组中每一项的总和、平均数。
  2. 求数组项的最大值和最小值。

数组的头尾操作方法

方法 功能
push( ) 在尾部插入新项
pop( ) 在尾部删除
unshift( ) 在头部插入
shift( ) 在头部删除

push( ) 方法:推入新项,如果要推入多项,用逗号隔开。
pop( ) 方法:删除数组中的最后一项,该方法会返回被删除的项,可用一个变量接收。
unshift与shift特性与push、pop相似。以上方法均会改变原数组

数组的其他方法

会改变数组的:

方法(会改变原数组) 功能
reverse 逆转排序
splice 切割
sort 改序

reverse( ) 方法
用于将数组中的全部项顺序置反。

splice( ) 方法
用于替换数组中的指定项。用法:

arr.splice(从下标为n的项开始 , 替换多少项 , 替换的新项可多项);

当第二个参数为0时,就会变为插入功能。
当第三个参数不写时,就会变为删除功能。
该方法会返回被删除的项组成的数组。

sort( ) 方法
arr.sort( );
sort开放了接口,可传入一个函数 arr.sort(function(){ });

  1. 必须写两形参! arr.sort(function(a,b){ });
  2. 看返回值
    1)返回值为正时,后面的数放前边。
    2)返回值为负时,前面的数放前边。
    3)返回值为0时,不做操作。
    自己总结就是,正数交换位置,负数不换(正交负不换),所以:
arr.sort(function(a,b){return a-b;})            //升序
arr.sort(function(a,b){return b-a;})            //降序
arr.sort(function(a,b){return Math.random()-0.5;})          //乱序

不改变数组的方法:

方法(不改变原数组) 作用
slice( ) 截取数组
concat( ) 连接多个数组

数组的slice方法:
不会改变原数组,用于得到子数组。
slice(a,b) 截取从下标为a开始,到下标为b(不包括b)结束。
如果不传第二个参数。就会截取从a开始的后面所有项。参数允许为负数。
数组的concat方法: 合并连接多个数组,不改变原数组。

数组与字符串间转换的方法,以及更多通性

数组转字符串: join( )方法,将数组改变为字符串类型并以参数作为连接符输出,不传参数默认逗号。传递的参数要用引号括起来。
字符串转数组: split( )方法,以什么字符拆分字符串,参数为空时将拆为一个字符串。
更多相关性:

  1. 可以使用方括号写下标的形式访问某个字符,等价于charAt( )方法。
  2. 字符串的一些算法问题可以改为数组解决。

indexOf( ) 和 includes( )

indexOf( ) 方法:搜索数组中的元素并返回下标数,如果不存在返回-1。
includes( ) 方法:判断数组是否包含一个元素,返回布尔值。

数组经典算法题

冒泡排序:

for(var i=1; i<=arr.length-1;i++){
    for(var j=0; j<arr.length-i; j++){
        // j多功能担任遍历数组的下标
        if (arr[j]>arr[j+1]) {
//因为j是访问不到最后一位的,j<arr.length-i,j最大只能到每一趟的倒数第二位。但是因为在if里面比较的是[j+1]位,所以第一轮刚好能访问到最后一位
            var temp = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = temp;
        }
}

数组去重:
思路:准备一个空数组,遍历原数组,如果遍历到的项新数组里没有(用indexOf判断或includes判断,后者是ES6),则push进去。

function unique(arr){
    var newArr = [];
    for(var i=0;i<arr.length;i++){
        if(newArr.indexOf(arr[i])===-1){
            newArr.push(arr[i]);
        }
    }
    return newArr;
}

随机样本:
思路:准备一个空数组,遍历原数组,随机选择一项,推入新数组,并删除这项。

二维数组

数组高阶函数map() reduce() filter()

map方法接收一个函数作为参数,这个函数会接收三个参数:参数一是这个数组里的每一项,参数二是每一项的下标索引值,参数三是调用map方法的这个数组本身,return的是指定的值进一个新的数组。最后map方法返回这个新的数组

var arr = [{name:'ming',age:18},{name:'he77',age:20},{name:'qi',age:24}]
var newArr = arr.map(function(item,index,arrSelf){
    console.log(item,index,arrSelf)
    return item.age
})
console.log(newArr)     //[18,20,24]

reduce方法接收一个函数作为参数以及一个“开始计算的初始值”,这个函数会接收两个参数:参数一是上一次return的结果,参数二是这一次参与计算的数据。不断的计算最后return一个值。最后reduce方法返回这个值

var arr = [{name:'ming',age:18},{name:'he77',age:20},{name:'qi',age:24}]
var newValue = arr.reduce(function(previous,joining){
    console.log(previous,joining)
    return previous+joining.age
},0)
console.log(newValue)   //62

filter方法接收一个函数作为参数,这个函数的参数会接受一个参数,参数一是这个数组的每一项,return符合判断为true的数据进一个新数组,最后filter方法返回这个新数组。

var arr = [{name:'ming',age:18},{name:'he77',age:20},{name:'qi',age:24}]
var  newArr = arr.filter((item)=>{return item.name.includes('i')})
console.log(newArr)     //[{name:'ming',age:18},{name:'qi',age:24}]

引用类型和基本类型

基本类型:number、string、boolean、undefined、null
引用类型:array、object、function、regexp...

相等判断:

所以浅克隆中如果克隆到数组,并不完全克隆,这个时候就需要深克隆。

函数

函数就是语句的封装,方便复用,具有“一次定义,多次调用”的优点。

函数的定义

使用function关键字,function是功能的意思。例如:

function fun( ){

}

function表示定义函数。
fun 是函数名,命名规则见js标识符命名规则。
圆括号()是形参列表,即使没有形参,也必须书写圆括号。
大括号{ }是函数执行的语句体

第二种定义函数方法是使用函数表达式:

var fun = function(){ }

调用函数

在函数名字后面书写圆括号即可,也可以称为函数执行符。。

函数声明提升

只有函数声明能提升,函数表达式不提升。提升相关详情见GO、AO预编译。
例题

fun();
var fun = function(){alert('a');}
function fun(){alert('b');}
fun();
//执行这段程序,会先弹出b,然后弹出a。
//所以函数只是声明提升,预编译后不再参与变量赋值。

函数的参数

函数声明时圆括号内定义形参(形式参数)
调用函数时传入实参(实际参数)

arguments

函数内的 arguments 表示它接收到的实参列表,他是一个类数组对象。

类数组对象:
是对象,可以当数组用。
和数组类似可以用方括号访问对象的属性值,但不能调用数组的方法,可以自己写入模拟数组方法。

构成要素:
①属性要为索引(数字)属性
②必须要有length属性

return 函数的返回值

使用return关键字。
调用一个有返回值的函数,可以当一个普通值来用。
遇见return语句会立即退出函数。(结合if语句,往往不需要写else分支了)
return后不接内容则函数返回undefined,接内容则函数返回内容。

函数算法题

寻找喇叭花数(喇叭花数:三位数,每一位数字阶乘的和等于他本身)

函数的递归

函数内部语句调用这个函数自身,从而发起函数新一轮迭代,在新的迭代中又执行调用函数自身的语句,一直迭代下去,知道函数执行到某一次时不在迭代,然后一层一层返回,则形成函数递归。

递归的要素

  1. 边界条件:确定递归何时终止,也称递归出口。
    ①要么找到当参数等于某个值时候函数有结果
    ②要么函数执行次数有限,当循环到某一次的时候不再调用函数自身。
  2. 递归模式:大问题分小问题,递归体。

递归常见算法题

斐波那契数列:每项等于前两项的和。

function fibonacci(n){
    if(n==0) return 0;
    else if(n==1) return 1;
    return fibonacci(n-1)+fibonacci(n-2);
}

实现深度克隆数组:

function arrClone(arr){
    var newArr=[];
    for(var i=0;i<arr.length;i++){
        if(!Array.isArray(arr[i])) newArr[newArr.length]=arr[i];
        else newArr[newArr.length] = arrClone(arr[i]);
    }
    return newArr;
}

变量作用域

JavaScript是函数及作用域编程语言:变量只在其定义时所在的 function 内部有效,function 内的变量成为 function 的局部变量。
不见变量定义在任何函数里面,这个变量就是全局变量,或者 function 内不声明变量直接赋值也是全局变量。
遮蔽效应: 如果函数中也定义了与全局变量同名的变量,则函数内的变量会将全局变量“遮蔽”。

js运行三部曲

  1. 语法解析
  2. 预编译
  3. 解释执行

会发生以下几种情况:
函数声明整体提升
变量声明提升
imply global 暗示全局变量,window就是全局

预编译发生在函数执行前一刻。
全局预编译创建GO对象。
局部预编译先创建AO对象,再逐步执行(部分执行)。

全局预编译三部曲

  1. 生成GO对象。
  2. 找变量声明,将变量名作为GO属性名,值为undefined。
  3. 在函数体里面找函数声明。

局部预编译四部曲

  1. 生成AO对象(activation object)
    AO{ }
  2. 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined。
AO{
    a: undefined;
    b: undefined
}
  1. 将实参值与形参统一。
AO{
    a: 1;
    b: undefined
}
  1. 再函数体里找函数声明。
AO{
    a: function a(){ };
    b: undefined;
    d: function d(){ }
}

闭包

JavaScript的函数会产生闭包(closure)。闭包是函数本身和该函数声明时所处的环境状态的组合。
函数能够“记忆住”其定义时所处的环境,即使函数不在其定义的环境被调用,也能访问定义时所处环境的变量。
在Javascript中,每次创建函数时都会创建闭包。
但是,闭包特性往往需要将函数“换一个地方”执行,才能被观察出来。
闭包非常实用,它允许我们将数据与操作该数据的函数关联起来。
用途:

  1. 记忆性:当闭包产生时,函数所处环境的状态会始终保持在内存中,不会在外层函数调用后自动清除。
  2. 模拟私有变量。

坏处:
滥用闭包会造成内存泄漏,引发性能问题。

立即执行函数 IIFE

IIFE全称:Immediately Invoked Function Expression,立即调用函数表达式,一被定义就立即被调用。

IIFE形成方法

只有表达式才能被执行符号(圆括号)执行。

表达式:由运算元和运算符构成,并产生运算结果的语句结构

能被执行符号执行的表达式会忽略这个函数的名字,所以立即执行函数写函数名会被忽略。

IIFE作用

  1. 为变量赋值,例如一个值需要经过一段流程控制语句后才产生,那么就可以把这堆流程控制语句封装成函数并立即执行然后返回值。
  2. 将全局变量变为局部变量(让IIFE里面的函数立即形成一个新的闭包)。

DOM

DOM是 Document Object Model 文档对下个模型,是Javascript操作HTML文档的接口。

DOM 节点树

节点的四个属性

属性名 作用
nodeName 元素的标签名,以大写形式表示,只读
nodeValue Text节点或Comment节点的文本内容,可读可写
nodeType 该节点类型,见下表
attributes Element节点的属性集合

nodeType 常用属性值

属性
1 元素节点,例如<p>和<div>
3 文字节点
8 注释节点
9 document节点
10 DTD节点

延迟运行

访问元素节点

寻找/遍历/访问主要依靠document对象。

几乎所有DOM的功能都封装在 document对象中。
document 对象也表示整个HTML文档,是DOM节点树的根

常用方法有:

方法 功能
document.getElementById( ) 通过ID得到元素
document.getElementsByTagName( ) 通过标签名得到元素数组
document.getElementsByClassName( ) 通过类名得到元素数组
document.getElementsByName( ) 只有部分标签name可生效
document.querySelector( ) 通过选择器得到元素
document.querySelectorAll( ) 通过选择器得到元素数组

document.getElementById( )

当html元素有id属性的时候,可以用id获取该元素节点
注意事项:

  1. 如果页面上有相同id的元素,则只能得到第一个。
  2. 不管元素藏得多深,都能通过id找到

getElementsByTagName( )

通过标签名得到节点数组。
注意事项:

  1. 数组方便遍历,从而可以批量操控元素节点。
  2. 即使页面上只有一个指定标签名的节点,也将得到长度为1的数组。
  3. 任何一个节点元素可以调用getElementsByTagName方法,比如box1.getElementsByTagName( 'p');, 从而得到一个元素内部的子元素节点。

getElementsByClassName( )

通过类名得到节点数组。
注意事项:

  1. 从IE9开始兼容。
  2. 其他同上Tag特性

querySelector( )

通过选择器得到元素。例如:
var the_p = document.querySelector('#box1 .p')
注意事项:

  1. querySelector( )方法只能得到页面上一个元素,如果有多个元素符合条件,只能得到第一个元素。
  2. querySelector( ) 方法从IE8开始兼容,但从IE9开始支持CSS3的选择器,比如 :nth-child() 、:[src^='seven']等选择器形式支持良好。(伪类不行)

querySelectorAll( )

通过选择器得到元素数组。计师页面上只有一个符合选择器的节点,也将得到长度为1的数组

querySelector和querySelectorAll注意事项: 都不是实时的,例如节点是后期加上的会获取不到。

节点关系,节点之间的关系

关系 考虑所有节点 只考虑元素节点
子节点 childNodes children
父节点 parentNode parentElement
第一个子节点 firstChild firstElementChild
最后一个子节点 lastChild lastElementChild
前一个兄弟节点 previousSibling previousElementSibling
后一个兄弟节点 nextSibling nextElementSibling

封装节点关系函数

//兼容IE8以下的获取所有子元素节点函数,类似children属性
function getChildren(node){
    var children = [];
    for(var i=0; i<node.childNodes.length; i++){
        if(node.childNodes[i].nodeType == 1){
            children.push(node.childNodes[i]);
        }
    }
    return children;
}
//兼容IE8以下获取前一个兄弟元素节点
function getPreviousElementSibling(node){
    while(node.previousSibling != null){
        if (node.previousSibling.nodeType == 1) {
            return node.previousSibling;
        }
        node = node.previousSibling;
    }
}

//兼容IE8以下获取后一个兄弟元素节点
function getNextElementSibling(node){
    while(node.nextSibling != null){
        if (node.nextSibling.nodeType == 1) {
            return node.nextSibling;
        }
        node=node.nextSibling;
    }
}
//获取所有兄弟元素节点
function getAllElementSibling(node){
    var o =node;
    var preES = [];
    var nexES = [];
    while(o.previousSibling != null){
        if (o.previousSibling.nodeType == 1) {
            preES.unshift(o.previousSibling);
        }
        o = o.previousSibling;
    }
    o = node;
    while(o.nextSibling != null){
        if (o.nextSibling.nodeType == 1) {
            nexES.push(o.nextSibling);
        }
        o = o.nextSibling;
    }
    return preES.concat(nexES);
}

节点操作

改变元素节点中的内容可以使用两个相关属性:

改变元素节点的CSS样式

用 元素.style.要修改的属性 = ***;的形式,例如:

div1.style.backgroundColor = 'blue';
div1.style.backgroundImage = 'url(images/1.jpg)';
div1.style.fontSize = '32px';

注意:

  1. js语句不能用横杠-书写标识符,所以用小驼峰形式书写css属性名。
  2. css属性值要设置成完整形式。
  3. 注意写单位,注意写单位,注意写单位。
  4. 是通过CSS行内属性设置上的。
  5. 写入值必须是字符串,必须是字符串,必须是字符串。

脚本化CSS

以上操作称为脚本化CSS,读写行间样式,如果是js保留字,前面应该加css,例如:cssFloat
如何查询计算样式,可以使用:

window.getComputedStyle(元素名,null);
//第一个值传的是需要获取计算样式的元素,第二个值传入是否获取的是伪元素。
//IE8以下用ELE.currentStyle,或者封装计算样式方法
function getStyle(obj, name) {
    if(window.getComputedStyle) {
        //正常浏览器的方式,具有getComputedStyle()方法
        return getComputedStyle(obj, null)[name];
    } else {
        //IE8的方式,没有getComputedStyle()方法
        return obj.currentStyle[name];
    }
}

改变元素的css还可以用改变类名的写法

使用 DOMCUMENT.className += 需要样式的类型
或者使用dom的 classList 属性,classList是一个类数组,可以通过下标方式取得元素的class组,该属性有多种方法

方法名 功能
DOMCUMENT.classList.add(class1,class2) 在元素中添加一个或多个类名。如果指定的类名已存在,则不会添加
DOMCUMENT.classList.remove(class1, class2, ...) 移除元素中一个或多个类名。注意: 移除不存在的类名,不会报错。
DOMCUMENT.classList.toggle(class,布尔值) 在元素中切换类名。第一个参数为要在元素中移除的类名,并返回 false。 如果该类名不存在则会在元素中添加类名,并返回 true。 第二个是可选参数,是个布尔值用于设置元素是否强制添加或移除类,不管该类名是否存在。
DOMCUMENT.classList.item(index) 返回元素中索引值对应的类名。索引值从 0 开始。如果索引值在区间范围外则返回 null
DOMCUMENT.classList.contains(class) 返回布尔值,判断指定的类名是否存在。可能值:true - 元素包已经包含了该类名false - 元素中不存在该类名

改变元素节点的引用地址

比如图片的引用地址和a标签的跳转地址用 元素.src 和 元素.href

更改元素节点的HTML属性 setAttribute( ) 和 getAttribute( )

不符合W3C标准的属性,例如自定义属性名的属性,要使用 setAttribute( ) 和 getAttribute( ) 来设置,读取。格式是:

元素.setAttribute(属性名,属性值);
元素.getAttribute(属性名);           //返回属性名对应的值

例如:

div1.setAttribute('data-n',10);
div1.getAttribute('data-n');

setAttribute() 修改元素的类名用的是 class 而不是 className

removeAttribute() 删除元素节点的HTML属性

removeAttribute('属性名')

更改元素节点的自定义属性data

JavaScript的dataset属性。

元素节点.dataset.data后面的字符 = 'data-*的值';

如果后面的字符还是多个减号连接,那么data后面的字符用小驼峰式命名法。

节点的创建、插入、移动、移除、克隆

方法名 功能
document.createElement( ) 创建一个指定tag name的HTML元素
父节点.appendChild(孤儿节点) 将新创节点挂载成最后一个子节点
父节点.insertBefore(孤儿节点,标杆节点 ) 将新创节点挂载成标杆节点前一个子节点
父节点.removeChild( ) 从DOM父节点中删除一个子节点
子节点.remove( ) 从DOM中删除一个子节点
老节点.cloneNode( ) 克隆出一个新的孤儿节点
节点.replaceChild(新节点,旧节点) 替换节点

节点创建

document.createElement( ) 方法用于创建一个指定标签名的HTML元素,例如:

var div1 = document.createElement('div');

此时新创建的节点是“孤儿节点”,只生成在内存中,需要挂在到DOM树上才能看见他,也就是要“孤儿上树”,此时就需要使用孤儿上树的两个插入方法。

其他节点创建方法
方法名 作用
document.createTextNode( ) 创建一个文本节点
document.createComment( ) 创建一个注释节点
document.createDocumentFragment( )
创建一个虚拟的节点对象

节点插入

孤儿就是孤儿,要挂数只能找父亲,然后让一个野生父亲使用 appendChild( )insertBefore( ) 方法即可。

appendChild( )

任何已经在DOM树上的节点,都可以调用appendChild( ) 方法,它可以将孤儿节点挂载到它的内部,使孤儿节点成为最后一个子节点。

父节点.appendChild(孤儿节点);
insertBefore( )

任何已经在DOM树上的节点,都可以调用insertBefore( ) 方法,它可以将孤儿节点挂载到它的内部,成为它的“标杆子节点”之前的节点。

父节点.insertBefore(孤儿节点,标杆节点);

创建和插入练习:九九乘法表

<body>
    <table></table>
    
    <script type="text/javascript">
        var table = document.getElementsByTagName('table')[0];
        
        for (var i = 1;i<=9;i++) {
            var tbr = document.createElement('tr');
            table.appendChild(tbr);
            
            for (var j=1;j<=i;j++) {
                var trd = document.createElement('td');
                trd.innerText = i+'*'+j+'='+i*j;
                tbr.appendChild(trd);
            }
            
        }
    </script>
</body>
元素的移动

appendChild( ) 和 insertBefore( ) 还可以用作元素的移动,因为 一个节点不能同时位于DOM树的两个位置
如果将已经挂载到DOM树上的节点成为appendChild( ) 和insertBefore( ) 的参数,这个节点将会被移动,使用方法:

新父节点.appendChild(已经有父亲的节点);
新父节点.insertBefore(已经有父亲的节点,标杆子节点);

删除节点

父节点.removeChild(要删除的子节点);
子节点.remove(要删除的节点);

removeChild( ) 方法从DOM中删除一个子节点。
意味着节点不能主动删除自己,必须由父节点删除它。

克隆节点

var 孤儿节点 = 老节点.cloneNode( );
var 孤儿节点 = 老节点.cloneNode(true);

cloneNode( ) 方法可以克隆节点,克隆出的节点是孤儿节点。
参数可以传入一个布尔值,默认false,false意味着只克隆元素本身不管子节点。
如果是true则采用深度克隆,该节点的所有后代节点也都会被克隆。

替换节点

替换父节点中的子节点。用法:

父节点.replaceChild(新的子节点,旧的子节点)

此方法如果用在已经上树的节点上,会拉下来重新上树,还是那个道理,一个dom节点不能位于树上两个位置

事件

事件是实现交互的核心。用户与界面做出的交互动作都可以称为事件。例如“当用户点击元素时,当鼠标移动到元素上时,当文本框内容被改变时,当网页已加载完毕时。”

事件的三种模型

DOM0级:标签上直接绑定的onXXX事件和js用onXXX绑定的事件
IE:IE特有模型,解决了某三个DOM0级的问题。
DOM2事件模型:addEventListener方式添加事件,可以监听事件冒泡和捕获阶段阶段

常见事件

常见的鼠标事件

事件名 事件描述
click 鼠标单击
dblclick 鼠标双击
mousedown 鼠标按键按下
mouseup 鼠标按键松开
mousemove 鼠标按键移动
mouseenter、mouseover 鼠标进入
mouseleave、mouseout 鼠标离开
contextmenu 右击鼠标时触发并打开上下文菜单
mousewheel 鼠标滚轮

只有mouseup和mousedown区分鼠标键,button值0、1、2
如何解决mousedown和click冲突(例如a标签拖拽后会出发click事件):
利用时间戳和开关关闭click事件
mouseenter不冒泡,mouseover冒泡

常见的键盘事件

事件名 事件描述
keydown 键盘的键被按下(可响应所有按键,先于press执行)
keypress 键盘的键被按下,只能识别字符按键,例如方向键和功能键无法识别
keyup 当某个按键被松开

keypress返回的ASCII码(例charCode)可转字符

常见的表单事件

事件名 事件描述
input 当用户改变域的内容
change 当用户改变了域的内容后
focus 当某元素获得焦点(比如tab键跳转到或鼠标点击)
blur 当某元素失去焦点
submit 当表单被提交
reset 当表单被重置

常见的页面(窗体)事件监听

事件名 事件描述
load 当页面或图像完全加载
unload 当用户退出页面
scroll 滚动滚动条
resize 窗体大小改变时

事件监听

DOM允许我们书写JavaScript代码以让HTML元素对事件做出反应。
“监听”就是让计算机随时能够发现这个事件发生了,从而执行一些预先编写的语句。主要有两种方法。

  1. 元素.onXXX = function(){}
  2. 元素.addEventListener('',function(){},BOOLEAN);

两种监听区别

  1. onXXX只能监听冒泡阶段,addEventListener可以开启捕获阶段。
  2. onXXX只能绑定一个函数,后写的会覆盖。addEventListener可多次复写。

事件监听解除

  1. 元素.onXXX = null;
  2. 元素.removeEventListener('事件类型',函数,false);
  3. IE独有:元素.detachEvent('onXXX',函数);

事件传播

当元素形成嵌套结构时,事件就会发生传播,传播分为:

  1. 事件捕获:结构上的父子元素,开启捕获后,事件执行从父元素捕获到子元素。
  2. 事件冒泡:结构上的父子元素关系,时间会从子元素冒泡到父元素。

阻止事件传播: event.stopPropagation( ); 方法,或者ie678的event.cancelBubble=true;

事件对象

事件处理函数提供一个形式参数,他是一个对象,封装了本次事件的细节。这个形参通常用字母e或单词event表示:例如

div.onclick = function(e){ }
div.addEventListener('click',function(event){},false);

谁是实参呢,实参由浏览器提供,当事件触发时,浏览器会打包所有事件对象传入事件处理函数形参中。
实际上并不用形参实参的说法,实测有一个event对象就是事件对象。

鼠标位置属性

属性名 属性描述
offsetX、offsetY 鼠标指针相对于事件源元素的X轴、Y轴坐标
clientX、clientY 鼠标指针相对于浏览器视口的X轴、Y轴坐标
pageX、pageY 鼠标指针相对于整张网页的X轴、Y轴坐标
screenX、screenY 鼠标指针相对于电脑屏幕的X轴、Y轴坐标

键盘charCode属性和keyCode属性

e.charCode属性通常用于onkeypress事件中,表示用户输入的字符的ASCII码。
e.keyCode属性通常用于onkeydown事件和onkeyup中,表示用户按下的按键的键码。

charcode字符码
字符 字符码
数字0~数字9 48~57
大写字母A~Z 65~90
小写字母a~z 97~122
keycode键码
按键 键码
数字0~数字9 48~57
字母不分大小a~z 65~90
四个方向左上右下 37、38、39、40
回车键 13
空格键 32

元素offset系列属性

  1. 可以得到元素的偏移位置,返回不带单位的数值。这个数值以开启定位的父元素为准。
    ELE.offsetTop
    ELE.offsetLeft
  2. 可以得到元素的宽度和高度,包含padding和border,即元素盒子的大小。
    ELE.offsetWidth
    ELE.offsetHeight
  3. 返回带有定位的父亲,否则返回body

元素的client系列属性

  1. clientWidth 和 clientHeight 得到元素盒子除了border以外的尺寸大小。
  2. clientLeft 和 clientTop 不常用,相当于左边框和上边框的大小

元素的scroll系列属性

  1. scrollWidth 和 scrollHeight 得到元素盒子除了border外的所有内容的尺寸大小。
  2. scrollTop 和 scrollLeft 得到元素盒子被卷去的尺寸长度。该属性可用于“滑到底部才可以点击确定”

获取元素在页面上的属性

  1. 可以用window.getComputedStyle(元素名,null);
    //第一个值传的是需要获取计算样式的元素,第二个值传入是否获取的是伪元素
  2. 可以用 ELE.getBoundingClientRect(),例如ELE.getBoundingClientRect().left

事件的方法

preventDefault() 方法 阻止默认事件

e.preventDefault( ) 方法用来阻止事件产生的“默认动作”。
例如鼠标滚动的动作,表单提交,a标签跳转,右键菜单等。
一些特殊的业务需求,需要阻止事件的“默认动作”。
阻止默认事件还可以用return false;

stopPropagation( ) 方法 阻止事件传播

e.stopPropagation( ) 方法用来阻止事件继续传播
在一些场合,非常有必要切断事件继续传播,否则会造成页面特效显示出bug

事件委托

把事件绑定到父元素上,用 event.target 定位到子元素(利用了事件冒泡)
event.target 触发此事件的最早元素,即“事件源元素”
event.currentTarget 事件处理程序附加到的元素

事件委托的使用场景:

  1. 当有大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存开销。
  2. 当由动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听

注意事项:

  1. 不能委托不冒泡的事件给祖先元素
  2. 最内层元素不能再有额外的内层元素了

移动端事件

移动端使用 tansform:translate3d(); 会开启GPU加速,提高性能。

touch 事件:

touchstart、touchmove、touchend、touchcancel(比如操作时突然来电话)
touchstart 必须要在绑定事件的元素触发,触发start后,touchmove 和 touchend 手指移除元素依然可触发。

touch 事件的 TouchEvent 对象

这些对象都是一个 Touchlist,单指操作只需提取一个即可,比如 event.changedTouches[0]

其他触摸事件

touch 只是基础事件,还有其他例如单击,长按,双指等事件。
可以自行封装这些高级事件,也可使用例如 hammer.js 等别人封装好的事件库。

定时器和延时器

定时器

setInterval( ) 函数可以重复调用一个函数,在每次调用之间具有固定的时间间隔,写法:

setInterval(function(){},1000) 

第一个参数是函数,第二个参数是间隔时间,函数里的语句块将会在每过间隔时间后执行一次。
setInterval函数可以接受第3、4、5...个参数,它们将按顺序传入函数,例如。

setInterval(function(a,b){
    
},1000,1,2)
//第三个参数开始,表示传入函数内的实参

清除定时器

clearInterval( ) 函数,圆括号内传入要清除的定时器

延时器

setTimeout( ) 函数可以设置一个延时器,当指定时间到达后,会执行函数一次,不再重复执行,写法:

setTimeout(function(){

},1000)

清除延时器

clearTimeout( ) 函数可以清除延时器

初识异步语句

定时器和延时器是两个异步语句。

异步(asynchronous):不会阻塞CPU继续执行其他语句,当异步完成时,会执行“回调函数”(callback)

//例子:
setInterval(function(){
    console.log('a');
},1000)
console.log('b');
//会先输出b,再输出aaaaaa

异步语句不会阻塞程序的正常执行。
(这里记个记号,异步这部分是我学前端的第一个大坎,原因是我看的好多教程都没有讲解异步底层原理,直到去看了pink老师的讲解才理解了异步,因为这点导致我后面的promise和ajax学得一知半解的)

函数节流

延时器可以用作函数节流,即加个开关,定时打开或关闭开关,需足够时间才能执行下一次函数,具体方法:

var lock = true;
function 需要节流的函数名(){
    if(!lock) return;
    lock = false;
    setTimeout(function(){
        lock =true;
    },节流时间);
}

BOM

Browser Object Model 浏览器对象模型,是JS与浏览器窗口交互的接口。
一些与浏览器改变尺寸、滚动滚动条相关的,都要借助BOM技术。

window对象

window对象是当前js脚本运行所处的窗口,而这个窗口中包含DOM结构,window.document属性就是document对象。
在有标签页功能的浏览器中,每个标签拥有自己的window对象,不共用。

全局变量是window的属性

var a = 10;
//控制台输入window.a返回10

这意味着多个js文件之间是共享全局作用域的。

内置函数普遍是window的方法

如setInterva( ) 、alert( ) 等内置函数,普遍是window的方法

window属性

浏览器窗口尺寸相关属性

属性 意义
innerHeight 浏览器窗口的内容区高度,包含滚动条(如果有)
innerWidth 浏览器窗口的内容区高度,包含滚动条(如果有)
outerHeight 浏览器窗口的外部高度
outerWidth 浏览器窗口的外部宽度

浏览器的滚动条是有宽度的,如果想要获得不包含滚动条的窗口宽度,要用

document.documentElement.clientWidth

窗口改变事件resize见window事件

浏览器窗口滚动条相关属性

属性 意义
window.pageYOffset 获取当前滚动条Y轴已滚动像素值
window.pageXOffset 获取当前滚动条X轴已滚动像素值
window.scrollY
window.scrollX
document.documentElement.scrollTop
document.documentElement.scrollLeft

document.documentElement.scroll可读可写,可用作返回顶部。
滚动条相关事件scroll见window事件

浏览器窗口滚动条相关方法

方法名 作用
window.scoll( )
window.scollTo( )
window.scrollBy( )

传入(x,y)值滚动到输入的位置。
scroll和scrollTo一样,scrollBy是在现基础上累加

window方法

事件名 事件描述
load 当页面或图像完全加载
unload 当用户退出页面
scroll 滚动滚动条时
resize 窗体大小改变时

navigator对象

属性 意义
appName 浏览器官方名称
appVersion 浏览器版本
userAgent 浏览器的用户代理(含有内核信息和封装壳信息)
platform 用户操作系统

history对象

history.back();     //等同于浏览器回退按钮
history.go(-1);     //同上
history.go(1);      //前进一个页面
history,forward();      //同上

a标签实现回退功能

<a href="javascript:history.back();">回退</a>

location 对象

location.href 返回当前页面URL
location.reload(true) 传入true强制从服务器重新渲染页面
location.search 当前浏览器的get请求查询参数

面向对象

认识对象

Object 是键值对的集合。
写法:键和值之间用冒号分割,每组键值之间用逗号分隔。
如果对象的属性键名不符合js标识符命名规则,这个键名必须用引号包裹。

//方法一:
var 变量 = {
    键1 : 值1,
    键2 : 值2
}
//方法二:
var 变量 = new Object();

属性的访问

用 点语法 访问对象中指定的值,
如果属性名不符合js标识符命名规则,则必须用方括号里加引号里写属性名。
如果属性名以变量形式存储,也必须用方括号。

可用 hasOwnProperty( ) 方法判断属性是否是自己的,
或者使用 in 判断属性是否是自己和父级的。

属性的更改

直接用赋值运算符重新对属性赋值即可。

属性的创建

如果对象本身没有属性,直接用 点语法 写属性名,并用赋值运算符赋值即可,全局变量原理也是这样。

属性的删除

用delete操作符,例如:

delete 对象.属性;

对象的方法

如果某个属性值是函数,着它也被称为对象的方法。

方法的调用

使用点语法即可。

方法和函数

方法是对象的函数属性,需要用对象打点调用,函数也可以用window打点,所以二者在js里无差别。

对象的遍历

用 for(var 循环变量 in 对象){} 循环,例:

for(var k in obj){
    console.log(obj[k]);
}

循环变量可随意命名,一般为k或prop,循环变量会一次变为对象的每一个键。在for in 遍历里,一定要写成 对象[] ,的形式,因为循环变量是变量,中括号形式才能没问题循环所有形式的变量。

对象的深浅克隆

慕课网方法:

function deepClone(o){
    if (Array.isArray(o)) {
        var result = [];
        for (var i = 0;i < o.length;i++) {
            result.push(deepClone(o[i]));
        }
    }else if(typeof o == 'object'){
        var result = {};
        for (var k in o) {
            result[k] = deepClone(o[k]);
        }
    }else{
        var result = o;
    }
    return result;
}

渡一方法

function deepclone(origin,target){
    var target = target||{};
    for(var prop in origin){
        if(origin.hasOwnProperty(prop)){
            if(typeof(origin[prop]) == 'object'){
                if (Object.prototype.toString.call(origin[prop])=='[object object]') {
                    target[prop] = {};
                } else{
                    target[prop] = [];
                }
                deepclone(origin[prop],target[prop]);
            }else{
                target[prop] = origin[prop];
            }
        }
    }
    return target;
}

认识函数的上下文

函数中可以用 this 关键字,他表示函数的上下文。
this 指向必须通过调用函数时的“前言后语”来判断。
函数的上下文由调用方式决定,函数只有被调用,才会产生上下文。

上下文规则

  1. 对象打点调用他的方法函数,则函数的上下文是这个打点的对象。
  2. 圆括号直接调用函数,则函数上下文是 window 对象。
  3. 数组(或类数组对象)枚举出函数进行调用,则上下文是这个数组(或类数组对象)。
  4. 在IIFE函数中,上下文是window对象。
  5. 定时器、延时器调用函数,上下文是 window 对象。
  6. 事件处理函数的上下文是绑定事件的DOM元素,所以通常需要备份上下文。

call 和 apply

能指定函数的上下文,例:

函数.call(上下文)
函数.apply(上下文)

区别: 当函数需要参数时,call 要用逗号罗列参数; apply 需把参数写在数组中。

bind

bind 和 call 差不多,不过它只改变this指向,不会调用函数,执行完bind之后会返回一个已经修改好this指向的函数,可以用一个变量接收

function fn(){}
var ojj = {}
var bii = fn.bind(o)

用new调用函数

用new接函数调用,会从函数内部返回一个对象。
JS规定,使用new操作符调用函数会进行“四步走”用new调用函数的四步走:

  1. 函数体内会自动创建出一个空白对象。
  2. 函数的上下文(this)会指向这个空白对象。
  3. 函数体内的语句会执行。
  4. 函数会自动返回上下文对象,即使函数没有return语句。

上下文规则总结表格

情况 this指向
对象.函数() 对象
函数直接调用 window
数组[下标] () 数组
IIFE函数 window
定时器、延时器 window
DOM事件处理函数 绑定DOM元素
call和apply 任意指定
用new调用函数 秘密创建出的对象

构造函数

function 函数名(参数1,参数2){
    this.参数1同名 = 参数1;
    this.参数2同名 = 参数2;
}

然后用 new 调用函数执行:
构造函数开发者约定函数命名时首字母要大写,不用new调用不能正常工作。
构造函数用来“构造新对象”,它内部语句会为新对象添加属性和方法,完成对象初始化。

“类”与实例

JS中是没有类这个概念的,因为JS的对象与真正的面向对象语言有差别,叫“基于对象”。
构造函数可以看为是“类”,构造函数new出来的对象可以看作为“实例”。
判断这个实力是否是一个“类”构造出来的,可以用
A instanceof B

prototype 原型

任何函数都有prototype属性。
prototype属性是个对象,它默认拥有constructor属性指回函数。
普通函数的prototype属性没有任何用处,而构造函数的prototype非常有用,构造函数的prototype是它实例的原型。

原型链

[图片上传失败...(image-be2be0-1649463907747)]

hasOwnProperty 可以检查对象是否真正“自己拥有”某属性或方法。
in 可以检查对象是否能访问到这个属性或方法。
无法直接访问的意思是,构造函数自身无法直接调用或访问原型上的方法或属性,而实例可以,构造函数自身想访问原型上的方法必须先找原型再调用

在prototype上添加方法

将方法添加到构造函数上时,生成的每一个新对象就会多一个新占用内存的方法,会造成内存浪费。
解决方法就是将方法写道prototype上,只生成一个堆内存就够了。

重写constructor

prototype里面自带有一个constructor方法用于指回是哪个构造函数构造出来的,但通常一个构造函数的原型方法里需要塞下多个方法的时候我们通常需要用对象的形式,例如

function FFnn(){
    
}
FFnn.prototype = {
    say(){}
    sing(){}
}

通常会这样子通过对象的形式往里面添加多个方法,那么这个时候自带的construtor就没有了,所以这个时候如果你还想要有一个constructor方法指回构造函数,记得重新自己把constructor写上

FFnn.prototype = {
    constructor:FFnn
    say:function(){}
    sing:function(){}
}

继承

两个无关的构造函数,但一个构造函数需要的属性在另一个函数上也有,就可以利用原型链原理达成继承。
方法:使子函数的原型等于另一个函数的实例:

Fun2.prototype = new Fun1();

继承——圣杯模式

function inherit(Target,Origin){
    function F(){};
    F.prototype = Origin.prototype
    Target.prototype = new F();
    Targer.prototype.constructor = Targer;
    Targer.prototype.uber = Origin.prototype;
}

包装类

包装类的目的就是为了让基本类型值可以从他们的构造函数的prototype上获得方法。
包装类构造出来的值是一个对象,type of 返回 object ,可以参与运算。

Math 数学对象

除了之前介绍的那些 Math 方法以外,我们还有更多常用的Math方法。

四舍五入 Math.round()
实现四舍五入到小数点后某位:
数字 * 10的n次方 然后四舍五入 再 / 10的n次方。

得到参数列表最大最小值
Math.max() 得到参数列表最大值。
Math.min() 得到参数列表最小值。

如何求数组最大最小值:Math.max() 和 Math.min() 结合apply,例如:

var arr = [3,2,9];
var max = Math.max.apply(null,arr);
//ES6之后还可以
Math.max(...arr);

Date 日期对象

使用 new Date() 即可得到当前时间的日期对象,他是object类型值。如果什么参数都不加就是此时此刻的时间。
使用new Date(2021,6,12) 即可得到指定日期的日期对象,注意第二个参数是从0开始算的,6表示7月份。
也可以new Date('2021-7-12') 这样写,算时区。

常见日期方法

方法 功能
getDate() 得到日期1~31
getDay() 得到星期0~6
getMonth() 得到月份0~11
getFullYear() 得到年份
getHours() 得到小时数0~23
getMinutes() 得到分钟数0~59
getSeconds() 得到秒数0~59

时间戳

表示从1970年1月1日零点整距离某时刻的毫秒数。
可以通过 getTime() 方法或 Date.parse() 函数可将日期对象改为时间戳。
或者直接在date对象前面加个加号 + 即可,例如 var chuo = + new Date()
通过 new Date(时间戳) 可将时间戳改为日期对象

正则表达式

regular expression 描述了字符串的“构成模式”,经常被用于检查字符串是否符合预定的格式要求。
正则表达式“按位”描述规则,是指它是一位一位的描述子字符串的构成形式。

正则表达式的创建

使用 /内容/ 的语法形式,可以快速创建正则表达式
也可以使用 new RegExp('内容') 这种方法使用一些字符可能要用转义。
type of 检查正则表达式返回object。

元字符

元字符 功能
\d 匹配一个数字
\D 匹配一个非数字字符
\w 匹配一个单字字符
\W 匹配一个非单字字符
\s 匹配一个空白字符,包含空格制表符换行符
\S 匹配一个非空白字符
. 任意字符
^ 匹配开头
# 匹配结尾

量词

量词 意义
* 匹配前一个表达式0次或多次,等价于{0,}
+ 匹配前一个表达式1次或多次,等价于{1,}
? 匹配前一个表达式0次或1次
{n} n是一个正整数,匹配了前面一个字符刚好出现了n次
{n,} n是一个正整数,匹配前一个字符至少出现了n次
{n,m} n和m都是正整数,匹配前面的字符至少出现n次,最多m次

方括号表示法

  1. 使用方括号,比如[xyz]可以创建一个字符集合,表示匹配方括号中的任意字符,一个方括号代表一位。
  2. 可以用短横 - 来指定一个字符范围,^ 表示否定
    例如[0-9] [a-z] [A-Z] [0-9a-zA-Z_] [0-68-9]

修饰符

i 不区分大小写搜索
g 全局搜索

正则表达式的方法

test() 测试某字符串是否匹配正则表达式,返回布尔值
exec() 根据正则表达式,在字符串中进行查找,返回结果数组或者null。exec方法开启全局搜索后可逐条遍历(ES6迭代器概念)。

字符串的相关正则方法

方法 简介
search() 在字符串中根据正则表达式及逆行查找匹配,返回首次匹配到的位置索引,测试不到则返回 -1
match() 在在字符串中根据正则表达式进行查找匹配,返回一个数组,找不到则返回null
replace() 使用替换字符串替换掉匹配到的子字符串,可以使用正则表达式
split() 分割字符串为数组,可以使用正则表达式

本地存储

sessionStorage

sessionStorage 是一种浏览器本地存储数据的方式,只存储在本地,不发送到服务器端。当会话结束(例如关闭浏览器时),sessionStorage 中的数据会清空。基本用法:

其他属性:

localStorage

localStorage 时持久化的本地存储,除非手动清楚或者满了自动清除,否则数据永远不会过期。

ES6简介

ECMAScript 第六代标准

let 和 const

用于声明变量或声明常量

变量和常量
var 和 let 声明的就是变量,变量初始化后还可以重新赋值。
const 声明的是常量,常量一旦初始化后就不能重新赋值,否则报错。常量可以防止不经意间改变了不能改变的量。如果常量值的类型是引用类型,例如对象,可以修改对象属性的值。
const 声明常量一旦声明,就必须完成初始化,否则报错

let、const 与 var 的区别

  1. 重复声明:var 支持重复声明,let 与 const 不支持。
  2. 变量提升:let 与 const 不存在变量提升。
  3. 暂时性死区:只要作用域内存在 let 和 const ,他们声明的量就会绑定作用域,该量不再受外部影响。
  4. window对象的属性和方法:let 和 const 声明的量不会变成 window 对象的属性或方法。
  5. 块级作用域: let 与 const 存在块级作用域。

模板字符串

一般字符串:单引号和双引号。
模板字符串:反引号。

特征:

  1. 模板字符串中所有空格、换行、缩进都被保留在输出中。
  2. 同样可以使用转义字符输出反引号、反斜杠等特殊字符。
  3. 可以直接在模板字符串中使用${} 注入内容(变量,属性值,函数值,计算值等)

标签模板

是函数调用的一种特殊形式。模板字符串可以紧跟在一个函数后面,该函数将被调用来处理这个模板字符串,这被成为“标签模板”功能,例如:

alert('hello')
//等同于
alert`hello`

"标签"指的就是函数,紧跟在后面的模板字符串就是他的参数。
如果模板字符串中有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数。

let a = 5;
let b = 10;
function tag(tpldata,...values){
    console.log(tpldata); 
    console.log(values);
    console.log(arguments);
}
tag`hello ${a+b} world ${a*b}`
//等同于
tag(['hello',' world ',''],15,50)

“标签模板”的一个重要应用,就是过滤HTML字符串,防止用户注入代码


let message =
  SaferHTML`<p>${sender} has sent you a message.</p>`;

function SaferHTML(templateData) {
  let s = templateData[0];
  for (let i = 1; i < arguments.length; i++) {
    let arg = String(arguments[i]);

    // Escape special characters in the substitution.
    s += arg.replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");

    // Don't escape special characters in the template.
    s += templateData[i];
  }
  return s;}

箭头函数

写法: ()=>{} 小括号内参数,等号大于号,中括号内函数体。
通常需要用一个变量或常量接收。

注意事项

  1. 单个参数可省略小括号,0个或多个不可省略。
  2. 单行函数体可同时省略大括号和 return ,必须同时。
  3. 单行对象如果要省略大括号,需要在对象外加小括号:例如 const add = (x+y)=>({value:x+y})

箭头函数没有自己的this

非箭头函数执行一定会产生属于这个函数的this,箭头函数没有自己的this,只会从自己的作用域链的上一层继承 this.

箭头函数使用注意

  1. 不能作为构造函数。
  2. 需要 this 指向调用对象的时候箭头函数不适用。
  3. 需要 arguments 的时候,箭头函数没有 arguments 。可以用剩余参数替代。

Iterator 迭代器/遍历器

找到 iterator
先创建一个数组 const arr = [1,2] 用控制台查看,深挖其属性会有个 Symbol.iterator ,执行该方法 arr[Symbol.iterator]() 执行后会返回一个 Array Iterator{} 可以用一个量接收。

使用Iterator
iterator调用 next() 方法,会返回 {value: xxx , done: xxx}

可迭代对象

只要有 Symbol.iterator 方法,且这个方法调用后会产生一个 iterator 。
例如:数组、字符串、arguments、NodeList、Set、Map、剩余参数 等。
一般对象不是可迭代对象,可以给一般对象强行添加 Symbol.iterator 方法。

obj[Symbol.iterator] = ()=>{
    let index = ;
    return(
        next(){
        index++;
       ……
       }
    )
}
//类数组对象可以直接偷数组原型上的
LEISHUZU[Symbol.iterator] = Array[Symbol.iterator]

for of

想要遍历一些东西的时候,五花八门,对象用 for(...in...) ,数组用 for 循环、forEach 方法。而 iterator 只有一种遍历方式,就是“下一个”,一般用系统封装好的 for(...of...)

for(let item of arr){

}

可迭代对象的方法

forEach()

forEach() 方法可遍历 可迭代对象 。
forEach() 传入的第一个参数 是一个回调函数。回调函数可以使用三个参数,按顺序分别是 (1)可遍历对象键值对的值 (2)可遍历对线键值对的键 (3)可遍历对像本身。
forEach() 传入的第二个参数 是 this 指向(非箭头函数时才可用)。例如:

let arr = [11,22,33];
arr.forEach((value,key,self)=>{
    console.log(value,key,self)
},arr)

keys()

一些可迭代对象可以执行 keys() 方法,遍历此方法得到的对象,可以取得键名(例如数组的索引值,可迭代对象的键名)。例如:

let arr = [1,2,3]
for(let k of arr.keys()){
 console.log(k)
}

values()

可迭代对象的 values() 方法,遍历此方法返回的对象可以取得键值对的值(低版本浏览器不支持)

let arr = [1,2,3]
for(let k of arr.values()){
 console.log(k)
}

entries()

可迭代对象的 entries() 方法,遍历此方法返回的对象可以取得键值对(索引+值组成的数组)

let arr = [1,2,3]
for(let k of arr.entries()){
 console.log(k)
}

生成器函数

生成器函数,在旧函数声明方法 function fc(){} 中,在function关键字和函数名中间插入一个*号

function* (){}

普通函数执行时没有停止功能 Generator函数可以暂停

Generator 生成器 返回的是iterator接口

CO库就是不停的调用next方法将最后结果返回

Generator 中 yield 后面只能跟 Thunk 函数或 Promise 对象

function * gen(){
    console.log(arg);
    let one = yield 111;
    console.log(one)
}

let iterator = gen('AAA')
console.log(iterator.next());
console.log(iterator.next('BBB'))

next方法里面可以传入实参,传入的实参将会作为上一个yield整体返回的结果

展开运算符

... 可用于 可迭代对象 的展开。

例如,可以用于数组展开,把数组拆成一个一个数字。例如:Math.min 和Math.max 只能计算一串参数的最大最小值不能传入数组,这时就能传入数组同时用展开运算符展开。

let arr = [1,2,3];
//直接Math.min(arr)则不计算,可以
Math.min(...arr);

展开运算符可用于数组浅复制(浅克隆),例如

let arr1 = [1,2,3];
let arr2 = [...arr1]

所以展开 可迭代对象 外直接套一个中括号可以快速转变为一个数组。

展开运算符可以用于 对象 展开

但不能直接展开,只能在 {} 大括号中展开。
所以还可以用于对象复制,也可以用于合并对象,用于合并对象时,后展开的与前展开的对象同名的属性会覆盖。
展开空对象没有效果

let obj1 = {name:'mingzi',age:18}
let obj2 = {...obj1}

展开运算符只能“浅展开”

即只能展开一层

展开运算符特殊用法:剩余参数

当函数执行传入 比形参数量更多的 实参时,可以用剩余参数接受,写法 ...xxx ,本质是个数组,即使没有值,也是个控股数组,读取时不用 ... 直接读 xxx 即可。例如

function func(a,b,...sycs){console.log(sycs)}
func(1,2,3,4,5,6,7,8)
//返回数组[3,4,5,6,7,8]

箭头函数中的剩余参数

箭头函数中,因为箭头函数只有一个参数的时候可以省略小括号,但即使只有一个剩余参数,也不能省略小括号。
箭头函数没有arguments,所以可以用剩余参数替代,且剩余参数可以用数组的方法。

剩余参数必须、只能是最后一个参数,之后不能有其他参数,否则报错

剩余参数可以与解构赋值结合使用。
剩余参数与对象解构赋值结合使用会成为剩余元素。

解构赋值

可迭代对象全部都可以使用数组形式完成结构赋值,字符串还可以使用对象的结构完成解构赋值,例如: let {0:a} = 'bilibili' ,此时 a 为下标为 0 的字母b。

解构赋值的默认值

可以给解构赋值的常量变量赋一个默认值,例如 let[a=1,b=2]=[]
注意:只有右边与左边解构赋值严格等于 undefined 时,默认值才会生效。默认值拥有惰性求值的特性,即用得到才执行求值式求值,用不到则不执行。

解构赋值的应用用途

解构赋值可以用于快捷交换变量值,例如

let x=1,y=2;
[x,y]=[y,x]
//此时x和y就快速交换了值。

Set

Set是一些列无序、没有重复值的数据集合。

无序: Set没有下标去标识每一个值,所以set是无序的,不能像数组一样通过下标去访问 set 的成员,但遍历 set 的时候,是根据成员添加进 set 的顺序进行遍历的。
没有重复值: Set会自动去重,Set重复值的判断遵循全等原则,但NaN除外,在Set中NaN也不可重复。

创建一个 Set

使用 Set 构造函数 const se = new Set() 即可创建一个set,创建 Set 时可以传入一个可迭代对象作为参数,构造出来的Set就会拥有对象里的成员,例如:

let arr = [1,2,3]
const se = new Set(arr)

Set 的属性

size:可以访问Set里面的成员个数。new Set().size 会返回一个0.

Set 的常用方法

方法 作用
add() 添加成员,可连续使用
has() 查看是否有某个成员,返回布尔值
delete() 删除某个成员
clear() 清除所有成员
forEach() 遍历

add

可以连续添加,例如:

const se = new Set()
se.add(1).add(2).add(3)

delete

如果删除不存在的成员,则什么都不会发生,也不会报错

forEach

Set 的 forEach 是按照成员添加进Set的顺序遍历的

Set 用途

Set 可以用于 可迭代对象 去重,例如数组去重,字符串去重。或者需要使用Set方法的地方也可以转换成Set

Map

Map 和对象都是键值对的集合。
Map 和对象的区别:
对象一般用字符串当作键,Map 可以用所有数据类型的值当键,包括引用类型。

创建一个 Map

使用 Map 构造函数 const ma = new Map() 即可创建一个 Map ,创建 Map 时可以传入一个 entry object (例如二维数组,二维set对象,map对象),例如:

let arr2d = [
    [1,2],
    [3,4],
    [5,6]
]
const ma = new Map(arr2d);

Map 属性

size:获取有几个键值对,上面的代码然后 ma.size 会返回3.

Map 的方法

方法 作用
set() 添加新成员
get() 获取成员
has() 判断是否有这个成员返回布尔值
delete() 删除成员,如果没有则无事发生也不报错
clear() 清除所有成员
forEach() 遍历成员

set

可连续使用,例如 new Map().set(9,8).set(7,6) ,当后添加的成员与前添加的键名一样时,后添加的会覆盖前添加的。

get

获取成员,当获取不存在的成员时返回 undefined

Map 其他注意事项

Map 重复键名的判断方式遵循全等原则,NaN例外。

Map 用途

当你只需要键值对结构时,或者需要Map的方法时,或者使用字符串以外的值做键时。

ES6其他新增技巧

对象字面量增强

let name = 'mingzi';
let obj = {name};   //等同于let obj = {name:name}
obj.name    //直接返回 'mingzi'
//const obj = { func : function(){} }
const obj = { func(){} }
let bianliang = 'x'
const obj = {
    [bianliang] : 18;
}
obj.x   //返回18

函数参数的默认值

可以给函数的参数设置一个默认值,例如:

function xjiay(x=1,y=2){
    return x+y
}
xjiay()
//直接执行不传参数则使用默认值

ES6 其他新增方法

ES6字符串新增方法

includes() 方法

用于判断字符串是否有某些字符。
用法:第一个参数传入的是一个值,返回布尔值判断字符串内是否有这个值。第二个参数表示开始搜索的位置。

startsWith() 和 endsWith

用于判断参数是否在字符串的头部和尾部,返回布尔值。
例如 abcd.startsWith('ab') 返回true,abcd.endsWith('cd')返回true

repeat()

repeat() 方法返回一个新的字符串,表示将源字符串重复n次。例如:

'rero'.repeat(4);
//输出rerorerorerorero

参数如果不是整数会自动向下取整,为0次则不输出内容,为负数则报错

padStart() 和 padEnd()

pad是填充的意思,用于补全字符串长度,无法改变原字符。
用法:

  1. 第一个参数表示“需要补到的长度”是多少,通常是一个整数。
  2. 第二个参数表示“补入的值”,如果不传参数2,默认使用空格代替。

padStart() 是从头部开始补, padEnd() 是从尾部开始补。例如:

'x'.padStart(5,'ab')    //返回 'ababx'
'y'.padEnd(5,'6688')    //返回 'y6688'

trimStart() 和 trimEnd()

trim是去掉不必要部分的意思,用于清除字符串的首或尾的空格,中间的空格不会清除。
trimStart() 等同于 trimLeft() ,trimEnd() 等同于 trimRight()。
同时去除头尾空格的方法为 trim()。

matchAll()

matchAll() 方法返回一个正则表达式在当前字符串的所有匹配
返回的是⼀个遍历器(Iterator),⽽不是数组。

ES6数组新增方法

includes()

数组的 includes() 方法,可以判断数组中是否含有某个成员,判断基于全等原则,NaN除外。第一个参数传入的是一个值,返回布尔值判断数组内是否有这个值。第二个参数表示开始搜索的位置。

Array.from()

Array.from() 可以将 可迭代对象 和 类数组对象 转换成数组。

Array.of()

Array.of()方法用于将一组值转化为数组,即新建数组,而不考虑参数的数量或类型。

//使⽤Array.of()创建数组 
console.log(Array.of()); //[] 创建⼀个空数组
console.log(Array.of(8)); //[8] 创建只有⼀个元素值为8的数组 console.log(Array.of(1, 2, 3)); //[1,2,3] 创建⼀个值为1,2,3的数组 

//以前直接使⽤Array创建数组 
console.log(Array()); //[] 创建⼀个空数组 
console.log(Array(4)); // [ , , , ] 创建拥有4个元素空值的数组 
console.log(Array(1, 2, 3)); //[1,2,3] 创建⼀个值为1,2,3的数组

find() 和 findIndex()

find() 方法找到满足条件的第一个立即返回这个值。
findIndex() 方法找到满足条件的第一个立即返回其索引。

some() 和 every()

ARR.every() 和 ARR.every() 目的:确定数组的所有成员是否满足指定的测试

const data =[ 
    {name:'zhangsan',age:22,sex:'man'}, 
    {name:'lisi',age:25,sex:'woman'},
    {name:'wangwu',age:23,sex:'man'}, 
]; 
    
//使⽤some判断data中是否含有⼀条name以"wang"开头的 
let s1 = data.some(v => v['name'].startsWith("wang")); 
console.log(s1); //true

//使⽤every判断data信息中是否都是age⼤于20的信息。 
let s2 = data.every(v => v['age']>20); 
console.log(s2); //true 
若有⼀个不符合则返回false

fill()

ARR.fill() 函数,使用指定的元素替换原数组内容,会改变原来的数组。
参数一:要改变成的内容;参数二:要改变的开始位置;参数三:要改变的结束位置。

//空数组则没有替换 
console.log([].fill(6)); //[] 
//将数组中的值都替换成指定值6 
console.log([1,2,3].fill(6));//(3) [6, 6, 6] 
//从数组索引位置2开始替换成指定值6,替换到数组结束位置。 
console.log([1,2,3,4,5].fill(6,2)); //(5) [1, 2, 6, 6, 6]
//从数组索引位置2开始替换到索引位置4前结束。 
console.log([1,2,3,4,5].fill(6,2,4)); //(5) [1, 2, 6, 6, 5]

ES6对象新增方法

Object.assign()

Object.assign() 作用是合并对象
传入的参数为多个对象。本质是将后面参数的对象合并到第一个参数中,会改变第一个参数自身。如果不想改变任何参数,可以在第一个参数写一个空对象,例如: Object.assign({},obj1,obj2)

注意事项:基本数据类型作为源对象时,会先转换成对象,再合并。合并时如果出现同名属性,后面的会把前面的覆盖。

Object.keys() 、Object.values() 、 Object.entries()

这三个方法都是Object构造函数上的, 传入的参数是一个对象,执行后分别返回键名组成的数组、值组成的数组、键值对组成的数组。
为什么返回的不是 可迭代对象 ?因为 keys 是ES5的方法,ES5 中没有 Iterator 的概念,所以为了统一,Object 的这三个方法返回的都是数组。

Object.fromEntries()

是 Object.entries() 的逆操作,将可迭代对象作参数传入Object.fromEntries()中,会返回一个对象。

Object.is()

Object.is('foo', 'foo') // true 
Object.is({}, {}) //false
+0 === -0 //true 
NaN === NaN // false 
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

Object.getOwnPropertyDescriptors()

ES5 的Object.getOwnPropertyDescriptor()⽅法会返回某个对象属性的描述对象 (descriptor)。 ES2017 引⼊了Object.getOwnPropertyDescriptors()⽅法,返回指定对象所有⾃身属性(⾮继承 属性)的描述对象

const obj = { foo: 123, get bar() { return 'abc' } }; 
Object.getOwnPropertyDescriptors(obj);
// { foo: 
// { value: 123, 
// writable: true, 
// enumerable: true, 
// configurable: true }, 
// bar: 
// { get: [Function: get bar], 
// set: undefined, 
// enumerable: true, 
// configurable: true } }

proto属性,Object.setPrototypeOf()和Object.getPrototypeOf()

JavaScript 语⾔的对象继承是通过原型链实现的。ES6 提供了更多原型对象的操作⽅法。 具体详⻅⽂档

Object.defineProperty

参数一:给谁添加属性
参数二:添加属性的属性名
参数三:配置项,是一个对象,其中包括value。
此方法定义的属性默认是不可枚举/遍历的,可在配置项里修改enumerable:true开启遍历。
定于的属性默认是不可以被修改的,可在配置项修改 writable:true 开启修改
定义的属性默认是不可以被删除的,可在配置项修改 configurable:true 开启删除

类与对象

class (类)

class 类名{   }

注意没有圆括号和分号。

const 类名 =class{    }
//加new因为constructor必须要有new才能执行。
(new class {
    constructor(){
        执行语句;
    }
})

constructor

class里的构造方法 constructor 用法,如下:

class 类名{
    constructor(参数1,参数2...参数n){
        this.属性名1 = 参数1;
        this.属性名2 = 参数2
    }
}
class 类名{
    constructor(参数1,参数2...参数n){
        this.属性名1 = 参数1;
        this.属性名2 = 参数2
    }
    方法1 (){     }
    方法2 (){     }
}

类的方法(静态方法)

使用关键字 static ,写在声明里,是只有类可以访问的,实例无法访问,例如:

//static 静态方法名(){}
class Lei{
    static func(){
        console.log('静态方法')
    }
}
Lei.func()

类的属性(静态属性)

有三种方法:

  1. 类名.属性名 = 值
  2. 声明里写 static 静态方法(){return 值;}
  3. 声明里直接写 static 静态属性 = 值
    方法3存在兼容性问题,低版本浏览器不支持。

实例对象

如何创建一个实例:

new 类名();

即可生成一个实例对象,这个实例对象会拥有 class 里面的 constructor 的属性。例如

class Person{
    constructor(a,b){
        this.name = a;
        this.age = b;
    }
}
const student = new Person('ming',24);

这个 student 就会拥有 name 和 age 属性,属性值是什么取决于你写的是定值还是参数。
也可以给属性值添加默认值(截至2021年7月默认值已失效)

JS 类与对象 的私有属性和方法

想要实现私有,可以使用闭包实现,或者利用模块化。

class 继承

使用关键字 extends 和关键字 super ,例如:

class Person{
    constructor(a,b){
        this.name = a;
        this.age = b;
    }
    func(){console.log(1)}
}

class Student extends Person{

}

此时 Student 这个类就会拥有 Person 的所有构造方法和共享方法。

子类如果想要拥有自己的构造方法,则必须配合 super 关键字使用,否则会报错;
super 前面不能放任何this操作,否则报错。例如

class Person{
    constructor(a,b){
        this.name = a;
        this.age = b;
    }
    func(){console.log(this.name)}
}
class Student extends Person{
    constructor(x,y,c){
        super(x,y)      
        //这里super接收的参数会扔到父类的constructor中执行,this指向子类实例
        this.newProp = c
    }
}

子类想要使用父类的方法,子类调用的父类方法里的this指向的是父类,所以要在之类的构造方法里使用super

class 中的 super 关键字

  1. 在构造方法和一般方法中使用,super 代表的都是父类的原型 SUPERCLASS.prototype
  2. 在静态方法 static FUNCTION(){} 中使用,代表的是父类自身,this 指向子类。

class中的get和set

当实例调用读取父类方法时会触发get方法,get方法里面返回的值就是触发后返回的值
当实例修改父类方法时会触发set方法,set方法必须带有一个形参

class Phone{
    get price(){
        console.log('价格属性被读取了')
        return 'iloveyou'
    }
    set price(newVal){
        console.log('价格被修改了')
    }
}

let s = new Phone()
console.log(s.price);   //这里会触发get price并得到里面的返回值
s.price = 'free';   //这里会触发set

Module 模块系统

模块系统解决的主要问题:

  1. 模块化的问题。
  2. 消除全局变量。
  3. 管理加载顺序。

import、export、export default 和 script type='module'

export default

export default 可以直接接模块里的定变量名,类或类名,方法或方法名等。

export

export 导出可以有两种形式。

  1. 接声明或语句,例如 export let a = 'cosin'
  2. 接大括号,例如 export {导出1,导出2}
    export 的 import 只能用大括号接收,例如 import{导出1,导出2}

如果导出或导入的东西想改名称,可以使用 as 关键字,例如:

export {name as newName}    //此时import 接收的是 newName
import{newName as newnew}

import

import三种形式导入

  1. 通用形式导入,用as后接一个名字当作接受的对象
import * as m1 from './js/m1.js'
console.log(m1.name)
m1.fn()
  1. 解构赋值形式导入
import {name,fn} from './js/m1.js'
import {name as mingzi,fn as fangfa} from './js/m2/js'
import {default as mod} from './js/m3.js'
  1. 简便形式 仅针对 export deafault 默认暴露
import m3 from './js/m3.js'

同时导入

因为 export 和 export default 的导入方式不同,所以想一次导入这两种导出,可以使用:

  1. import default导出的,{非default导出} from '路径'
  2. 采用整体导入,使用通配符 * 星号然后接 as 然后接一个名字,就代表导入所有导出的,包括 export 和 export default 的,例如:
import * as obj from './module.js'

导入的内如是一个 Module 对象,导出的东西都会变成这个 Module 对象的属性,default 的内容会变成 default 属性

Module 注意事项

  1. 导入的变量是只读的,导入的对象的属性值可以修改。
  2. 一个模块可以直接写export,即导入后直接导出,此时这个模块不可以使用导入的任何东西,相当于一个中转站。
  3. import 和 export 命令具有提升效果,会提升到整个模块的头部并率先执行。所以 import 和 export 别写在代码块中,想要按条件导入,可以使用 import() 函数。
  4. 模块顶层的 this 指向 undefined ,可以利用这点提示使用模块加载,例如:
if(type of this !== 'undefined'){
    throw new Error('请使用模块加载')
}

ES6 数值拓展

Number.EPSILON

Number.EPSILON是 Javascript表示的最小进度,可以用来做浮点运算判断 、

fuction equal(a,b){
    if(Math.abs(a-b)<Number.EPSILON){
        return true;
    }else{
        return false;
    }
}
console.log(equal(0.1+0.2,0.3))

Number.isFinite 检测一个数值是否为有限数

console.log(Number.isFinite(100))

Number.isNaN

isNaN旧方法扔进Number中

Math.trunc()

将数字(参数)的小数部分抹掉

Math.sign()

判断参数是正数、负数、还是0

直接书写N进制数字

let b = 0b1010
let o = 0o777

ES7 新特性

includes

可以检测数组是否拥有该组员

两个星号(乘号)

两个星号后面接的数字代表几次方,2 ** 10 类似于 Math.pow(2,10)

ES8

async 函数

在函数前面加上 async 关键字

async function fn(){

}

async函数返回结果是一个promise,返回的promise状态由内容决定

  1. 如果返回的东西是一个Promise对象,则由该promise决定成功还是失败
  2. 如果返回结果是抛出一个错误,则状态为失败reject,例如 throw new Error('err')
  3. 其他返回结果大多为成功resolve/fulfilled

await

  1. await 要放在 async 函数中
  2. await 右侧的表达式一般为 promise 对象
  3. await 返回的是 promise 成功的值
  4. await 的 promise 失败了,就会抛出异常,需要使用 try{}catch{} 捕获

ES9

正则捕获分组

?<>

方向断言

?<=

dotAll 拓展

结尾加上s

ES10

数组拓展 flat

将多维数组转化为低维数组
ARR.flat()
参数是数字,意义为展开深度为几层,例如三维转一维,深度就是3-1=2

数组拓展 flatMap

如果数组使用map之后返回一个多元数组,flatMap则是把多元数组变回一维数组,
ARR.flatMap() 就是数组的map方法和flat方法结合

Symbol.prototype.description

let sb = Symbol('HQM')
sb.description

ES11

对象的私有属性

在属性前面加上井号

class Person{
    name;
    #age;
    #weight;
    constructor(name,age,weght){
        this.name = name;
        this.#age = age;
        this.#weight = weight;
    }
}

const girl = new Person('hong',18,'45kg')
console.log(girl.#age)  //此时会报错,私有属性不能在外部直接访问,可以在类定义一个内部方法通过内部方法访问

Promise.allSettled

可选链操作符

?.
当一个函数的参数 是一个对象的时候,如果要找传入的对象是否有这个值,一般需要层层判断,有了可选链操作符,直接使用 ?. 即可,不会报错

const fn = (config) => {
            console.log(config.db.a1);
        }
fn({
    db: {
        a1: '111',
        a2: '222'
    }
})
//如果有这个参数则不会报错
const fn = (config) => {
            console.log(config.db.a1);
        }
fn()
//如果没有这个参数则报错,只能使用 config&&config.db&&config.db.a1

有了可选链操作符,只需要这样即可

console.log(config?.db?.a1)

动态import

主要用来提升性能,动态import就是不在文件头部使用import导入模块,而是使用import()这个函数在使用的时候才导入,函数的参数就是导入这个模块的路径。
导入执行之后返回的是一个Promise,用then后的回调函数的参数接收此模块

BigInt() 大整数

在整数后面加一个字母n,就是大整型数据类型

let bi = 521n
console.log(bi,typeof bi)

使用 BigInt() 方法,往里面穿一个整型数字,就会转变为大整型数字

let norm = 123
console.log(BigInt(norm))

运用场景是大数值运算,BigInt不能和普通Int做运算

let max = Number.MAX_SAFE_INTEGER
console.log(max)
console.log(max+1)
console.log(max+2)
//此时就会计算丢失精度

console.log(BigInt(max))
console.log(BigInt(max)+BigInt(1))
console.log(BigInt(max)+BigInt(2))
//使用BigInt之后就没有问题

绝对全局对象 globalThis

上一篇下一篇

猜你喜欢

热点阅读