JavaScript是一种运行于JavaScript解释器/引擎中的解释型脚本语言。
JavaScript解释器作为JS脚本的运行环境,有如下两种呈现方式。
JS和Java的区别
编译型语言(JAVA,PHP),运行起来更快,解释性语言需要兼容不同的环境
JavaScript当前的应用领域十分广泛,例如
1992年,Nombas公司为自己cEnvi软件开发了一款脚本语言ScriptEase,可以嵌入在网页中。
1995年,Netscape公司为自己的Navigator2.0浏览器开发了另一种客户端脚本语言Livescript,为了赶时髦重命名为JavaScript,但其实和Java没任何关系。
1996年,Microsoft公司为了进军浏览器市场,在IE3.0浏览器发布了一个Javascript的克隆版,称为JSscript。
1997年,Javascript1.1作为草案提交给了ECMA,各个厂家合力推出了ECMA-262标准,定义了全新的ECMAScript标准脚本语言,各大浏览器厂家开始努力将ECMAScript作为实现的标注和目标。
2009年,Commonjs的提出,正式确立了JS向服务器端和桌面端应用发展的方向。
CommonJS规范在传统的基于浏览器的JS API基础上,指定了更加丰富的API规范,提供了类似于Java,Python,Ruby等语言一样强大的编程能力,并实现跨硬件平台,跨操作系统,跨解释器的应用体验。
目前实现了CommonJS规范的技术有,NodeJS,RequireJS,SealJS等等。
代码可使用任何文本编辑工具编写,语法类似于C和Java
脚本文件无需编译,由js引擎解释执行。
弱类型语言
基于对象
跨平台性
使用独立的JavaScript解释器
使用浏览器中内嵌的JavaScript解释器
在代数中,我们使用字母(比如 x)来保存值(比如 2)
x=2
y=2
sum=x+y
通过上面三行语句,能够计算出sum的值为5
在JavaScript中,这些字母被称为变量
var userName
var bookPrice=25.5
var publicData
, console.log(publicData)
break
case
catch
continue
default
delete
do
if
function
with
throw
undefined
等等class
int
float
等等注意
不允许重新赋值,不允许不赋值
const pi=3.14
//pi=3.1415
console.log(pi)
ECMAScript数据类型
原始类型(不同的JS运行环境支持的原始类型都是一样的)
引用类型(不同的JS运行环境提供的应用类型区别很大)
引用型数据和原始数据的区别与释放
person1=null
数字类型
整数
浮点数
var num1=13; //13
var num2=010; //8
var num3=0xa; //10
var num4=3.14; //3.14
var num5=3.14e3; //3140
var num6=3.14E-3; //0.00314
console.log(num6,typeof num6) //显示数值类型
字符串类型
所有变量值加引号都变成了字符串类型
charCodeAt()
查字符的unicode码 "一"最小
布尔类型
var b1=true
var b2=3>4
console.log(b1,typeof b1)
var person=null
console.log(person,typeof person);//null Object
JavaScript 属于弱类型程序语言
不同类型数据在计算过程中会自动进行切换
var n1=2+'hello';
console.log(n1,typeof n1);
//2hello string 加号表示字符串拼接
var n2='tedu'+true;
console.log(n2,typeof n2);
//tedutrue string 加号表示数字的运算
var n3=2+true;
console.log(n3,typeof n3);
//3 number
var n4=false+true;
console.log(n4,typeof n4);
//1 number
var m1=5-'3';
console.log(m1,typeof m1);
//2 number
var m2='8'-'4';
console.log(m2,typeof m2);
//4 number
var m3=3*true;
console.log(m3,typeof m3);
//3 number
var m4='9'/3;
console.log(m4,typeof m4);
//3 number
var m6=1+undefined;
console.log(m6,typeof m6);
//NaN number
var m7='1a'*3;
console.log(m7,typeof m7);
//NaN number
//总结:
//加减乘除里,‘2‘3’只有加法的隐式转换字符串会有拼接,当出现加号外的其他运算符都自动转为数值.
//NaN not a number 不是一个数字
//NaN和任何值运算,都返回NaN,不包括和字符串的相加
使用数据类型转换函数(全局函数)
表达式: 可以用于计算,可能会产生一个值的式子
+ - * / % ++ --
> <>= <= == === != !==
&& || !
& | ~ ^ << >> >>>
= += -= *= /= %=
+
?:
typeof instanceof void delete
加(+)、减(-)、乘(*)、除(/)、求余(%)
-
可以表示减号,也可以表示负号,如:x=-y+
可以表示加法,也可以用于字符串的连接 var num1=20-5 //15
var num2='20'-'5' //15
var num3='20a'-5 //NaN
var num4='20'-'5a' //NaN
使用%运算符,表示求余运算,或者称为取模运算
var i = 10 % 3 //i值为1
var i = 10.5 % 3 //i值为1.5
var i = -10 % 3 //i值为-1
var i = 10 % -3 //i值为1
var i = -10 % -3 //i值为-1
console.log(5%3) //2
console.log(3%5) //3 除不尽返回3
//案列:隔行变色
console.log(1%2) //1
console.log(2%2) //0
使用 ++
和 --
运算符
案例1
i ++ ; //相当于i=i+1;
i -- ; //相当于i=i-1;
i = 1;
j = i++ // i结果是2,j的结果是1 (赋值以后才自增)
i = 1;
j = ++i // i结果是2,j的结果是2 (自增以后才赋值)
案例2
var a=“1”;
a++;
console.log(a,typeof a); //2 number发生了隐式转换number
var i=5;
//先让i赋值给j,然后再执行自增
var j=i++;
console.log(j);//5
console.log(i);//6
var i=5;
//先让i执行自增,然后再赋值
varj=++i;
console.log(j);//6
console.log(i);//6
案例3
// 判断输出结果
var n=2
var r=n+++++n //2+4
console.log(r) //6
console.log(n) //4
关系运算用于判断数据之间的大小关系
>
大于<
小于>=
大于等于<=
小于等于==
等于!=
不等于表达式的值为boolean类型(true 或 false)
console.log(3>10) //false
console.log('3'>'10') //true
console.log('3'>10) //false
console.log('3a'>10) //false
console.log('3a'==10) //false
console.log('3a'《10) //false
// 隐式转换默认调用number,NaN
// NaN和任何数比较或运算结果都是NaN,除了加号拼接
全等运算符,不会发生’隐式类型转换’,只要两个数据类型或数值之一不相等,即判断为不相等
===
!==
var a1="10"
var a2=10
console.log(a1==a2) //true
console.log(a1===a2) //false
console.log(a1!=a2) //false
console.log(a1!==a2) //true
逻辑非(!)、逻辑与(&&)、逻辑或(||)
var age=11;
console.log( age>=60 || age<=12 ) //true
//工资 5000~7000
varsalary=8000;
console.log( salary>=3000 && salary<=8000 );//true
console.log(!true ); //false
案列1://声明变量保存用户名,密码,判断用户名是否为root,密码是否为123456,同时满足打印true,否则打印false。--à 常用于登录
var uname='root';
var upwd='888888';
console.log(uname==='root' && upwd==='123456' );
案列2://声明变量保存用户输入的值,如果值是root,或者是13112345678,或者是[email protected],如果满足一项打印true,否则打印false
var input='root';
console.log(input==='root' || input==='13112345678' || input==='[email protected]' );
关于"短路逻辑"的问题
false && ? => false
true || ? => true
案列3
var num=3;
num>5 && console.log(a); //不执行不报错
num<1|| console.log(b); //报错 b is not defined
案列4
var i=10;
var j=10;
var k=i-->0 || j++>0;
console.log(i,j,k);//9 10 true
案列5
使用短路逻辑
-声明变量保存年龄,如果满18岁打印成年人
var age=17;
age>=18&& console.log('成年人'); //成年人
位运算,将数字以二进制形式进行运算,10进制转换为2禁止,再转换为10进制
按位与(&)
上下两位都是1,结果是1,否则是0按位或(|)
上下两位含有1,结果是1,否则是0按位异或(^)
上下两不同为1,相同为0按位右移(>>)
删除二进制的最后一位或者多位,每次缩小到原来的一半或者更多按位左左移(<<)
在二进制的最后添加为0,成倍增加 //按位与 & 1 1 1
console.log(5&7);
/*
101 5
111 7
--------------
101 5
*/
console.log(5&12);
/*
0101 5
1100 12
---------
0100 4
*/
//按位或 | 1 1 1/1 0 1
console.log(5|12);
/*
0101ᅠᅠᅠ5
1100 12
-------
1101 13
*/
console.log(9|17);
/*
01001
10001
---------
11001 25
*/
//按位异或 ^ 不同为1 相同为0
console.log(5^12);
/*
0101
1100
1001 9
*/
//按位右移 移动几位 末尾删几个
console.log(49>>1); //往右移动一位 24
console.log(49>>2); //往右移动二位 12
console.log(49>>3); //往右移动三位 6
console.log(49>>6); //……………. 0
/*
32+16+1
100000+10000+1
110001
11000 24
1100 12
*/*/
//按位左移 移动几位末尾添加几个0
console.log(3<<1); //6
console.log(3<<2); //12
console.log(3<<3);//24
/*
11
110 6
1100 12
11000 24
*/
&&的优先级大于||
year%4===0&& year%100!==0 || year%400===0&& console.log('闰年');//无显示
(year%4===0 && year%100!==0 || year%400===0)&& console.log('闰年');
=
进行赋值运算Var score=90;
Var isFullMarks=score==100;
Var x=y=z=10; //这种写法不推荐使用
扩展赋值表单式
var a=10
a+=3 //相当于 a=a+3
a-=3 //相当于 a=a-3
a*=3 //相当于 a=a*3
a/=3 //相当于 a=a/3
a%=3 //相当于 a=a%3
在绝大多数变成语言种,a+=3由于运行时可以进行优化,执行效率都要优于a=a+3
案例
// 赋值运算符的优先级小于&&和||
var price=120
price>=100 && (price*=0.8)
console.log(price) //96
?:
需要对三个表达式进行运算 表达式1?表达式2:表达式3
表达式1的值应该是boolean类型,表达的含义
案例
var age = 20
var msg = age >= 18 ? "成年人":"未成年人"
var a = 10
var b = 20
var max =a>b ? a : b
顺序
,分支
,循环
三种基本的程序逻辑组合实现当条件满足时运行某些语句,当条件不满足时则不运行这些结构----if结构
语句0
if(逻辑表达式){
语句1
语句2
}
语句3
// 执行语句0
// 判断逻辑表达式的值
// 若值为true,则执行if语句块中的语句
// 若值为false,则不执行if语句块中的语句
// 执行语句3
//满30减10
var total=31;
if(total>=30){
//在原来上减10
total-=10;
};
console.log(total);
//声明变量保存一个人的年龄,如果满18岁打印成年人
var age=22;
if(age>=18){
console.log('满十八岁');
}
当条件满足时运行某些语句,当条件不满足时运行另外的一些语句----if-else结构
语句0
if(逻辑表达式){
语句1
}else{
语句2
}
语句3
// 执行语句0
// 判断逻辑表达式的值
// 若值为true,则执行if语句块中的语句1
// 若值为false,则执行if语句块中的语句2
// 执行语句3
// 判断一个人是否为成年人
var age=13;
if(age>=18){
console.log('成年人');
}else{
console.log('未成年人');
}
console.log('判断结束'); true和false最后都会执行
// 声明变量保存性别的值(1/0),如果是1打印男,否则打印女
var sex=0;
var result;
if(sex){
result='男';
}else{
result='女';
}
console.log(result);
//三目运算符 (条件表达式? 表达式1 : 表达式2)
result=sex ? '男' : '女';
console.log(result);
//区别:三目只能写一个表达式,ifelse的大括号里可以写多个表达式,所以简单的语句用三目表达式,复杂的用if else表达式。
// 声明变量分别保存用户名和密码,如果用户是root,并且密码是123456,打印登录成功,否则打印登录失败
var inputUname='hello';
var inputUpwd='123456';
if (inputUname==='root' && inputUpwd==='123456')
{console.log('登录成功');
}else{
console.log('登录失败');
}
//登陆失败
//三目运算符
inputUname==='root' && inputUpwd==='123456' ? console.log('登录成功') : console.log('登录失败')
//登录失败
事实上,else if结构就是if else嵌套的简便写法
// if-else的嵌套写法
if(score>=90){
... ... ...
}else{
if(score>=80){
... ... ...
}else{
... ... ...
}
}
// else-if
if(score>=90){
... ... ...
}else if(score>=80){
... ... ...
}else{
... ... ...
}
案例
声明变量保存订单的状态码 (1,2,3,4,5…)
1-等待付款 2-等待发货 3-运输中 4-已签收 5-已取消
根据状态码打印对应内容,不存在的状态码打印"非法的状态"
var status=3;
if(status===1){
console.log('等待付款');
}else if(status===2){
console.log('等待发货');
}else if(status===3){
console.log('运输中');
}else if(status===4){
console.log('已签收');
}else if(status===5){
console.log('已取消');
}else{
console.log('非法的状态码');
}
使用弹出提示框输入商品的价格和数量并计算总价,如果满500打九折,使用变量保存余额,如果足以支付,打印"支付成功",否则打印"支付失败"
var price,num,total,money=800;
price=prompt('请输入商品的价格');
num=prompt('请输入商品的数量');
total=price*num;//隐式转换自动转为数值
if (total>=500)
{
total*=0.9
}
console.log(total);
if(money>=total){
alert('支付成功');
console.log('当前余额为'+(money-total));//加法隐式字符串的拼接、优先级
}else{
alert('余额不足');
}
switch case语句是一种特殊的分支结构,可以根据一个表达式的不同取值,从不同的程序入口开始执行
通常case1、case2、…、caseN对应完全不同的操作,可以和break语句配合使用,执行完相应语句后即退出switch块,不继续执行下面的语句。break语句的作用在于跳出switch结构
var num=2;
switch(num){ //没有break继续执行下一句语句
case1:
console.log('查询余额...');
break;
case2:
console.log('在线充值...');
break;
case3:
console.log('转接人工服务...')
}
switch-case的优势
案例
使用switch-case语句根据一个人的成绩做出对应评价
var score=56;
switch ( parseInt(score/10) ) //除10取整数
{
case 10:
case 9:
console.log('A');
break;
case 8:
console.log('B');
break;
case 7: //自动转入下一语句
case 6:
console.log('C');
break;
default:
console.log('D');
}
循环次数不确定,一般搭配死循环的使用
forEach不支持break的使用
案例1
var i=1;
while(true){
console.log(i);
//当i为10的时候,结束循环
if(i===10){
break;
}
i++;
} //利用死循环
案例2
//猜数字的游戏
//声明变量保存一个数字,使用无限循环弹出提示框并输入数字,如果输入的数字大于保存的数字,警示框提示'猜大了',同理,猜小了,猜对了
var num=4999;
while (true)
{
varstr=prompt('请输入要猜的数字');
if(isNaN(str)){
alert('请输入一个数字');//如果输入的值为NaN
}elseif(str>num){
alert('猜大了');
}elseif(str
while循环语法格式
while(boolean表达式){
循环体语句
}
// 若boolean表达式为true,则执行一遍循环体中的语句,然后再判定一次boolean表达式,若为true,则再次执行一遍循环体中的语句,直到boolean表达式的值为false,则循环结束。
while(isHungury){
eatSomething()
}
// 需要注意的是,一般情况下,循环操作中会存在使得循环终止的可能性,否则将成为死循环
案例1
// 打印1~10
var i=1; //初始值
while(i<=10){//循环条件
//循环体
console.log(i);
i++; //增量
}
console.log('当循环结束的时候,i的值是'+i)//拼接
案例2
// 循环打印70 65 60 55 50
var i=70
while (i>=50)
{
console.log(i);
i-=5;
}
案例3
//打印1-100之间所有的奇数
var i=1;
while(i<=100){
//奇数
if(i%2===1){
console.log(i);
}
i++;}
//计算1-100之间所有整数的和
var i=1;
var sum=0;//用于记录和
while (i<=100)
{
//把每次产生的值加到sum
sum+=i
//console.log(sum);
i++
}
//打印sum最终的结果
console.log(sum);
//计算1-100之间所有偶数的和
var i=1;
var sum=0;
while(i<=100){
if(i%2===0){
//加到一起
sum+=i;
}
i++;
}
console.log(sum);
do-while循环语法格式
do{
可执行的语句; // 无论循环条件是否满足,循环体至少执行一遍
}while(boolean表达式)
// 先执行一次循环体中的语句,然后判定boolean表达式的值,若为true,则继续执行循环体中的语句;然后再继续判定boolean表达式的值...直到boolean表达式的值为false退出
do{
eatSomething()
}while(isHungry)
案例1
// 打印30-50之间的所有整数
var i=30;
do
{
console.log(i);
i++
}while (i<51);
案例2
// 打印1-100的所有奇数
var i=1;
do
{ //判断是否为奇数
if(i%2===1){
console.log(i);
}
i++
}while (i<=100);
* 案例3
```javascript
//计算1~100之间所有能被7整除的数字的和
var i=1;
var sum=0;
do
{
if(i%7===0){
sum+=i;
}
i++;
}while (i<=100);
console.log(sum);
//计算1~20之间所有能被3整除的数字的乘积
var i =1;
var s=1;
do
{
if(i%3===0){
s*=i;
}
i++;
}while (i<=20);
console.log(s);
while和do-while语句的区别
//ATM机输密码,先输密码再判断对错。
//使用while循环
var upwd=123456;
while(true){
varstr=prompt('请输入密码');
if(str===upwd){
break;
}
}
//使用do-while循环
var upwd=123456;
do
{
varstr=prompt('请输入密码');//输入密码
}
while ( str!=upwd);
for循环的语法格式
// 初始值 循环条件 增量
for(表达式1;表达式2;表达式3){
循环体语句
}
for循环执行过程
计算表达式1的值
计算表达式2(boolean表达式)的值,如果为true则执行循环体,否则退出循环
执行循环体
执行表达式3
计算表达式2;如果为true则执行循环体,否则退出循环
如此循环往复,直到表达式2的值为false
案例
//1-50之间所有能被3整除
for (var i=1;i<=50;i++ )
{
if(i%3===0){
console.log(i);
}
}
//计算101-200之间所有的偶数和
for (var i=101,sum=0;i<=200;i++)
{
if(i%2==0){
sum+=i;
}
}
console.log(sum);
//打印本世纪所有闰年
for (var i=2000;i<2100;i++ )
{
if(i%4===0 && i%100 !==0||i%400===0){
console.log(i);
}
}
//九九乘法表的第五行
//1*5= 5 ,2*5=10 ,3*5=15 ,4*5=20 ,5*5=25
for(var i=1,str='';i<=5;i++){
//str+=i; //1 2 34 5在一行
str+=i+'*5='+i*5+' '; //字符串的拼接
}
console.log(str);
for语句三个表达式特殊用法
表达式1位置内容为空
var sum=0;
var i=1;
for(;i<=100;i++){
sum+=i;
}
console.log('1-100的和为:',sum) //1-100的和为: 5050
表达式3的位置内容为空时
var sum=0;
for(var i=1;i<=100;){
sum+=i;
i++;
}
console.log('1-100的和为:',sum) //1-100的和为: 5050
表达式1,2,3位置内容均为空时
for(;;){
console.log('我要读书...') //死循环
}
循环中使用break语句
var sum=0
for(var i=0;i<=100;i++){
if(sum>=4000){
break
}
sum+=i
}
// 当总和大于等于4000时,退出循环
循环中使用continue 语句
//打印1-10之间所有整数,不包含5
for(var i=1;i<=10;i++){
//当i为5,跳过后续语句
if(i===5){
continue;
}
console.log(i);
}
//计算1-100之间所有偶数的和,遇到奇数跳过
for (var i=1,sum=0;i<=100;i++){
//如果i为奇数,跳过
if(i%2===1){
continue;
}
sum+=i;
}
console.log(sum);
循环嵌套(一)
需要多次重复执行一个或多个任务的问题考虑使用循环来解决
for/while/do-while三种循环在很多情况下是可以互换的;一般情况下,for循环使用最多
案例1
//*****
for (var i=1,str='';i<=5;i++)
{
str+='*';
}
console.log(str);
//*****
//*****
//*****
//*****
//*****
for (var j=1;j<=5;j++ )
{
for(var i=1,str='';i<=5;i++)
{
str+='*';
}
console.log(str);
}
//外层:循环行
//内层:循环列
案例2
//* 1 1
//** 2 2
//*** 3 3
//**** 4 4
//***** 5 5
for (var j=1;j<=5;j++ )
{
for(var i=1,str='';i<=j;i++) //str写在内层,表示每循环一层把之前一层清空
str 写在外层,表示每循环一层把之前一层加上
{
str+='*';
}
console.log(str);
}
//外层:循环行
//内层:循环列
案例3
//打印九九乘法表 (外层是九行
/*
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
*/
//外层循环
for(var i=1;i<=9;i++){
//内层循环
for(var j=1 str='';j<=i;j++){
str+=j+'*'+i+'='+(j*i)+'';
}
console.log(str);
}
案例4
//打印本世纪前10个闰年
for (vari=2000,count=0;i<=2100;i++)
{
if(i%4===0 && i%100!==0 ||i%400===0 ){
console.log(i);
//如果i为闰年
count++;
//每次有一个闰年产生,判断是否为10
if(count===10){
break;
}
}
}
console.log(count);
循环嵌套(二)
案例1
for(var i=0,j=8;i<=5;i++,j--){
console.log(i,j)
}
案例2
// 多个条件表达式后边的起作用
for(i=1,j=8;j>=1,i<=5;i++,j--){
console.log(i,j)
}
for(var i=1,j=8;i<=5,j>=1;i++,j--){
console.log(i,j)
}
案例3
//计算1/20+2/18+.....10/2
for(vari=1,j=20,sum=0;i<=10;i++,j-=2){
sum+=i/j;
}
console.log(sum);
forEach(fun)专门用于对原数组中每个元素执行相同的fun函数对象规定的操作
forEach(function(elem,i,arr){})
【面试题】:forEach能否取代for循环
every(fun) 判断数组中每一个元素是否都满足fun函数定义的条件,只有满足才返回true,否则返回false
every(function(elem,i,arr){ return xxx})
some(function(elem,i,arr){ return xxx})
判断数组中是否包含满足fun函数定义得条件得元素。只要包含返回true,否则返回falsefilter(function(elem,i,arr){ return xxx})
专门用于筛选出数组中符合fun函数判断条件得新数组map(function(elem,i,arr){ return xxx})
专门用于基于原数组创建新数组对象
对数组中的每一个值做操作 并把操作后的值汇成一个新数组
var newArr=arr.map((val)=>{
return val+2
});
console.log(newArr);
map()和forEach()的区别: 前者不改变原数组的内容,后者改变原数组的内容
reduce((prev,elem,i,arr)=>{ return prev+elem})
reduce()和reduceRight()方法会迭代数组中的每一个元素,汇总出和一个最终结果值返回for循环跳出循环
var arr = [1,3,5,7,9];
var id = 5;
for (var i = 0; i < arr.length; i++) {
if(arr[i]=== 1) continue; //跳过当次循环
console.log(arr[i])
if (arr[i] === id) {
break; //满足条件,跳出循环
}
if (arr[i] === 6) {
return; //满足条件,可以终止当前函数
}
}
forEach跳出循环
通过抛出异常的方式跳出循环,return 跳出当次循环
var arr = [1,3,5,7,9];
var id = 5;
try {
arr.forEach(function (curItem, i) {
if(curItem === 1) return;
console.log(curItem)
if (curItem === id) {
throw Error(); //满足条件,跳出循环
}
})
} catch (e) {
}
arr.forEach(function (curItem, i) {
if(curItem === 1) return;
console.log(curItem)
if (curItem === id) {
return; //满足条件,跳出当次循环
// break 语法报错;
}
})
console.log(isNaN('123.4a')) // true
console.log(isNaN(parseInt('123.4a'))) //false
console.log(isNaN(parseFloat('123.4a'))) //false
console.log(isNaN(Number('123.4a'))) //true
//函数:toString()
//只能数值和布尔值转换成字符串
varnum=2;
varstr=num.toString();
console.log(str,typeofstr);
varislogin=true
varstr1=islogin.toString();
console.log(str1,typeofstr1);
console.log(‘2’.charCodeAt())查字符串的unicode
解析成一个string或number的整数部分
如果没有可以转换的部分,则返回NaN(Not a Number)
第一个参数将浮点数转换为字符串,取浮点数的整数部分
第一个参数不是字符串,parseInt会调用这个参数的tostring方法,把参数强制转换成字符串
第二个参数可选,是用来告诉parseInt,字符串用什么进制表示
当第二个参数是undefined,0或未指定,js会假定一下情况
如果输入的string是0x或0X,radix被假定为16,字符串的其余部分被当做十六进制去解析
如果输入的string以0开头,radix被假定为10或者8,具体选择哪一个radix取决于浏览器的实现,现在一般10进制比较多
如果输入的string以其他任何值开头,radix是10(十进制)
当第二个参数为1,parseInt只会返回NaN
案例
函数:parseInt(string,radix)
console.log( parseInt(3.14) ); //3
console.log( parseInt('6.14') ); //6
console.log( parseInt('a9.18') ); //NaN
console.log( parseInt('9.18a') ); //9
console.log(parseInt(true)); //NaN
console.log( parseInt(undefined) );//NaN
console.log( parseInt(null) ); //NaN
//面试题1:
const obj={
val:0,
tostring(){
this.val++,
return this.val
}
}
parseInt(obj,10)//1
parseInt(obj,10)//2
parseInt(obj,10)//3
//面试题2:
例如:[1,2,3].map(parseInt) //[1,NaNNaN]
[1,2,3].map(parseInt)等价于
[1,2,3].map((num,index)=>parseInt(num,index))等价于
[parseInt(1,0),parseInt(2,1),parseInt(3,2)]
radix为0,parseInt(1,0)等价于parseInt(1,10),string以1开头,默认会转换为10进制的1 1
1进制里面不会存在2 NaN
2禁止里面不会存在3 NaN
//函数:ParseFloat()
//解析出一个string的浮点数部分
console.log( parfloat('9.18a') ); //9.18
//函数:Number()
console.log( Number(true) );//1
console.log( Number(null) );//0
console.log( Number(undefined) );//NaN
console.log( Number('1a') );//NaN
var num1=prompt('请输入第一个数字');
var num2=prompt('请输入第二个数字');
num1=Number(num1);
num2=Number(num2);
alert(num1+num2);
// 函数只能在浏览器端运行,在服务端不支持该两个函数。
是返回true,不是返回false,无限值只有infinity 1/0 0做除数返回的是infinity
// 要求弹出提示框输入内容,使用eval来执行输入的内容
var str=prompt('请输入一组算数表达式')
console.log(eval(str))
JavaScript中创建函数得三种方式
创建普通函数
function 函数名称(){
函数体;//所封装的代码
}、
调用函数: 函数名称();
// 函数里存的代码,不调用函数是不会执行的
// 例1 创建函数,封装计算1-100之间所有整数的和,并调用3次
for (var i=1,sum=0;i<=100;i++ )
{
sum+=i
}
console.log(sum);
}
getsun();
getsun();
getsun();
创建带有参数的函数
function 函数名称(参数列表){
// 参数列表:用于接受传递的数据 形参
函数体...
}
调用: 函数名称(参数列表)//实际传递的数据 实参
// 例 计算任意两个数字相加的和
function add(a,b){//形参
console.log(a+b);
}
add(2,3); //实参
add(4,9);
add(8,15);
// 例 创建函数,计算1-任意数字之间所有数字的和,调用多次
functiongetsum(n){
//计算1-n之间所有整数的和
for (var i=1,sum=0;i<=n;i++)
{
sum+=i;
}
console.log(sum);
}
//getsum(100);
//getsum(50);
// 例 创建函数,计算任意两个年份之间闰年的个数,调用多次
function getrun(n1,n2){
//计算n1~n2之间闰年的个数
/*
for(vari=n1;i<=n2;i++)
*/
for (var count=0;n1<=n2;n1++ )
{
if (n1%4===0 && n1%100!==0 ||n1%400===0)
{
count++;
}
}
console.log(count);//结果输出到控制台
}
getrun(2000,2100);//值只是打印在console里,并没有保存下来。
验证:var str=getrun(2000,2100);//没有存返回值,并不能赋值给str。
创建带有返回值的函数
// 有return就可以赋值给变量
// 如果赋值给变量时,函数中没有return或者return后不加任何值,返回undefined
function 函数名称(参数列表){//参数列表:用于接受传递的数据
函数体:
Return 值 //返回值(函数调用后产出的结果)
}
// 例 计算任意两个数字相加的和
function add(a,b){
return a+b;
}
验证:var res=add(1,6);
console.log(res); //7有return赋值成功
// 例 创建函数,比较任意两个数字的大小,返回最大值
function getMax1(a,b){
return a>b ? a : b //三目运算符=if else
}
var num=getMax1(5,13);
console.log(num);
// 例 创建函数,比较任意三个数字的大小,返回最大值
function getMax2(a,b,c){
varmax=a>b?a:b;
returnmax > c ? max : c;
}
var res=getMax2(2,7,4);
console.log(res);
比较多个数字,用的是数组
// 例 1-等待付款 2-等待发货 3-运输中 4-已签收 5-已取消 其他-无法追踪
function getstatus(n){
switch(n){
case1:
return'等待付款';//return 出现后,后面语句不执行,所以break注释;
//break;
case2:
return'等待发货';
//break;
case3:
return'运输中';
//break;
case4:
return'已签收';
//break;
default:
return'无法追踪'
}
}
var res=getstatus(4);
console.log(res);
// 例 创建函数,传递任意一个年份,如果是闰年返回true,否则返回false
//由题可知,肯定是一个参数.参数为年数
function isrun(year){
if(year%4===0&& year%100==0 || year%400===0){
returntrue;
}
returnfalse;
}
//var res=isrun(2028);
//console.log(res);
isrun(2036) ? console.log(366) :console.log(365);
// 例 阶乘:!5 = 5*4*3*2*1
//创建函数,传递任意一个数字,返回1~任意数字之间所有整数之间所有整数阶乘的和 !1+!2+!3+!4+!5
//第一步:创建函数,传递任意一个数字,返回1~任意数字之间所有整数的阶乘
function getJc(n){
//1~n
for(var i=1,s=1;i<=n;i++){
s*=i;
}
return s;
}
//console.log(getJc(5));
//第二步:创建函数,传递任意一个数字,返回1~任意数字之间所有整数的和
function getsun(n){
for(var i=1,sum=0;i<=n;i++){
//1+2+3+4+5
//sum+=i;
//!1+!2+!3+...+!5
// getJc(1)+getJc(2)+...+getJc(3)
sum+=getJc(i)
//sum+=i;
}
return sum;
}
console.log(getsun(5));
console.log(i);报错,i变量处于函数内部,只能在函数内部使用,所以i是局部变量。
使用直接量方式创建函数
var functionName=function(arg1,...argN){
// 函数体...
}
function add(num1,num2){
var sum=num1+num2
return sum
}
// 等价于
var add=function(num1,num2){
var sum=num1+num2
return sum
}
函数名本质上也是一个变量名,是指向某个Function对象得引用
在js中函数也是以对象形式存在的,每个函数都是一个function对象实列
function add(){}
console.log(typeof add) //function
使用Function对象创建函数
var functionName=new Function(arg1,...argN,functionBody)
fuacntion add(num1,num2){
var sum=num1+num2
return sum
}
// 等价于
var add=new Function('num1','num2','var sum=num1+num2;return sum')
Function是一个类,所有的方法都是Function类实例化的对象,给Function原型上绑定方法,则所有方法都可以调用这个方法
var fun=function(){ //变量名称fun就是函数名称
console.log(1)
}
fun() //调用
函数有名字会污染全局
匿名函数自调用不会对全局造成污染,其实就是创建了一个作用域
// 创建函数作用域,防止污染全局
(function(形参){
//作用域
})(实参);
var num=3;
(function(num){
num++
console.log(num);//4
})(num);
console.log(num);//3
function fn(a){
a 就是函数名称
a() 调用传递的匿名函数
}
fn(function(){})
案例1
// 创建函数,调用的时候,传递两个匿名函数,每个匿名函数中都是返回一个数字,要计算两个数字相加的和
function add(a,b){
// a和b对应传入的匿名函数
var num1=a()
var num2=b()
console.log(num1+num2) //3
}
add(function(){return 1},function(){return 2})
案例2
函数名称就是一个变量,不管哪种方式,都保存了函数的结构
函数名称()是调用函数,得到函数的返回结果
案例
// 使用函数表达式创建函数,计算任意两个数字之间所有整数相加的和,并返回结果
var add=function(a,b){
//循环a~b
for(vari=a,sum=0;i<=b;i++){
sum+=i;
}
return sum;
}
console.log(add(1,3));// 6
递归是在函数中调用自身的一种算法
function say(){
alert('hahahh');
say();
}
say (); //构成死循环 又叫递归
递归的使用:
边界调节 ==break
递归前进
递归结束(return)
var i=0;
function say(){
i++;
alert('hahahh');
//当i为3,递归结束
if(i===3){//边界调节
return;//递归结束
}
say();//递归前进
}
say (); //构成死循环 又叫递归
案例1:
// 使用递归计算1-5之间所有整数的和
function getsum(n){
if(n===1){
ᅠᅠᅠᅠᅠᅠ return1;
}
//当前的和由n+前n-1的和
return n+getsum(n-1);
}
console.log(getsum(5))
案例2:
// 计算斐波那契数列
// 前两项是1,第三项开始,每项是前两项的和
// 1,1,2,3,5,8,13,21.34
// 分别使用递归和普通的函数的方式计算斐波那契数列的第n项
1.递归方式 (嵌套层次太深,造成计算缓慢,没有充分利用cpu)
function fib(n){
//当n为1或者2,返回1
if(n===1||n===2){
return1;
}
returnfib(n-1)+fib(n-2);
}
console.log(fib(5));
/*fib(4)+fib(3)
fib(3)+fib(2)+fib(2)+fib(1)
fib(2)+fib(1)+fib(2)+fib(2)+fib(1)
*/
fib(5)
2.循环方式
function fib2(n){
var n1=1,n2=1;
//循环从第三项开始
for(var i=3;i<=n;i++){
//每循环一次都要挪动一次n1和n2
var tmp=n1;
n1=n2;
n2=tmp+n2;
}
//循环后n2就是所求项的值
return n2;
}
console.log(fib2(50));// 运算速度快
作用域
//全局作用域
var a=1;//全局变量
function fn(){
//函数作用域
varb=2; //局部变量
console.log(a);//全局变量可以调用
}
fn();//调用函数
console.log(b); //局部变量不能调用到
//函数里不加var是全局变量
var c=3;
function fun(){
c=4;
}
fun();
console.log(c); //4
function foo(){
//没有加var变成了全局变量
d=5;
vare=f=8;
//f=8 全局
//var e=f 局部
}
foo(); //调用函数
//console.log(d); //5//console.log(e);
console.log(a)
var a=1
↓
var a
console.log(a) //undefined
a=1
function fn(){
console.log(b)
var b=2
}
fn()
↓
function fn(){
var b
console.log(b) //undefined
b=2
}
fn()
var c=5
function fun(){
console.log(c);
var c=7
}
fun()
↓
var c=5
function fun(){
var c
console.log(c)//undefined
c=7
}
var e=9
function f00(){
console.log(e) //9
e=7
console.log(e) //7
}
foo()
var n=7
function fn2(n){
// 形参就是相当于函数内var声明的变量
// var n=8
n+=2
console.log(n) //10 局部变量 n+=2
}
fn2(8)
console.log(n) //全局变量 n=7 不变 7
函数作用域:变量在声明它的函数体以及这个函数体内签套的任何函数体内都是有定义的
函数的可访问范围也分为全局函数和局部函数,全局函数可以在任何位置访问,而局部函数只能所有的作用域下访问
//全局作用域函数
function fn(){
console.log(1);
}
//局部函数
function foo(){
fn();//1
//fun是局部函数
functionfun(){
console.log(2);
}
fun();//访问到了
}
foo();//先调用外面的foo函数
fun();//访问不到
var a=100
function f(){
a=200
console.log(a) //200
}
f()
console.log(a)//200
var a=100
function f(){
a=200
console.log(a) //200
}
console.log(a)//100
f()
var a=100
function f(){
var a=200
console.log(a) //200
}
console.log(a)//100
f()
// 就近原则,全局变量和局部变量互不影响
bar()
function bar(){
consol.log(3)
}
词法表示包括不必计算的变量的函数,也就是说,该函数能使用函数外定义的变量
MDN对于闭包的解释: 闭包是哪些能够访问自由变量的函数,这里的自由变量就是指作用域外的变量
红宝书对于闭包的解释: 有权访问另一个函数作用域的变量的函数
var n;
function fn(){
var b="b"
n=function(){
return b
}
}
var a=100
function f(){
var a=200
function g(){
return a
}
return g
}
var g=f()
console.log(g())
// 1. 函数f被调用时,其局部变量a保存在活动对象中
// 2. 通常在调用结束时,该活动对象被释放
// 3. 但函数f中的内嵌函数g引用到了父级函数活动对象中的属性a,而该函数的引用又作为f的返回值,返回给了外部
// 4. 所以该外层函数的活动对象,由于有了外层引用而在方法调用结束后并没有被回收
// 5. 函数g被调用时,所使用的是声明在方法f中的局部变量a
// 6. 该变量存放在调用f方法时所创建的活动对象中,而该对象由于存在外部引用,在方法f调用完成后依然没有被回收。
// 定义一个变量,只被getValue和setValue两个函数访问
var getValue,getValue
(function(){
var secret=0;
getValue=function(){
return secret
}
setValue=function(value){
secret=value
}
})()
console.log(getValue())//0
setValue(100)
console.log(getValue())//100
secret=0
console.log(getValue())//100
在父级作用域里会生成一个变量var i=0,在子作用域里使用这个变量,这样声明的那个变量i就是自由变量,这种作用域嵌套环境叫做 闭包环境
节流防抖函数柯里化 高阶函数 vue的响应式原理 react hooks fab的原理
对象是一组属性(property)和方法(method)的集合
例如 一个人:属性有姓名,性别,身高,体重,方法有走路说话
使用大括号创建空对象
属性名和属性值之间用冒号隔开
多组属性之间用逗号隔开
属性名中的引号可加可不加,如果含有特殊字符必须要加
var person={
name:'tom',
sex:'男',
userName:'root',
'user@pwd':'1234'
}
new Object()
创建一个空对象
需要通过访问对象属性来添加每个属性
// 创建完函数对象之后,才能添加属性
var laptop=new Object()
laotop.lid=5
laptop.title='小米air'
laptop['price']=4999
console.log(laptop)
class Person{
constructor(name,age,sex){
this.name=name
this.age=age
this.sex=sex
}
interview(){
console.log(`我叫${this.name},今年${this.age}岁`)
}
}
const person=new Person('德玛',17,1)
console.log(person)
// 创建对象person,然后将create()里的对象放入person对象的原型对象中
const person=Object.create({
name:'tom',
sex:'男',
userName:'root',
'user@pwd':'1234'
})
person.age=17
console.log(person)
// 唯一创建没有原型对象的对象的方法
const person=Object.create(null)
person.age=17
console.log(person)
对象.属性名
对象['属性名']
如果访问不存在的属性返回undefined
var person={
name:'tom',
sex:'男',
userName:'root',
'user@pwd':'1234'
}
//访问属性
console.log(person.age);
console.log(person['name']);
console.log(person['user@pwd']); //属性名含有引号用中括号的方法
//访问不存在的属性--返回undefined,不会报错
//修改属性
person.name='huwenhao';
console.log(person);
//添加不存在属性
person.addr='北京';
console.log(person);
方法一: 对象.属性名===undefined
true不存在 false 存在
方法二:对象.hasOwnProperty(‘属性名’)
true存在 false不存在
方法三:‘属性名’in 对象
true存在 false 不存在
案例 1
var person={
name:'tom',
age:18,
sex:'男'
}
console.log(person.name===undefined);//false
console.log(person.height===undefined);//true
console.log(person.hasOwnProperty('height'));//false
console.log( 'phone' in person);//false
console.log( 'age' in person);//true
案例 2
//创建一个商品对象,含有编号,标题属性,如果价格属性不存在,添加价格属性,如果编号属性存在,添加上架时间属性
var laptop={
lid:7,
title:'小米Air'
}
if(laptop.price===undefined){
laotop.price=3000
}
if(laptop.hasOwnProperty('lid')){
laptop.shelfTime="2023-10-1"
}
console.log(laptop)
var person={
name:"richard",
age:23,
say:function(){
console.log(this.name)
//this是当前指代的对象,防止后期对象名修改
},// 匿名函数赋值给say
run:function(){
console.log('running')
}
person.say()
person.run()
//外部添加方法
person.work=function(){ }
}
//创建计算器对象,含有两个数字属性,创建一个方法来实现计算加减乘除
var calc={
num1:8,
num2:15,
jsq:function(t){
if(t==='+'){
console.log(this.num1+this.num2);
}elseif(t==='-'){
console.log(this.num1-this.num2);
//当前对象下的num1和num2
}elseif(t==='*'){
console.log(this.num1*this.num2);
}elseif(t==='/'){
console.log(this.num1/this.num2);
}
}
};
calc.jsq('+');
calc.jsq('/');
// 创建圆对象,包含属性圆周率,半径,添加计算周长和面积的方法
var circle={
r:5,
pi:3.14,
getLength:function(){
return 2*this.pi*this.r
},
getArea:function(){
return Math.pow(r,2)*this.pi
}
}
//Return是返回值 看结果要打印 this表示这个对象, 要想调用属性作运算必须加上。
//Console.log(circle.getLength());
//Console.log(circle.getArea());
【注:202309015】 String Number Boolean 是基础类型数据,但是都可以包装成对象类型(new的方式去创建),并且无论是基础数据类型或者对象类型都可以使用原型对象上的api
new String()
返回对象String()
返回字符串
var str1= 'dema' //字面量
var str2=new String(2) //构造函数
var str3=String(2) //普通函数
console.log(typeof str2) // object
console.log(typeof str3) // string
console.log(str2+3) // 23
console.log(str23+3) // 23
String() 可以转数组和对象为字符串
var obj={name:'tom',age:18}
var arr=[1,2,3]
console.log(String(obj)) //[object Object]
console.log(String(arr)) //1,2,3
字符串的api
无论用何种形式创建的字符串都可以使用
length
获取字符串的长度
字符串[0]
字符串也有下标
charAt()
获取下标对应的字符
lastIndexOf(str)
查找字符最后出现的下标, 找不到都会返回-1,不支持正则
toUpperCase()
输出字符串都大写
toLowerCase()
输出字符串都小写
slice(start,end)
截取字符串,start开始的下标,end解释的下标,不包括end本身,如果end为空,截取到最后,如果是负数表示倒数,不改变原数组
substr(start,count)
截取字符串,start开始的下标,count是截取的长度,count为空截取到最后,start为负数表示倒数,不改变原数组
substring(start,end)
截取字符串,start表示开始的下标,end结束的下标,end为空截取到最后,如果是负数,自动转为0,如果start大于end,则位置会自动交换。不改变原数组
split(sep/reg,[howmany])
将字符串按照指定的字符切割成数组,sep表示指定的字符,支持正则
var data='How are you'
var regexp=/\s+/
var arr=data.split(regexp)
console.log(arr);//["How","are","you"]
匹配模式(敏感词):作用:用于查找、替换字符串、能写正则
indexOf(str,start)
start开始查找的下标,如果为空,表示从从第一个开始,找不到都会返回-1,不支持正则
search(str/reg)
查找满足条件的第一个,并返回下标,如果找不到返回-1,支持正则
match(str/reg)
用于查找满足条件的所有敏感词的内容和位置,返回数组 /china/ig,支持正则, i->ignore 忽略大小写 ,g->global 全局查找
var arr=str.match(/正则/g)
arr:[0:敏感词,index:位置i]
,如果没找到返回null,但是只能找到第一个do while
+reg.exec()
replace(str/reg,replacement)
对字符串特定格式的子串进行替换,返回替换后的结果
第一个参数既是一个固定的子串,也是一个正则表达式对象
第二个参数是一个回调函数,也可以是一个字符串
第二个参数可以给正则里的分组占位
var pid='320121199808310091'
var birth=pid.slice(6,14)
// 用括号分组,打开懒惰模式
var reg=/(\d{4})(\d{2)(\d{2})/
// ()自动添加组号
// 组1 组2 组3
birth=birth.replace(reg,'$1年$2月$3日')
console.log(birth)
注【20230917】:查找敏感词的api总结
var num1=new Number(true); //转为数值,同时返回对象
var num2= Number(true); //转为数值,返回数值型
var num3=1;//字面量
//console.log(num1,typeof num1);
//console.log(num2,typeof num2);
console.log(num1+2);
console.log(num2+2);//用法相同
toFixed(2)
保留数字小数点后2位; 不改变原数字;用此api转换以后是字符串类型toString(2)
转换成2进制;不改变原数字;用此api转换以后是字符串类型//圆周率 pl
console.log(Math.PI);//3.141592653589793
//取绝对值 absolute abs()
console.log(Math.abs(14-63)) //49
//向上取整 ceil()
console.log(Math.ceil(3.14));//4
//向下取整 floor()
console.log(Math.floor(3.14));//3
//四舍五入取整 round()
console.log(Math.round(7.51));//8
//取一组数字的最大值 max()
console.log(Math.max(23,9,78,45,6));//78
//取一组数字的最小值 min()
console.log(Math.min(23,9,78,45,6));//6
//x的y次方 pow()
console.log(Math.pow(5,2));//25
//随机数random()
console.log(Math.random());//>=0 <1
// 案例
var alp=['a','b','c','d','e','f','g','h','i','g','k','l','i','n','o','p','q','y','s','d','u','v','w','x','y','z'];
var alp2=[];
for(var i=0;i<4;i++){
num=Math.floor(Math.random()*alp.length)
alp2.push(alp[num]);
}
console.log(alp2);
//删除元素 循环完删除就不会重复
Alp.splice(num,1)
用于对日期时间的存储和计算
通常打印出的时间是0时区的时间
var d=new Date('2028/7/20 19:30:45');
console.log(d,typeof d);
2028-07-20T11:30:45.000Zobject
//打印出来的时间早8小时,object
创建date对象
用字符串形式最简单,用月份还要考虑月份
var d1=new Date('2029/10/23 10:45:30');//2029-10-23T02:45:30.000Z
var d1=new Date('2029/10/23');//2029-10-22T16:00:00.000Z
var d2=new Date(2029,10,23,10,45,30);//2029-11-23T02:45:30.000Z 月份是0~11 1月份就是0
var d3=new Date();//存储当前系统时间 做秒杀倒计时
var d4=new Date(1000);//距离计算机元年的毫秒数
console.log(d2);
获取date对象中存储的日期时间
```javascript
var d = new Date();
console.log(d.getFullYear());//2021 getYear有bug,不要用,采用截取方式获得后两位。 年
console.log(d.getMonth());//6 月份0~11
console.log(d.getDate());//16 日
console.log(d.getHours());//小时
console.log(d.getMinutes());//分钟
console.log(d.getSeconds());//秒
console.log(d.getMilliseconds());//毫秒
console.log(d.getDay())//星期 星期日-星期六 0—6
console.log(d.getTime());//距离计算机元年的毫秒数
```
例1 计算从2031/05/01到2031/10/01
// 计算两个时间相差的毫秒数
var d1=new Date(‘2023/5/1 18:23:50’)
var d2=new Date(‘2031/10/1 5:45:0’)
var d=d2-d1
// 把单位转化为秒
d=Math.floor(d/1000)
// 把相差的值换算成天
var day=Math.floor(d/(246060))
// 相差的小时首先去除相差的天
var hour=d%(246060)
hour=Math.floor(hour/(60*60))
// 相差的分钟,去除相差的小时,剩余的秒转为分钟
var minute=d%3600
minute=Math.floor(minute/60)
// 相差的秒种,去除相差的分钟
var second=d%60
console.log(day,hour,minute,second) //3074 11 21 10
本地日期时间格式
var d=new Date('2028/7/20 19:30:45');
console.log(d.toLocaleString()) //2028/7/20 19:30:45
console.log(d.toLocaleDateString()) //2028/7/20
console.log(d.toLocaleTimeString()) //19:30:45
设置日期时间
var d=new Date('2028/7/20 19:30:45')
d.setFullYear(2038)
d.setMonth(9) // 0-11
d.setDate(d.getDate()+3)
d.setTime(1000) //设置距离计算机元年毫秒数
var d1=new Date('2028/7/20 19:30:45')
var d2=new Date(d1) //以参数形式构建一个新对象
var d1=new Date('1994/8/18 13:20:30')
var d2=new Date(d1)
var d1=new Date('1995/9/18 13:20:30')
console.log(d1)
console.log(d2)
//d1要以参数形式保存在d2中,d1发生重新赋值,d2的值不会发生改变
var emp1='tom';
var emp2='king';
var emp3='jerry';
//数组字面量
var emp=['tom','king','jerry'];
console.log(emp);
//数组可存储不同的数据
var emp=['tom','king','jerry',18,19,20,true,false];
console.log(emp);
new Array(元素1,元素2….)
new Array(3) 初始化数组的元素个数为3,可以添加更多的元素
1.var company=new Array('百度','腾讯','阿里');//和对象不同,数组的内置函数创建可以直接添加
//console.log(company);
2.var company2=new Array(3); //创建元素,包含三个元素
company2[0]='百度';
company2[1]='腾讯';
company2[2]='阿里'; //添加元素
console.log(conpany 2);
// 数组中创建空对象,对象指向同一个堆内存地址
var a=new Array(3).fill({})
consol.log(a) //[{},{},{}]
a[0].name='dema'
console.log(a) //[{name: 'dema'},{name: 'dema'},{name: 'dema'}]
//方式1: 数组中创建空对象,对象指向不同的堆内存地址
var a=[{},{},{}]
a[0].name='dema'
console.log(a) //[{name: 'dema'},{},{}]
//方式2: 数组中创建空对象,对象指向不同的堆内存地址
var a=Array.from({length:3},()=>{return {}})
a[0].name="dema"
console.log(a) //[{name: 'dema'},{},{}]
// 通过 下标 数组名[下标从0开始]
var city=['北京','上海', '深圳', '广州'];
console.log(city[0]);//北京
console.log(city[1]);//上海
//访问不存在的数组返回undefined
console.log(city[1],city[5]);//上海 undefined
//通过下标修改或添加数组中的值
数组.length
数组几个元素,数据长度是几可以在数组的末尾添加元素 数组[数组.length]=值
var a=[];
a[a.length]=1;
a[a.length]=2;
a[a.length]=3;
console.log(a)//[1,2,3]
var day=3;
a[day]=4;
console.log(a)//[1,2,3,4]
遍历数组
关联数组:以字符串做下标,只能单独的添加每一项元素
var emp=[]
//添加元素
emp['ename']='tom',
emp['sex']='男',
emp.age=18,
console.log(emp) //[ename: 'tom', sex: '男', age: 18]
索引数组:以数字为下标
api:JS中预定义好的方法
toString() 将数组元素转为字符串,字符串用“,”隔开,不改变原数组
join(‘-’) 将数组元素按照指定的字符组合为字符串,不改变原数组
concat(arr1,arr2,…) 拼接多个数组 其他数组以参数形式,不改变原数组
slice(start,end) 截取数组的元素,start表示开始的下标,end是结束的下标,不包括end,如果是负数表示倒数,如果是end为空截取到最后,返回数组,不改变原数组
reverse() 翻转数组中的元素 改变原数组
sort()分类,排序,默认按照unicode码排列,改变原数组
arr.sort(function(a,b){return a-b})
arr.sort(function(a,b){return b-a})
Splice(start,count,value)
删除数组中的元素,
start:开始的下标,start为负数表示倒数
count:删除的数量,count为0表示不删除,count为空删除到最后
value删除后添加的元素,返回删除后的元素
元素组会发生变化,返回的是一个数组,里面存的是删除的数据
push() 在末尾添加一个或多个元素
pop() 删除末尾的一个元素
unshift() 在开头添加一个或多个元素
shift() 删除开头的一个元素
【注】新增返回添加后数组的length,删除返回第一个或最后一个删除的元素
Var arr=[[],[],[]]
arr[下标][下标]
正则表达式(Regular Expression):由一些普通字符和特殊字符组成的,用以描述一种特定字符规则的表达式。
正则表达式常用于在一段文本中搜索,匹配或替换特定形式的文本,如:词语出现频率的统计、验证字符串是否符合邮箱格式、屏蔽一篇帖子中的限制性词语等
所有的单个大小写字母、数字都是一个正则表达式,用以匹配单个字符,这个字符与他本身相同,如
var regexp=/ipod/
var data='Apple iPod is No.123 cool??'
console.log(regexp.test(data)) //false
正则表达式中有些字符有特殊的语法含义,是不能直接使用的,必须使用\进行转义后才能使用. \ / * ? + [ ( ) ] { } ^ $ |
,对不是元字符的字符进行转义是不会出问题的,但是未对元字符进行转义就会有错误。
字符集
:正则表达式使用如下语法匹配一个范围内的字符
[abc]
匹配指定集合内的任一个字符 /[3458]/
[^abc]
匹配不在指定集合内的任意字符 /[^12679]/
[0-9]
匹配任一个数字 /[0-9]/
[a-z]
匹配任一个小写字符 /[a-z]/
[A-Z]
匹配任一个大写字符 /A-Z/
[A-z]
匹配大写A到小写z的所有字符,即A-Z[]^_`a-z /[A-z]/
预定义字符集
:正则表达式可以使用如下元字符引用来进行简化
\d
匹配一个数字 /\d/ 等价于 /[0-9]/
\D
匹配一个非数字 /\D/ 等价于 /[^0-9]/
\w
匹配一个数字/字母/下划线 /\w/ 等价于 /[0-9a-zA-Z_]/
\W
匹配一个非数字/字母/下划线 /\W/ 等价于 /[^0-9a-zA-Z]/
\s
匹配一个空白字符 /\s/ 等价于 /[\n\r\t\v\f]/
\S
匹配一个非空白字符 /\S/ 等价于 /[^\n\r\t\v\f]/
.
匹配除了回车和换行外的任何单个字符 /./等价于/[^\n\r]/
数量词
正则表达式可以使用如下特殊字符定义字符的出现频次–量词元字符
n?
匹配零次或1次字符n /a?/
n*
匹配零次或多次字符n /a*/
n+
匹配1次或多次字符n /a+/
n{x}
匹配字符n出现x次 /a{3}/
n{x,y}
匹配字符n出现x到y次 /a{2,4}/
n{x,}
匹配字符n的出现>=x次 /a{3,}/
选择和分组
正则表达式使用如下语法定义子表达式分组或选择
exp1|exp2
使用|进行条件选择 /ex|Ex|post|Post/
(exp1)
使用()指定分组—子表达式,每个分组自动获取形如\1、\2、\3…的分组号指定匹配位置
可以使用如下字符进行指定位置的匹配
^
匹配字符串的开头 /^a/
注意^
的两种用法$
匹配字符串的结尾 /a$/
\b
匹配单词的边界 /\bhis\b/
\B
匹配单词的非边界 /\Bhis\B/
?=x
匹配其后紧接x的字符串 /do(?=not)/
?!x
匹配其后没有紧接x的字符串 /do(?!not)/
\
\
\
将特殊字符转为普通字符\n
将普通字符n转义为换行符\t
将普通字符转义为制表符(tab键效果)
// 打印出 c:\xampp\htdoc\js
console.log('c:\\xammp\\htdoc\\js')
创建正则表达式对象
直接量语法 var patt1=/pattern/attributes
var regexp1=/\d{2,3}/ig
调用RegExp构造函数 var patt2=new RegExp(pattern,attributes)
var regexp2=new RegExp('\\d{2,3}','ig')
global
(只读)RegExp对象是否具有g修饰符ignoreCase
(只读)RegExp对象是否具有i修饰符multiline
(只读)RegExp对象是否具有m修饰符lastIndex
用法设置/获取下次匹配的起始位置source
(只读) 返回模式匹配所使用的文本$1...$9
(全局属性)指代一次搜索中某个子表达式匹配的文本compile()
exec()
test() 验证
面向对象的语言,面向对象适合大量数据的管理和维护,在程序中,在程序中先用对象结构描述现实中一个具体事务的属性和功能,在后续程序开发当中按需使用对象中已经确定的属性和功能。
封装
继承
多态
可以使用function模板批量的创建某种类型的多个实例,并且这些实例具备相同的基础属性
// 创建对象模板---也称为对象的构造方法
function Emp(){
// 此处的Emp作为对象实例的模板,可以多次调用以构造多个同类型的实例
}
// 好处:结构定义一次,就可以调用无数次
// 使用模板创建对象的多个实例
// 此处省略了new关键字会怎样呢,会返回undefined
var e1=new Emp()
var e2=new Emp()
console.log(tyoepf e1) //Object
// Emp是否是e1的构造函数
console.log(e1 instanceof Emp) //true
利用模板定义对象的属性
this
关键字声明对象的属性
function Emp(){
this.ename="Tom"
this.salary=3500
this.hiredate=new Date()
}
var e1=new Emp()
console.log(e1.ename)
console.log(e1['hiredate'])
function Emp(ename,salary,hiredate){
this.ename=ename
this.salary=salary
this.hiredate=hiredate
}
利用模板定义对象的方法
对象模板中使用this
关键词声明对象方法
function Circle(r){
this.r=r;
// 方法声明在构造方法内,则每创建一个实例,都会创建一个函数对象
this.getSize=function(){
return Math.Pl*this.r*this.r
}
// 方法声明在构造函数外,则所有的实例共用同一个函数
this.getPerimeter=perimeter
}
function perimeter(){
return 2*Math.Pl*this.r
}
var c1=new Circle(5)
console.log(c1.getSize())
console.log(c1.getPerimeter())
this关键字
JavaScript中,this关键字用在方法内。专门引用正在被调用的方法当前所在的对象
函数中,this为当前对象
var hero={}
hero.sayName=function(){
return 'hello'+this.ename
}
构造函数中,this引用新创建的对象
function Emp(){
this.ename="Tom"
this.salary=3500
this.hiredate=new Date()
}
扩展单个对象的成员
function Emp(ename,salary,hiredate){
this.ename=ename
this.salary=salary
}
var emp1 = new Emp('Tom',3500)
var emp2 = new Emp('Jerry',4500)
// 扩展单个对象的成员
emp1.hireDate="2023-09-18"
console.log(emp1.toString()+":"+emp1.hireDate) //[object Object]:2023-09-18
console.log(emp2.toString()+":"+emp2.hireDate) //[object Object]:undefined
扩展共享值
function Emp(ename,salary,hiredate){
this.ename=ename
this.salary=salary
}
var emp1 = new Emp('Tom',3500)
var emp2 = new Emp('Jerry',4500)
Emp.prototype.hireDate="2015/05/01"
console.log(emp1.toString()+":"+emp1.hireDate) //[object Object]:2015/05/01
console.log(emp2.toString()+":"+emp2.hireDate) //[object Object]:2015/05/01
可以使用delete关键字删除对象的属性
function Emp(ename,salary,hiredate){
this.ename=ename
this.salary=salary
}
var emp1 = new Emp('Tom',3500)
var emp2 = new Emp('Jerry',4500)
Emp.prototype.hireDate="2015/05/01"
console.log(emp1.toString()+":"+emp1.hireDate) //[object Object]:2015/05/01
console.log(emp2.toString()+":"+emp2.hireDate) //[object Object]:2015/05/01
//删除属性
delete emp1.name;
delete Emp.prototype.hireDate
console.log(emp1.toString()+":"+emp1.hireDate) //[object Object]:undefined
console.log(emp2.toString()+":"+emp2.hireDate) //[object Object]:undefined
自有属性:通过对象的引用提娜佳的属性;其他对象可能无此属性;即使有,也是彼此独立的属性
原型属性:从原型对象中继承的属性属性,一旦原型对象中属性值改变,所有继承自该原型的对象属性均改变
自有属性重写原型属性
function Hero(){
this.name="jsscript"
}
Hero.prototype.name="cs"
var hero=new Hero()
hero.age=18
console.log(hero.name) //jsscript
delete hero.name
console.log(hero.name) // cs
delete Hero.prototype.name
console.log(hero.name) //udnefined
// 原型链,自由属性没有,就去原型属性找,原型属性没有返回undefined
自有属性和原型属性都可以访问获取,区别方法
console.log(hero.hasOwnProperty('name'))
console.log('name' in hero)
_proto_
属性自定义对象的__proto__属性
内置对象的__proto__属性
可通过两种方式获取对象的原型,进而设置共享属性
使用构造函数prototype属性
使用专门的Object的getPrototypeOf静态方法
// 实现Array类型的isArray方法,检查任意类型对象是否是数组类型
// 使用4种方法实现
// typeof方法无法区分数组对象和普通类型的对象
var arr=[1,2,3]
var now=new Date()
var obj={}
// 判断对象的爹如果是Array.prototype,则说明这个对象是数组家的孩子
console.log(
arr.__proto__==Array.prototype
now.__proto__==Array.prototype
obj.__proto__==Array.prototype
)
console.log(
Object.getPrototypeOf(arr)==Array.prototype
Object.getPrototypeOf(now)==Array.prototype
Object.getPrototypeOf(obj)==Array.prototype
)
// 构造函数和元对象是互相引用的关系
// 判断妈妈,判断子对象强行访问父对象的constructor属性,获取构造函数时array
console.log(
arr.constructor==Array
now.constructor==Array
obj.constructor==Array
)
console.log(
arr instanceof Array
now instanceof Array
obj instanceof Array
)
// 自定义
// 向array家的构造函数添加静态方法isArray
//if(Array.isArray===undefined){
Array.isArray=function(obj){
return Object.getPrototypeOf(obj)==Array.prototype
// obj.__proto__=Array.prototype
// obj.constructor=Array
// obj instanceof Array
}
//}
// es5
console.log(
Array.isArray(arr)
Array.isArray(now)
Array.isArray(obj)
)
isPrototypeOf
该方法用于判定一个prototype对象是否存在于另一个对象的原型链种,如果是返回true,否则返回false默认的toString()方法返回的信息量很少
var emp1={
"ename":"Tom",
"salary":3500,
"hiredate":new Date(2014,0,15),
"toString":function(){
return this.ename+this.salary
}
}
console.log(emp1.ename) //Tom
console.log(emp1['hiredate']) //Wed Jan 15 2014 00:00:00 GMT+0800 (中国标准时间)
console.log(emp1.toString()) // Tom3500
优点: 对象之间不会纂改各自原型中继承来的属性和方法
function A(){
this.name="a"
this.toString=function(){return this.name}
}
function B(){
this.name="b"
}
B.prototype=new A()
var b=new B()
console.log(b.name)//b
console.log(b.toString())//b
delete b.name //删除b自身的name属性
console.log(b.name)//a
console.log(b.toString())//a
仅影响当前对象的继承关系,不影响其他的对象
语法:Object.setPrototypeOf(子对象,父级对象)
function A(){
this.name="a"
this.toString=function(){return this.name}
}
function B(){
this.name="b"
}
var a1=new A()
var a2=new A()
Object.setPrototypeOf(a2,new B())
console.log(a1.toString()) //a
console.log(a2.name)//a
delete a1.name
console.log(a1.name)//undefined
delete a2.name
// B作为了a2的原型对象,而没有作为a1的原型对象
console.log(a2.name) //b
出于效率考虑,尽可能地将可重用的属性和方法添加到原型中
function A(){}
A.prototype.name="name"
A.prototype.toString=function(){return this.name}
function B(){}
B.prototype=A.prototype
B.prototype.name="b"
var b=new B()
console.log(b.name) //b
console.log(b.toString())//b
A.prototype.name="C"
console.log(b.name) //C
console.log(b.toString())//C
// B.prototype=A.prototype的方式会造成原型对象的地址指向同一处,造成数据冗余
// ===========================================================================================
// 解决方法
function A(){}
A.prototype.name="name"
A.prototype.toString=function(){return this.name}
function B(){}
B.prototype.__proto__=A.prototype
B.prototype.name="b"
var b=new B()
console.log(b.name) //b
console.log(b.toString())//b
A.prototype.name="C"
console.log(b.name) //b
console.log(b.toString())//b
增强的对象直面量
var id=1001,ename="eric",age=19
var eric={
ename,age,["emp"+id]:id,
intr(){
console.log("l am"+this.ename)
}
}
console.log(eric) //{ename: 'eric', age: 19, emp1001: 1001, intr: ƒ}
"use strict" //class关键字必须用于严格模式
class Emp{ //声明一个类
constructor(ename){ //构造函数
this.ename=ename
}
work(){
console.log(`ENAME:${this.ename}`)
}
}
var e3=new Emp('约翰') //创建类的实例
e3.work()
class+{}
来包裹构造函数和原型对象
// es5创建对象的实例
function Emp(ename){
this.ename=ename
}
Emp.prototype.work=function(){
console.log('l am'+this.ename)
}
var e3=new Emp('约翰')
console.log(e3)
// 旧的语法不符合封装的原理,所以衍生出了es6的class方式
class Emp{
constructor(ename){
this.ename=ename
}
// 方法自动放进原型对象中
work(){
console.log('l am'+this.ename)
}
}
var e3=new Emp('月哈')
console.log(e3)
ES6中的继承通过使用class配合extends
关键字来实现
class Emp{
constructor(ename){
this.ename=ename
}
work(){
console.log('l am'+this.ename)
}
}
class Programer extends Emp{ //extends 继承类
constructor(ename,skill){
super(ename) //继承父类的属性
this.skill=skill
}
work(){
super.work() //继承父类的方法
console.log(`我会:${this.skill}`)
}
}
var p1=new Programer('汤姆','js')
p1.work()
console.log(p1)
ES6的class中也可以使用get/set访问器属性实现虚拟扩展属性
案例1
class Emp{
constructor(firstName,lastName){
this.firstName=firstName
this.lastName=lastName
}
get fullName(){
return this.firstName+" "+this.lastName
}
set fullName(v){
this.firstName=v.split(" ")[0];
this.lastName=v.split(" ")[1]
}
}
var lilei=new Emp("Li","Lei")
console.log(lilei.fullName) //Li Lei
lilei.fullName="Han Meimei"
console.log(lilei.firstName,lilei.lastName) //Han Meimei
案例2
class Emp{
constructor(ename,age){
this.ename=ename
Object.defineProperty(this,"_age",{
writable:true,
enumerable:false,
configurable:false
})
this.age=age
}
// prototype
// age访问器属性
// age访问器属性放在原型对象是因为访问器属性不存值,他其实本质上是两函数,分开写到原型对象中可以节省内存
get age(){
return this._age
}
set age(value){
if(value>=18&&value<=65){
return this._age=value
}else{
throw Error('年龄必须介于18-65之间')
}
}
// prototype
work(){
console.log(`l am ${this.ename}`)
}
}
var e1=new Emp('dema',25)
console.log(e1.age)
e1.age=11
console.log(e1.age)
class Rectangle{
constructor(name,x1,y1,x2,y2){
this.name=name
this.x1=x1
this.y1=y1
this.x2=x2
this.y2=y2
}
static defaultRectangle(){ //加static为该类添加静态成员,报错,c1.defaultRectangle is not a function
return new Rectangle('default',0,0,100,100)
}
}
var r1=Rectangle.defaultRectangle()
console.log(r1) //{name: 'default', x1: 0, x2: 100, y1: 0, y2: 100}
class Circle extends Rectangle{
constructor(name,x1,y1,x2,y2){
super(name)
this.x1=x1
this.y1=y1
this.x2=x2
this.y2=y2
}
}
var c1=new Circle('default',0,0,100,100)
c1.defaultRectangle()
console.log(c1)
script
开启严格模式为整个script
标签开启严格模式,需要在所有语句之前放一句特有语句use strict
// 严格模式下
"use script"
m=10
consol.log(m) //报错,找不到m
// 非严格模式下
m=10
console.log(m)
// 严格模式下,不允许为未声明的变量赋值;非严格模式下给未声明的变量赋值会自动在全局创建变量
注意,盲目拼合严格模式和非严格模式可能会有陷阱,建议用函数包裹要采用严格模式的代码段
没有静默失败 (静默失败:不报错也没有效果)
禁止给常量赋值
(function(){ //采用非严格模式的函数
const Pl=3.14
pl=1.14 // 静默失败
console.log(Pl)
})()
(function(){//采用严格模式的函数
"use strict"
const Pl=3.14
pl=1.14 // 报错
console.log(Pl)
})()
禁止删除不允许删除的属性
(function(){ //采用非严格模式的函数
delete Object.prototype //静默失败
console.log(Objecct.prototype)
})()
(function(){//采用严格模式的函数
"use strict"
delete Object.prototype //报错
console.log(Objecct.prototype)
})()
现在的大多数浏览器严格模式静默失败升级为错误,是在设置对象属性禁止删除之后才会报错。以上例子会不会成功取决于浏览器的版本。
严格模式下,多了一个eval作用域,专门保存eval中创建的变量,一旦eval执行结束,变量随eval作用域释放
var x=17;
eval("var x=42,console.log(x)"); //42
console.log(x) //42
var x=17
eval("'use strict';var x=42;console.log(x)")//42
console.log(x)//17
arguments中的值,不再随参数变量的值改变而改变
(function(a){ //采用非严格模式的函数
a++ ;
console.log(arguments[0])//11
})(10)
(function(a){//采用严格模式的函数
"use strict"
a++ ;
console.log(arguments[0])//10
})(a)
不允许在函数内调用arguments.callee,这意味着,无法再匿名函数实现递归调用
var f=function(n){
var fn=arguments.callee
if(n<3){
return 1
}else{
return fn(n-1)+fn(n-2)
}
}
// 用循环代替递归执行效率更快
function a(){
var f1=1,f2=1,fn
for(var i=3;i<=n;i++){
fn=f1+f2
f1=f2
f2=fn
}
return fn
}
Object.prototype.toString()
方法打印出来,打印结果的第二部分就是类名
[object Array]
[object Date]
[object Object]
每个数据属性都有四个特性(Attribute)来保护数据属性
[[Value]]
实际存储属性值的特性
[[Writable]]
表示能否修改属性的value值。默认为true,如果设置为false,则该属性只读
[[Enumerble]]
表示能否通过for in遍历循环到该属性,默认为true,如果改为false,则使用for in 遍历不到
[[Configurable]]
表示能否通过delete删除该属性或能否修改其他属性的特性,默认为true,如果改为false,则属性的其它特性一旦定义,不可修改
属性的特性无法直接访问到,只能通过特定的API访问
通过特定的API获取属性的特性 Object.getOwnPropertyDescriptor
var person={name:"huwenhao",age:20}
var descriptor=Object.getOwnPropertyDescriptor(person,"name")
console.log(descriptor) //{value: 'huwenhao', writable: true, enumerable: true, configurable: true}
// 除value外,其余特性,不写默认为true
设置单个属性的特性Object.defineProperty
Object.defineProperty(obj,"属性名",{
value:值,
writable:true/false,
....
})
// 在obj对象中添加指定"属性名"的数据属性,同时设置该属性的特性
// 如果指定"属性名"已存在于obj对象中,则直接修改原属性的特性
// Object.defineProperty()一次只能添加或设置一个属性
设置多个属性的特性:Object.defineProperties()
Object.defineProperties(obj,{
"属性名":{
value:值,
writable:true/false,
....
},
"属性名":{
value:值,
writable:true/false,
....
},
.....
})
数据属性的开关只能对自己提供最简单的方法,无法使用自定义的规则来保护自己的属性
访问器属性是专门控制对一个数据属性读写操作的特殊属性。它不包括具体属性值。而是包含一对get/set方法
今后只要保护一个数据属性的读写操作,就用访问器属性
访问器属性不能直接定义
一次定义一个数据属性通过Object.defineProperty
方法
var emp={
_id:1001,
name:"埃里克",
_eage:25
}
for(var key in emp){
console.log(`${key} in ${emp[key]}`)
}
// _id in 1001
// name in 埃里克
// _eage in 25
Object.defineProperty(emp,"_id",{
enumerable:false,
configurable:false
})
Object.defineProperty(emp,"id",{ //添加id属性
get:function(){
return this._id
},
enumerable:true,
configurable:false
})
Object.defineProperty(emp,"_eage",{
enumerable:false,
configurable:false
})
Object.defineProperty(emp,"eage",{
get:function(){
return this._eage
},
set:function(value){
if(value>=18&&value<=65){
this._eage=value
}else{
throw Error('年龄必须介于18-55之间')
}
},
enumerable:true,
configurable:false
})
for(var key in emp){
console.log(`${key} in ${emp[key]}`)
}
// name in 埃里克
// id in 1001
// eage in 25
// 尝试获得emp的id属性
console.log(emp.id) //1001
// 尝试修改emp的id属性
//emp.id=-2
//console.log(emp)
// 尝试获取emp的eage属性
console.log(emp.eage)
// 尝试修改emp的eage属性
// emp.eage=18
// console.log(emp)
// emp.eage=-1
// console.log(emp)
一次定义多个数据属性通过Object.defineProperties
方法
// 案例1
var emp={
id:1001,
name:"埃里克",
eage:25
}
Object.defineProperties(emp,{
"_id":{
value:emp.id,
enumerable:false,
configurable:false
},
"id":{
get(){
return this._id
},
enumerable:true,
configurable:false
},
"_eage":{
value:emp.eage,
enumerable:false,
configurable:false
},
"eage":{
get(){
return this._eage
},
set(value){
if(value>=18&&value<=65){
this._eage=value
}else{
throw Error('年龄必须介于18-55之间')
}
}
}
})
//操作
console.log(emp.eage)
emp.eage=18
console.log(emp.eage)
console.log(emp._eage)
访问器属性的特性
[[Get]]
读取属性时自动调用的函数,不是必须,如果不提供,表示不可读取受保护的属性值[[Set]]
写入属性值时自动调用的函数,不是必须,如果不提供Set方法,表示受保护的属性值为只读[[Enumerable]]
同数据属性的特性[[Configurable]]
同数据属性的特性Object是Object类型得构造函数,所以typeof Object返回得是Function
而Object.prototype对象才是所有对象得父对象,
Object作为构造函数,有三种情况
Object()
如果传入参数null,undefined,或者不传入,则相当于new Object(),创建有原型的空对象Object(原始类型值)
将原始类型的值转化为Object类型,也等效于new Object(原型类型值)Object(引用类型的对象)
什么都不做,还返回原对象Object类的新方法
Object.getPrototypeOf(obj)
得到obj的原型对象Object.getOwnPropertyDescriptor(obj,"属性名")
返回对象中指定属性的特性Object.getOwnPropertyNames(obj)
获取对象中所有属性名的数据Obejct.keys(obj)
仅返回可被枚举的属性名组成的数组Object.create()
用于创建一个新对象
新对象继承自指定的父级原型对象
同时又扩展出多个自有属性
语法
Object.create方法的特殊用法
创建一个原型为null的空对象:var o=Object.create(null)
实现子类型构造函数的原型继承父类型构造函数的原型: Sub.prototype=Object.create(Super.prototype)
创建普通空对象 等效于var o={}:var o=Object.create(Object.prototype)
创建对象同时扩展自有属性
var flyer={name:"A380",speed:1000}
var plane=Obejct.create(flyer,{
capacity:{
value:555,
writable:true,
enumerable:true
}
})
案例
function Flyer(fname,speed){
this.fname=fname;
this.speed=speed
}
Flyer.prototype.fly=function(){
console.log(`${this.name}以时速${this.speed}飞行`)
}
function Plane(fname,speed,capacity){
Flyer.call(this,fname,speed)
this.capacity=capacity
}
Plane.prototype=Object.create(Flyer.protoType)
var b747=new Plane('B747',700,300)
b747.fly()
console.log(b747)
访问器,特性是保护对象的数据属性,防纂改是保护对象的结构
ES3标准的Javascript中,程序员可以在任何时候,任意位置,无论有意还是无意的修改任何对象的属性
这些纂改可能会影响内置对象的内置属性和方法,从而造成内置对象的纂改,导致正常的功能无法正常执行。
es5标准为了防止程序员纂改关键对象的属性和方法,提供了三种保护方式
Object.isExtensible(obj)
获取对象是否可扩展
Object.preventExtensions(obj)
修改Extensible为false,一旦改为false,就不可能再改为true
var obj={}
console.log(Object.isExtensible(obj))//true
obj.id=1001
console.log(obj.id)
Object.preventExtensions(obj)
console.log(Object.isExtensible(obj))//false
obj.name="scrott"
console.log(obj.name) //undefiend
Object.isSealed(obj)
判断obj对象是否为密封对象
Object.seal(obj)
修改obj对象的内部属性和属性的特性,使其密封
var obj={id:1001,name:'dema'}
console.log(Object.isSealed(obj)) //false
Object.seal(obj)
console.log(Object.isSealed(obj))//true
console.log(Object.isExtensible(obj))//false
console.log(Object.getOwnPropertyDescriptor(obj,"id"))
在开发场景中,如果每一个属性都不能删除,直接给该对象设置Object.seal(对象),所以一般情况下,对象保护到密封对象阶段
不允许对对象的现有属性及其属性值做任何修改,也不允许增加新属性
冻结对象
Object.isFrozen(obj)
判断obj是否被冻结
Obejct.freeze(obj)
冻结obj对象
var obj={id:1001,name:'dema'}
console.log(Object.isFrozen(obj))//false
Object.freeze(obj)
console.log(Object.isFrozen(obj))//true
obj.age=18
obj.name="Tom"
console.log(obj)
应用环境:多个模块共用的对象,需要冻结
call
apply
bind
只改变当前一次,下次再调用该方法,仍然需要改变this的指向
让函数中的this指向call的第一个参数,后面的参数为传入原方法的值
会自动执行原方法
var obj={
a:10,
b:15
}
function fn(x,y){
console.log(x+y)
console.log(this.a+this.b)
}
fn.call(obj,2,3);
调用函数前的一个常见需求是希望可以预定this的值,或是预先为部分形式参数指定实际值
ECMAScript5中定义了一个新的函数bind,可以实现这样的需求
bind函数用于基于现有函数创建一个新函数对象,同时为新函数对象提前绑定this值或设置参数值
bind函数在调用时,需要指定this的值,同时还可以指定若干个形式参数的实际值
bind的作用
let newAdd=add.bind(obj)
【注意】
bind不会执行原方法,如果必须要使用bind执行原方法在后面加();但是bind执行原方法是伪代码,因为bind的作用是形成新方法(把this改变后的方法保留下来),所以永远不用bind去改变this指向的同时执行源代码,返回值是this改变后的新方法 所以把其赋值给新方法即可,后续调用原方法就是调用this改变后的方法
【面试题:this的指向有多少种情况】
分为两个方向 第一个方向是function声明的函数,大括号中会形成一个作用域,从而改变this的方向,谁调用这个函数,this指向谁。 第二个方向是ES6的箭头函数,this指向函数声明的位置。
function声明的函数有以下几种情况:
1.对象里的方法 obj.fn() this指向obj
2.构造函数里的this 指向正在new的对象
3.原型对象里的方法因为原型对象上的方法只有通过类实例化才能调用,所以方法中的this会指向这个类实例化的对象。
4.直接调用的fn this会指向window
5.回调函数,匿名函数的自调用都是相当于直接调用这个函数所以this都指向window
6.dom触发的事件中的匿名函数会指向触发这个事件的dom元素
7.vue组件中 this会指向vue对象
箭头函数,有如下几种情况
1.对象中的方法 this指向window 所以在对象中的方法一般不用箭头函数
2.当new的时候 this会强制性的指向这个实例化的对象,所以箭头函数对new没有影响
3.在原型对象中用箭头函数,this会指向window,所以在原型对象中一般也不写箭头函数
4.在函数自调用时,使用箭头函数 输出this就会报错,所以不能使用箭头函数
5.在回调函数中,小括号不会形成作用域,this不会发生改变,但是this所在的作用域和它外部作用域会指向同一个地方。
6.在vue中方法使用箭头函,this指向window
总结
回调函数:
在回调函数中,function形式的函数相当于直接调用,所以this指向window;箭头函数形式的函数的this和它所在作用域的外部作用域指向同一个地方
Function:
不是属于对象的方法,调用就是直接调用,那么它的this指向window,有没有改变,要看function的外层作用域指向谁。
1.不产生新的作用域,this不会改变;
2.箭头函数虽然产生了新作用域,但是this的指向也在创建时就确定了,所以是否会改变要看调用时所在的作用域和方法内部的this是否一致。
ES6中,变量的作用域分为三种
局部作用域:只能在当前函数中使用
全局作用域:可以在任意函数内使用–是全局对象的成员
块级作用域:只能在当前块内使用—es6特性
"use strict" //块级作用域需要运行于严格模式
for(let i=0;i<10;i++){
console.log(i) // 正常执行
}
console.log(i) //执行错误
//console.log(a);
//var a=1;
//undefined
//console.log(b);
//let b=2;
//不会提升 直接报错
//letb=2;
//letb=4;
//console.log(b);
//不允许重复声明,但可以重复赋值
//大括号叫做块级作用域,把let封装进去,不会造成全局污染。
{
//var c=3; //可以打印
let c=3;
console.log(c); //console在作用域以内可以打印
}
console.log(c); //console在作用域以外不能打印
// =====================================================
// 使用let可以实现闭包效果
for(let i=1,sum=0;i<=100;i++){
sum+=i
}
console.log(sum);//用let声明的变量都不是全局变量,所以在括号以外访问不到sum,所以sum不能放入循环中。
let sum=0
for(let i=1;i<=100;i++){
sum+=i
}
console.log(sum); //sum写外面就可以打印
箭头函数是匿名函数的另一种写法,不等同于匿名函数
var arr=[10.20.15.17]
arr.sort(function(a,b){
return a-b
})
↓
// 箭头函数
var arr=[10,20,15,17]
// arr,sort((a,b)=>{
return a-b
})
↓ 简化为
arr.sort((a,b)=>a-b)
console.log(arr)
var fn=function(){
}
↓ 简化为
var fn = () => {
console.log('fn')
}
fn()
箭头函数内外共用统一的this
var arr=[1,2,3]
// arr.forEach(function(elem,i,arr){
// arr[i]=elem*2
// })
arr.forEach((elem,i,arr)=>arr[i]=elem*2)
console.log(arr)
// ===================================
<button id="btn1">click me</button>
var n=0
var btn1=document.getElementById('btn1')
// btn1.οnclick=function(){
// alert(++n)
// }
btn1.onclick=()=>{
alert(++n)
}
默认值:es6中,允许参为函数的参数列表末尾的几个参数变量,预先定义默认值
Array.prototype.indexOf=function(val.formi=0){
...
}
var arr=[1,2,3,4,3,2,1]
arr.indexOf(2)//1
arr.indexOf(2,2)//5
可以给参数设置默认值
// es5的写法
function add(a,b,c){
//es5设置参数默认值
c=c||0
return a+b+c
}
console.log(add(2,4))
console.log(add(2,4,8))
// es6的写法
function add(a,b,c=0){
return a+b+c
}
add(2,4)
...
打散数组和剩余参数...
放在形参中是参数形成一个数组(剩余参数),放在实参中是将数组打散为单个值(打散数组)
剩余参数,es6中,为了代替arguments类数组对象来接收不确定个数的参数值,新增了剩余参数(rest)的特性
function calc(base,...bonus){
return bonus.reduce(
(prev,val)=>prev+val,base
)
}
calc(10000,1000,2000,3000) //16000
es6中为了代替apply,实现更灵活的打散数组类型参数的目的,提供了散播(spread)的新特性
function calc(base,b1,b2,b3){
return bonus.reduce(
(prev,val)=>prev+val,base
)
}
var bonus=[1000,2000,3000]
calc(10000,...bonus) //16000
(反引号)
来指示多行语句${}
在字符串中内嵌JavaScript变量,并使用一对(反引号)
来只是字符串,方便实现字符串的拼接es6中,为了将一个复杂结构中的成员值,分别打散到多个变量中,批量赋值,提供了结构的新特性
数组解构:按数组下标匹配每个变量和对应位置的值
var list =[1,2,3]
var [a,b,c]=list
console.log(a,b,c)
[a,c]=[c,a] //交换俩变量的值
对象解构:按对象属性米名,匹配每个变量和对象的值
var obj={x:1,y:2,z:3}
var {x:a,y:c,z:c}=obj
console.log(a,b,c) //1.2.3
参数解构:定义函数的参数列表是,也可以用解构语法,可代替apply打散数组或对象中的成员值为单个值,再分别传入
function f([name,val]){
console.log(name,val)
}
function g({name:n,val:v}){
console.log(n,v)
}
function h({name,val}){ // {name,val} 等价于 {name:name,val:val}
console.log(name,val)
}
f(['bar',42])
f({name:'foo',val:22})
f({name:'bar',val:42})
代码耦合性太强,牵一发而动全身,难以维护
大量冗余的代码相互嵌套,代码的可读性变差
为了解决回调地狱问题,ES6中新增了Promise的概念
//向某位女生发送一则表白短信
//name:女神的姓名
//onFulfilled:成功后的回调
//onRejected:失败后的回调
function sendMessage(name,onFulfilled,onRejected){
//模拟 发送表白短信
console.log(`${name},我喜欢你`)
console.log(`等待${name}的回复`)
//模拟 女神回复需要一段时间
setTimeout(()=>{
if(Math.random()<=0.1){
//成功
onFulfilled(`${name}:我也喜欢你`)
}else{
//失败
onRejected(`${name}:我有喜欢的人了`)
}
},1000)
}
sendMessage(
'李建国',
(reply)=>{
//如果成功,输出回复的消息,结束
console.log(reply)
},
(reply)=>{
//如果失败,输出回复的消息,向下一个女神发
console.log(reply)
sendMessage(
'张茜',
(reply)=>{
//如果成功,输出回复的消息,结束
console.log(reply)
},
(reply)=>{
//如果失败,输出回复的消息,向下一个女神发
console.log(reply)
sendMessage(
'张小毛',
(reply)=>{
//如果成功,输出回复的消息,结束
console.log(reply)
},
(reply)=>{
//如果失败,输出回复的消息,向下一个女神发
console.log(reply)
console.log('万年单身狗')
}
)
}
)
}
)
这套规范最早诞生于前端社区,规范名称为Promise A+
Promise A+规定:
1、所有的异步场景,都可以看作是一个异步任务,每一个异步任务,在js中应该表现为一个对象,该对象称之为promise对象,也叫做任务对象。
2、每个任务对象都应该有2个阶段(未决阶段 unsettled 、已决阶段settled),三个状态(pending挂起 rejected失败 fulfilled 完成状态)。
primise是一个构造函数
- promsie是一个容器,promsie本身不是异步的,但是容器内部往往存放一个异步操作
- promise构造函数接受一个函数作为参数,该参数分别是resolve和reject。reject和resolve两个形参接收的是函数,接收的函数由javascript引擎提供,不用自己部署
- 通过new promise构造器创建的promise对象是有状态的,状态可能是pending,resolved,rejected中的一种。新创建的promise对象的状态是pending,后续可以变为resolved或rejected,状态一旦变为resolved或rejected就不会再变了
- resolve函数调用后会将promise对象的状态从未完成变为成功,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
- rejected函数调用后会将promise对象的状态从pending(未完成)变成rejected(失败),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
- new promise()里的是主任务,resolve,reject把任务放进微任务队列中。
catch(onRejected)=.then(null,onRejected)
为什么可以一直then下去?
=》 因为 then的返回值是一个Promise对象
如果回调函数的返回值是Promise对象,则正常then下去
如果不是Promise对象 then方法就会自己创造一个空的Promise对象作为返回值以保证后续的then可以执行空的Promise对象,里面没有人为规定的成功与失败 => 执行第一个回调函数
注: then里面传的是回调函数,也就是方法体 => 不允许在then里直接执行函数
如果需要执行方法体怎么办? => 套在函数里,并且返回出来
load('1.js').then(function(){
return load('2.js')
}).then(function(){
return load('3.js')
}).then(fn)
then方法必定会返回一个新的promise,可以理解为后续处理也是一个任务
新任务的状态取决于后续处理
若没有相关的后续处理, 新任务的状态和前任务一致,数据为前任务的数据
new Promise((resolve,reject)=>{
resolve(1)
})
.then((res)=>{
console.log(res)//1
return 2
})
//若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据
.catch((err)=>{
return 3
})
.then((res)=>{
console.log(res) //2
})
若前任务还没有完成,新任务挂起
const pro1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},1000)
})
const pro2=pro1.then((data)=>{
console.log(data) //1
return data+1
})
const pro3=pro2.then((data)=>{
console.log(data) //2
})
console.log(pro1,pro2,pro3) //看pro2和pro3的状态和值要用到定时器
若后续处理了,则根据后续处理的情况确定新任务的状态
// 后续处理无错,新任务的状态为完成,数据为后续处理的返回值
new Promise((resolve,reject)=>{
resolve(1)
})
.then((res)=>{
console.log(res)//1
return 2
})
// 后续处理执行有错,新任务的状态为失败,数据为异常对象
new Promise((resolve,reject)=>{
resolve()
})
.then((res)=>{
console.log(res.toString())
return 2
})
.catch(err=>{
return 3
})
.then((res)=>{
console.log(res) //3
})
// pro1 fulfilled undefined
// pro2 rejected xxx报错
// pro3 fulfilled 3
// pro4 fulfilled 3
// 后续执行后返回的是一个任务对象,新任务的状态和数据与任务对象一致
【20230104总结】:首先then必然会返回一个promise,要想任务继续下去,必然要根据上一个promise的状态进行处理。如果没有处理,那数据和状态和上一个任务的promise一样。catch的promise状态只为fulfilled,如果上一个任务是成功,拿不到值,里面代码不执行,如果上一个任务失败,拿到值,代码执行。
promsie是一个构造函数,为我们提供了静态的方法
const pro=Promise.resolve(1);
类似于
const pro1=new Promise((resolve)=>{
resolve(1)
})
const pro3=Promise.reject(1);
类似于
const pro4=new Promise((resolve,reject)=>{
reject(1)
})
const pro5=Promise.resolve()
const pro6=Promise.reject()
const pro7=Promise.resolve()
const proo=Promise.all([pro5,pro7])//完成后的数据会变成一个数组
proo.then((result)=>{
console.log(result)
})
const proo2=Promise.all([pro5,pro6,pro7])//失败后返回第一个失败的数据
const pro8=Promise.resolve(1)
const pro9=Promise.reject(2)
const pro10=Promise.resolve(3)
const proo3=Promise.any([pro8,pro9,pro10])
setTimeout(()=>{
console.log(proo3)
},1000)
[
{status:'fulfilled',value:1},
{status:'rejected',reason:2},
{status:‘fulfilled’,value:3}
]
const pro11=Promise.resolve(1)
const pro12=Promise.reject(2)
const pro13=Promise.resolve(3)
const proo4=Promise.race([pro11,pro12,pro13])
setTimeout(()=>{
console.log(proo4)
},1000)
示例:如何把非promise对象变成promise对象?
var a = 5
Promise.resolve(a).then((res)=>{
console.log(res)
})
var p1 =Promise.resolve(1)
var p2 =Promise.reject(2)
var p3 =Promise.reject(3)
all()
Promise.all([p1,p2,p3]).then((res)=>{
console.log(res)
},(res)=>{
console.log('失败了')
console.log(res)
})
//失败了 3
// all的参数是一个数组,存放一系列Promise对象
// 只有当这些对象都成功时(用resolve传递数据),all才是成功
// 执行后续then里面的第一个回调函数 => 传递的数据是一个数组,
// 存放所有Promise对象用resolve方法传力过来的数据
// 如果有失败的Promise对象(用reject传递数据),all就是失败
// 执行后续then里面的第二个回调函数 => 传递过来的数据只有第一个失败的数据
// 当遇到 必须所有数据都回去成功才执行下一步 的需求时 就用all
var p1 =Promise.reject(1)
var p2 =Promise.reject(2)
var p3 =Promise.reject(3)
race()
Promise.race([p1,p2,p3]).then((res)=>{
console.log(res)
},(res)=>{
console.log('失败了')
console.log(res)
})
// race方法
// 注: resolve/reject 都是传递数据的方法=> 不管传递过来的数据是哪以中方法都会执行后续的then
// race到底是成功还是失败?
// 要看第一个传递过来的数据是resolve 还是 reject
// 如果是resolve 就是成功的 => 执行then里面第一个方法
// 如果是reject 就是失败的 => 执行then里面第二个方法
async会将其后的函数的返回值封装成一个promise对象,而await会等待这个promise完成,并将其resolve的结果返回出来。(promsie的特点–无等待),所以在没有await的情况下执行async函数,他会立即执行,返回一个promsie对象,并且,绝不会阻塞后面的语句。
await等待的是一个表达式,这个表达式的计算结果是promise对象或者其他值,async函数返回一个promise对象,所以await可以用于等待一个async函数的返回值
await表达式的运算结果取决于它等的东西
**总结:**async会将其后的函数的返回值封装成一个Promise对象,而await会等待这个Promise完成,并将resolve结果返回出来
区别主要在于按顺序调用多个异步函数时的写法和报错获取
promise方式
ajax().then(func1).then(func2).then(func3).then(func4)
await/async的方式
async function demo(){
await res=ajax()
await res=fun1(res)
await res=fun2(res)
await res=fun3(res)
await res=fun4(res)
}
报错
1. promise使用.catch抓取错误
2. awit/async 使用try catch的方式抓取错误
总结
当遇到多个异步函数时,1.promise方法需要很多then,会导致代码不易读,且结构复杂;2. await/async方式让异步代码的格式 与 同步代码一样,更易读。
promise本身是同步的立即执行函数,当在executor中执行resolve或者reject的时候,此时是异步的操作,会先执行then/catch,当主栈完成后,才会去调用resolve/reject中存放的方法执行。
console.log('script start')
let promise1=new Promise(function(){
console.log('promise1')
resolve()
console.log('promsie1 end')
}).then(function(){
console.log('promise2')
})
setTimeout(function(){
console.log('setTimeout')
})
console.log('script end')
执行顺序
script start-->promsie1-->promise1 end-->script end-->promise2-->setTimeout
async 函数返回一个Promise对象,当函数执行的时候,一旦遇到await就会先返回,等到触发的同步操作完成,再执行函数体内后面的语句。可以理解为是让出出了线程,跳出了async函数体
async function async1(){
console.log('async1 start')
await async2()
consol.log('async1 end')
}
async fucntion async2(){
console.log('async2')
}
console.log('script start')
async1()
console.log(script end)
输出顺序:script start-->sync1 start-->async2-->script end-->async1 end
let s=Symbol()
let x=Symbol()
当网络通信时采用TCP协议时,在真正的读写操作之前。server和client之间必须创建一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需要4次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的。按照通信方式可以大致分为两类
模拟一下短连接的情况,client向server发起连接请求,server接到请求,然后双方建立连接。client向server发送信息,server回应client,然后一次读写就完成了,不过一般都是client先发close操作,为什么呢,一般的server不会回复完client后立即关闭连接 的。
短连接的优点:存在的连接都是有用的连接,提高服务端资源利用率
在模拟一下长连接的情况,client向server发起连接,server接受client连接,双方建立连接。client和Server完成一次读写之后,他们之间的连接不会主动关闭,后续的读写操作会继续使用这个连接
长连接的特点:实现客户端服务端实时交互,但浪费资源
WebSocket是一种在浏览器端进行Socket通信的一组用户编程接口。WebSocket对象提供了用于创建和管理WebSOcket连接,以及可以通过该连接发送和接收数据的API。可以基于WebSocket实现客户端服务端长连接通信。
WebSocket特点有以下三点
Socket.io是一个为浏览器与服务器之间提供实时,双向的,基于事件网络通信库。Socket.io实现了WebSocket协议,抹平一些技术细节及兼容性
Socket.io包括
一般websocket应用都会嵌在一个webserver应用内部,这个webserver应用除了提供普通的http请求之外,还提供了websocket服务,所以需要基于exoress与socket.io搭建应用基本架构。
服务端构建websocket服务(node)
安装node
安装socket.io
新建public目录并设置为静态资源托管目录
```javascript
const express=require('express')
var app=express()
//静态资源托管
app.use(express.static(__dirname+'/public'))
app.listen(8080,()=>{
console.log('server is running')
})
```
建立WebSocket连接需要客户端与服务端基于socket.io提供的API来实现
客户端
<script src="scripts/socket.io.js"></script>
<script>
// 创建连接服务器的websocket
</script>
服务端
const express=require('express')
var app=express()
//将express模块注入到http当中,这样,http就能使用express的功能
var http=require('http').createServer(app)
var serversocket=require('socket.io')(http)
// 与客户端构建websocket长连接
serversocket.on('connection',(client)=>{
console.log(client.id)
})
http.listen(1000,()=>{
console.log('server is running ')
})
实现客户端向服务端发消息,包括客户端与服务端两部分
客户端广播事件 emit
client.emit('eventName',arguments)
//例如:
client.emit('TextMessage','hello')
服务端侦听事件 on
socket.on('eventName',arguments)
//例如
serversocket.on('connection',(client)=>{
client.on('TextMessage',(data)=>{
console.log('TextMessage:'+data)
})
})
实现服务端向客户端发消息,包括服务端与客户端两部分
服务端广播事件
serversocket.on('connection',(client)=>{
client.emit('TextMessage','hello')
})
客户端侦听事件
client=io('ws://127.0.0.1:8080')
client.on('TextMessage',(data)=>{
console.log('TexMeg:'+data)
})
实现服务端向所有客户端广播事件
serversocket.emit('hello')
// 向特定客户端发送信息:client.emit()
// 向所有客户端发送消息:serversocket.emit()
子线程特别浪费cpu资源
JavaScript采用单线程模型,也就是说所有的任务只能在一个线程上完成,一次只能做一件事。WebWorker就是为JavaScript创造多线程环境,通过主线程来创建worker线程,可以将繁重的计算工作交由worker线程来实现,此时可以减轻主线程的工作压力
想了解WebWorker的存在价值,就需要了解JavaScript浏览器事件循环机制。两者的实现技术不一样。
WebWorker的价值就是把耗时的计算操作交给WebWorker在后台异步完成,结束后给主线程(UL)线程发消息(计算结果),这样就不会阻塞主线程完成ui绘制
子线程一般用来发送请求或处理耗时的计算操作,js底层不会让子线程更新ui;子线程与主线程并发执行,子线程会把数据封装一下,放入事件队列,由主线程自发的去轮询这个事件队列,并且渲染到页面上。
同理,主线程给子线程发消息,也会产生事件队列然后子线程去轮询他并执行。
案例
创建WebWorker实例,在子线程中执行耗时计算
主线程和子线程之间完成数据的传递
// html文件中
<script>
// new一个worker对象,括号中写上文件路径
let worker=new Worker('worker.js')
// 主线程主动向子线程传递消息
worker.postMessage('Hello!!')
// 主线程接收子线程发来的消息
worker.onmessage=(res)=>{
console.log(res)
worker.postMessage('Thank you')
}
</script>
// 创建一个js文件`worker.js`作为子线程
console.log('worker...')
function fb(n){
return n<3 ? 1 : fb(n-1) + fb(n-2)
}
res = fb(43)
// 子线程接收主线程的信信息
// 主线程发送的数据会被封装成一个messageEvent消息事件,发送的数据封装在data中
this.onmessage=(res)=>{
console.log(res)
}
// 子线程主动发送消息给主线程
this.postMessage(res)
原生js中,所有的js脚本文件,都要引入到html文件中,才能运行
模块化开发使js文件之间直接可以相互引用,不需要经过第三方
引入–import
引出–export
只导出一个类型/接口 默认导出
导出多个类型/接口
在创建时就导出
例如
export const db=12
export function show(){
return 123456
}
export calss Demo(){
constructor(){}
}
/**
* 引入时
* import {db,show,Demo}
*
*
* **/
export default db
/**
*
* import xyz from 'demo.js'
*
* 相当于 let xyz=ab
* **/
核心 DOM
针对任何结构化文档的标准模型XML DOM
针对XML文档的标准模型HTML DOM
针对HTML文档的标准模型HTML DOM
当网页被加载时,浏览器会创建页面的文档对象模型hello
,元素将会成为dom树上的一个元素对象
),一个标签不等于元素,单独一个标签不会成为DOM树上的节点对象,但是,单标签元素,一个标签也可以成为元素对象。文档中的元素,属性,文本,注释都会被看作是一个节点
DOM树中每种节点都对应一种节点类型
Document
document节点对象的父类型Element
所有元素节点对象的父类型Attr
属性节点对象的类型Text
文本节点对象的类型Comment
注释节点对象的类型节点对象类型与继承关系
DOM1级定义了Node类型。Node类型是所有节点类型的父类型。所以,Node类型提用了所有节点对象共有的基本属性和方法。
除此之外,还提供了节点关系的属性以及增加,删除节点的方法
Node类型提供的公共属性和方法,多属于核心DOM的范围
nodeName
节点的名称,String类型属性
nodeType
节点类型,Number类型属性
nodeValue
节点的值,String类型属性
案例
console.log(document.body.nodeName)//'BODY'
console.log(document.body.nodeType)//1
console.log(document.body.nodeValue)//null
Node类型中定义了节点树中所有节点彼此间的关系属性
如果拿到一个元素对象,可以用节点关系进行查找其他
节点树中的节点彼此拥有层级关系,Node类型使用如下属性关联节点树上的每个节点对象
parentNode
获取父节点childNodes
获取子节点集合 类数组对象firstChild
获取第一个子节点lastChild
获取最后一个子节点每个节点对象都可使用如下方法访问平行的兄弟节点
previousSibling
获取上一个兄弟节点nextSibling
获取下一个兄弟节点案例:递归方式遍历节点树
// 遍历一个父节点下所有的后代节点
// 1.遍历父节点下的直接子节点
function getChildren(parent){
console.log(parent.nodeName)
// 获取当前父节点的直接子节点集合
var childNodes=parent.childNodes;
// 遍历直接子节点集合
for(var child of childNodes){
// 2.对每个子节点调用和当前父节点完全相同的遍历操作
getChildren(child)
}
}
//即使看不到的空格回车都是节点对象
getChildren(document.body)
document.createElement('元素名')
document.createTextNode('text')
document.createComment('container begin')
document.createDocumentFragment()
只有当同时添加多个平级子元素时,才会想到用文档片段
文档片段中可以包含其他的子节点,待到所有子节点添加完成后,再将整个文档片段添加到DOM树中,从而提高页面渲染效率,防止页面闪屏问题
var fragment=document.createDocumentFragment()
var th=docuemnt.createElement('th')
fragment.appendChild(th)
var tr=document.querySelector('table>thead>tr')
tr.appendChild(fragment)
parentNode.appendChild(childNode)
parentNode.insertBefore(newChild,existingChild)
parentNode.removeChild(childNode)
【注】子节点被删除前应接触所有绑定的事件,否则无法回收事件绑定所占的内存
parentNode.replaceChild(newNode,oldNode)
DOM操作元素的四步
flowchart LR
Node.prototype --> Element.prototype.__proto__ --> HTMLElement.prototype.__proto__ --> HTMLTableElement.prototype.__proto__
文档中的元素都是Element类型的对象
Element类型的对象,通过原型继承自Node.prototype
所以,Node类型定义的属性和方法,所有Element对象都具备
Element类型,除从Node类型继承了所有节点公共的属性和方法外,还扩展了专门操作元素节点的属性和方法
元素节点与普通节点的不同,是元素节点可以有特性(Attribute)和内容(innerHTML)
由Element类型提供的所有元素对象共有的属性和方法,也称为核心dom
getAttribute('id')
获取元素对象指定特性的值setAttribute('id','s2')
获取元素对象指定特性的值attributes()
获取元素对象所有特性对象的集合hasAttribute('id')
判断元素对象是否包含指定特性removeAttribute('id')
移除元素对象的指定特性innerHTML
获取元素对象开始标签和结束标签之间的HTML内容原文在HTML文档中,所有HTML元素对象,又都是HTMLElement类型的对象
HTMLElement类型是Element类型的子类型,所以Element类型提供的属性和方法,所有HTML元素对象都可以使用
HTMLElement类型除了继承Element类的属性和方法外,还重点扩展了简化操作HTML文档中元素对象的标准属性。
HTML文档中,每种HTML元素对应一个专门的类型
HTMLButtonElement
HTMLFormElement HTMLTableElement
![]()
HTMLImageElement
HTMLSelectElement
-
其实所有HTMLxxxElement类型,都是HTMLElement类型的子类型
-
每种HTMLxxxElement元素,除了具有HTMLElement类型公共的方法外,好扩展了自己对应的HTML元素特有的属性和方法
- HTMLImageElement 扩展了src属性
- HTMLTableElement 扩展了rows,tBodies等属性
- HTMLSelectElement 扩展了options属性等
-
凡是HTMLElement类型及其子类型提供的属性和方法,都称为HTML DOM
-
HTML DOM专门用于简化对HTML元素对象的操作
【总结】
- 核心 DOM类型,可对XML,HTML文档中所有元素节点执行增加,删除,修改,查询操作,但API 相对繁琐
- HTML DOM类型,专门针对HTML文档中的HTML元素,简化操作HTML元素对象的标准特性或子元素。
元素的内容
-
元素节点对象的innerHTML属性读取或设置元素节点中的HTML内容
-
元素节点对象的TextContent属性用于读取或设置元素节点中的文本内容–子HTML标签会被去除
-
nodeValue和TextContent
-
【节点类型】 element node 【nodeValue】null 【textContent】 所包含的所有子节点中的文本内容的组合
-
【节点类型】 text node 【nodeValue】所包含的文本内容 【textContent】 所包含的文本内容
-
案例
来自新华社的消息
var p=document.getElementById('msg')
console.log(p.nodeValue) //null
console.log(p.TextContent) //来自新华社的消息
-
【注意】有争议的innerText
- 标准的DOM操作中,并没有innerText属性
- IE8及之前的IE浏览器不支持标准的textContent属性
- 使用innerText实现类似的功能,但Firefox仍不支持此属性
- innerText与标准的textContent属性并不完全相同
- 案例
来自新华社的消息
var p=document.getElementById('msg')
console.log(p.nodeValue) //null
console.log(p.innerText) // 来自新华社的消息 / 类似于浏览器解析后的效果
console.log(p.TextContent) //来自新华社的消息 / 类似于源代码的效果
元素的属性
-
元素的属性也是一个节点对象
-
元素节点的attributes属性返回节点的属性集合,即NamesNodeMap—一个类数组对象
-
案例
<a id="bdlink" class="sitelink" href="javascript:void(0) onclick="jump()">百度搜索a>
<script>
var a=document.getElementById('bdlink')
console.log(a.attributes) //返回类数组对象 NamedNodeMap
console.log(a.attributes.length) // 4
console.log(a.attributes[1]) //class="sitelink" 属性节点
console.log(a.attributes['class']) // class="sitelink" 属性节点
script>
// 获取属性节点对象
console.log(a.attributes[3])
console.log(a.attributes["onclick"])
console.log(a.getAttributeNode("onclick"))
// 获取属性值
console.log(a.attributes[3].value)
console.log(a.attributes["onclick"].value)
console.log(a.getAttributeNode("onclick").value)
// 创建属性节点对象
var attrNode=document.createAttributes('class')
attrNode.nodeValue="hightLight"
// 实际开发中用最简方式:h1.classname=”hightlight”(不以属性节点对象方式来操纵属性值。而是可以直接强行设置属性值。而html标准属性都可用.直接修改属性值,这也是实际开发中最推崇的方式)
// 修改属性的值
console.log(a.setAttributes(name,value)) //ie8一下版本不支持
console.log(a.setAttributes(attrNode))
// 移除属性
console.log(a.removeAttribute('class'))
console.log(a.removeAttributeNode(attrNode))
// 想移除a元素的class属性
// 以节点方式
// var classNode=a.attributes['class'] //获取节点方式1
// var classNode=a.attributes[0] //获取节点方式2
// a.removeAttributeNode(classNode)//移除
// 也可以用属性名来删除
// a.removeAttributes('class')
// 是否拥有该属性
a.hasAttributes('属性名') //true 或 false
a.hasAttributes() //是否拥有该属性
-
Property and Attributes
-
特性(Attribute):在html元素开始标签中定义的HTML标准属性或自定义属性,用get/setAttribute()属性
-
属性(property):内存中的对象上定义的,用.访问的属性。
-
共同点:操作标准属性时,二者通用
-
差异:操作自定义属性时,不通用
elem.新定义属性=值; //getAttribute()访问不到
elem.setAttribute('自定义属性','值') //elem.自定义属性 访问不到
元素的样式
-
HTML中定义样式三种方法
- 内联样式:style=“xxxx:xxx;xxx:xxx”
- 内部样式表:
- 外部样式表:
-
DOM分别提供了针对的API操作各个位置定义的样式
-
- 最常用的就是访问元素的内联样式:
style特性
- HTML元素的style特性,返回一个CSSStyleDeclaration类型的对象。Style特性中的CSS样式属性,都是style对象的属性。可通过.运算符获取和设置样式属性的值
- 仅包含HTML元素中定义的内联样式
- 属性名:style对象中的属性名都是将CSS中的样式属性名去横线,变驼峰后的结果,比如
- CSS中:background-color,list-style-type
- style对象中:backgroundColor,listStyleType
- 获取或设置元素的内联样式
- 获取:
elem.style.属性名
所有属性均返回字符串类型
- 设置:
elem.style.属性名="值"
;值也要是字符串
- 强调:在浏览器标准模式下。带单位的属性设置时,必须加单位。取值时,也会返回带单位的值
- style只代表内联样式 所以element.style.属性名只能获取元素的内联样式
-
- 获取计算后的样式
getComputedStyle(元素).属性名
var style=getComputedStyle(h1);
console.log(style.fontSize)
style.fontSize="15px";//报错 只能通过style.属性名的方式,设置内联
样式,利用优先级高的特点,覆盖其他样式表中的样式
// 修改样式一律选择内联样式 元素.style.属性名
// 获取样式一律选择计算后的样式 getComputedStyle(元素).属性名
-
- 操作内部和外部的样式表中的样式属性
- 先获取包含属性的样式表对象:CSSStyleSheet
var sheet=document.styleSheets[i]
- 获取样式表中所有规则的集合:
var rules=sheet.cssRules || sheet.rules
- 获取规则集合中包含目标属性的规则:CSSRule
var rule=rules[i]
- 获取或设置规则中的目标属性
rule.style.属性名
(此修改将影响所有使用该规则的元素,所以务必谨慎)
20.5.2、选取元素
- 通过HTML查找
- 通过CSS选取元素
- 其他选取
通过HTML选取元素
-
id 选取元素,私人定制,只能查找某一个元素
document.getElementById('id')
- 可用于在当前DOM树中根据ID选取某一个子元素
-
通过标签名选取元素
-
node.getElementsByTagName('标签名')
-
可以根据标签标签名返回所有具有指定标签名的元素集合
-
案例
var ul=document.getElementById('menuList')
var list=ul.getElementsByTagName('li')
console.log(typeof list) // 类数组对象 不但找子元素还找所有后代元素
console.log(list.length)
-
通过name属性选取元素
-
document.getElementsByName('name属性值')
-
可以返回DOM数中具有指定name属性值的所有子元素集合
-
案例
<form>
<input type="checkbox" name="hobby">
<input type="checkbox" name="hobby">
<input type="checkbox" name="hobby">
</form>
var list=document.getElementsByName('hobby')
console.log(typeof list)
console.log(list.length)
通过css选取器选取元素
-
按类名选取
node.getElementsByClassName('className')
- HTML5中新添了一个可以根据class名称选取元素的方法
-
按选择器选取
node.querySelector('selector')
// 返回第一个匹配的
node.querySelectorAll('selector')
//返回全部匹配的类数组集合
其他选取
-
document.all
是一个类数组对象,是页面中所有元素的一个集合,具体用法
- document.all[index]
- document.all[‘id’]
- document.all.id
- document.all[‘name’]
- document.all.tags[‘tagName’]
【注】通过document.all检索一个元素,已经被getElementById(),getElementsByTagName()以及getElementsByName()等方法所取代
document.documentElement
对象是整个HTML文档的根元素(即元素)的引用
document.head
对象是HTML文档中元素的引用
document.body
对象是HTML文档中元素的引用
20.6、常用HTMLxxxxElement类型的对象
-
HTML Image对象
- Image对象代表嵌入的图像
![]()
标签每出现一次,一个Image对象就会被创建
- 也可以使用new Image()创建一个新对象
- 常用属性
- src
- height
- width
-
HTML Table对象
-
Table对象
- 代表一个HTML表格
标签表示一个Table对象
- 常用属性
rows
行
var table=document.getElementById('t1')
console.log(table.rows.length)
- 常用方法
insertRow(index)
返回TableRow对象
deleteRow(index)
-
TableRow对象(行)
- TableRow对象代表一个HTML表格行
标签表示一个TableRow对象
- 常用属性
cells
innerHTML
rowIndex
- 常用方法
-
insertCell(index)
返回TableCell对象
-
deleteCell(index)
var table=document.getElementById('t1')
console.log(table.rows[0].cells.length)//2
console.log(table.rows[1].rowIndex) //1
-
TableCell对象(单元格)
-
TableCell对象代表一个HTML表格单元格
标签表示一个TableCell对象
-
常用属性
cellIndex
innerHTML
colSpan
rowSpan
var table=document.getElementById('t1')
console.log(table.rows[0].cells[0].rowSpan)
console.log(table.rows[1].cells[0].innerHTML)
-
HTML Select对象
-
Select对象
- 代表HTML表单的一个下拉列表
标签即表示一个Select对象
- 常用属性
options
selectedIndex
size
- 常用方法
add(option)
remove(index)
- 事件
-
onchange
<select id="sel1" onchange="showInfo()">
<option value="11">aa</option>
<option value="22">bb</option>
</select>
-
Option对象
-
代表HTML表单中下拉列表中的一个选项
-
标签表示一个Option对象
-
创建对象
var o=new Option(text,value)
-
常用属性
index
text
value
selected
function showInfo(){
var selObj=document.getElementById('sel1')
var i=selObj.selectedIndex
console.log(selObj.options[i].value)
}
-
HTML Form对象
- Form对象代表一个HTML表单
- 在HTML文档中每出现一次,Form对象就会被创建
- 常用属性
action
method
enctype
length
表单中的元素数目
elements[]
包含表单中所有元素的数组
- 常用方法
reset()
submit()
提交表单
-
HTML Input对象
- 常用表单标签与HTMLDOM对象有如下对应关系
- 【html标签】 【HTML DOM对象】 Text 【常用成员】name/value blur()/focus()/select()
- 【html标签】 【HTML DOM对象】 Password 【常用成员】name/value blur()/focus()/select()
- 【html标签】 【HTML DOM对象】 Checkbox 【常用成员】name/value/checked blur()/focus()/click()
- 【html标签】 【HTML DOM对象】 Radio 【常用成员】name/value/checked blur()/focus()/click()
- 【html标签】 【HTML DOM对象】 Textarea 【常用成员】name/value blur()/focus()/click()
- 【html标签】 【HTML DOM对象】 Submit 【常用成员】name/value/checked blur()/focus()/click()
20.7、BOM概述
-
BOM:Browser Object Model,浏览器对象模型,用来访问和操纵浏览器窗口,使JS有能力与浏览器交互。
- 通过使用BOM,可移动窗口,更改状态栏文本,执行其他不与页面内容发生直接联系的操作。
- 没有相关标准,但被广泛支持
-
DOM:Document Object Model,文档对象模型,用来操作当前HTML文档的内容
- 定义了访问和操作HTML文档的标准方法
- 通过对DOM树的操作,实现对HTML文档数据的操作
- W3C制定了相关的标准
-
BOM模型主要包括如下的对象
- 【对象名】window 【含义】 表示浏览器中打开的窗口
- 【对象名】navigator 【含义】 包含有关浏览器的信息
- 【对象名】screen 【含义】 包含有关客户端显示屏幕的信息
- 【对象名】history 【含义】 包含用户(在浏览器窗口中)访问过的URL
- 【对象名】location 【含义】 包含有关当前URL的信息
- 【对象名】document 【含义】 包含当前浏览器加载的文档信息
- 【对象名】event 【含义】 包含当前所触发的事件对象
20.7.1、Window大对象
-
window对象表示浏览器打开的窗口
-
如果文档包含框架(frame 或 iframe框架),浏览器会为HTML文档创建一个window对象,并为每个框架创建一个额外的window对象
-
window对象是BOM的根对象,其他对象其实都是window对象的属性,window对象的属性和方法都可以省略"window.",如window.document可以简写为document,window.alert()可以简写为alert()
-
window对象常见属性
- defaultStatus 设置或返回窗口状态栏中的默认文本
- innerheight 返回窗口文档显示区的高度
- innerwidth 返回窗口文档显示区的宽度
- length 设置或返回窗口中的框架数量
- name 设置或返回窗口的名称
- opener 返回对创建此窗口的窗口的引用
- outerheight 返回窗口的外部高度
- outerwidth 返回窗口的外部宽度
- pageXOffset 设置或返回当前页面相对于窗口显示区左上角的x位置
- pageYOffset 设置或返回当前页面相对于窗口显示区左上角的y位置
- parent 返回父窗口
- self 返回对当前窗口的引用
- status 设置窗口状态栏的文本
- top 返回最顶部
-
窗口的打开和关闭
-
可使用如下方法关闭一个窗口
- close() 关闭当前窗口
- self.close() 关闭当前窗口
- xxWindow.close() 关闭指定窗口
-
可使用如下方法打开一个新窗口
var newWin=window.open(URL,name,features,replace)
- URL 可选,声明了要显示文档的URL
- name 可选,声明了新窗口的名称,这个名称可以用作标记和的属性target的值
- features 可选,声明了新窗口要显示标准浏览器的特征
- replace 可选,true - URL 替换浏览历史中的当前条目;false - URL在浏览历史创建新的条目
-
窗口的特性(兼容性差)
- channelnode=yes|no|1|0 是否使用剧院模式显示窗口
- directories=yes|no|1|0 是否添加目录按钮,默认为yes
- fullscreen=yes|no|1|0 是否使用全屏模式显示浏览器,默认是no。处于全屏模式的窗口必须同时处于剧院模式
- height=pixels 窗口文档显示区的高度。以像素计
- left=pixels 窗口的x坐标,以像素计
- location=yes|no|1|0 是否显示地址字段,默认为yes
- menubar=yes|no|1|0 是否显示菜单栏,默认为yes
- resizable=yes|no|1|0 窗口是否可调节尺寸,默认为yes
- scrollbars=yes|no|1|0 是否显示滚动条,默认为yes
- status=yes|no|1|0 是否添加状态栏,默认为yes
- titlebar=yes|no|1|0 是否显示标题栏,默认是yes
- toolbar=yes|no|1|0 是否显示浏览器的工具栏,默认为yes
- top=pixels 窗口的y坐标
-
浏览器规定每次打开一个新窗口都有一个唯一的name属性值,target使用自定义值,实际是将自定义值赋值给name属性,因为name属性值唯一,所以最多只能再打开一个窗口。
-
Target='_blank’可以开多个,是因为blank是不指定窗口名,浏览器随机分配窗口名
-
window.open(‘url’)
默认是_blank 打开多个新的窗口
-
window.open(‘url’,’_self’)
在自身打开窗口
-
window.open(‘baidu’)
打开一个新的窗口
-
窗口的大小和定位
-
窗口的大小
- window.innerHeight/Width 浏览器窗口的可见区域
- window.outerHeight/Width 浏览器窗口的外边框整体区域
- screen.height/width 显示器的完整分辨率
- screen.availHeight/Width 显示器去除任务栏的剩余分辨率
- 调整大小
- resizeTo(newWidth,newHeight)
- resizeBy(changeWidth,changeHeight)
-
窗口的定位(获取窗口位置的坐标)
- 左上角x坐标 window.screenLeft||window.screenX
- 左上角y坐标 window.screenTop||window.screenY
- 事件发生时,鼠标距离显示器左上角的坐标 event.screenX/Y
-
移动窗口位置
- window.moveTo(newX,newY)
- window.moveBy(changeX,changeY)
-
对话框(window共提供了三种对话框)
- window.alert(msg) 弹出一个警告框
- window.prompt(msg,[value]) 弹出一个输入提示框
- window.confirm(msg) 弹出一个确认框
-
定时器
- 多用于网页动态时钟,制作倒计时,跑马灯效果等
- 周期性时钟:以一定的间隔执行代码,循环往复
- 一次性时钟:在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行
-
周期性定时器
-
setInterval(exp,time) 周期性触发代码exp
- exp:执行语句,回调函数或字符串
- time:时间周期,单位为毫秒
- 返回已经启动的定时器对象
-
clearInterval(tID) 停止启动的定时器
- tID:启动的定时器对象
-
案例
window.setInterval('alert('hello');',3000)
// 或者
window.setInterval(func,3000)
function func(){
alert('hello')
}
-
一次性定时器
-
setTimeout(exp,time) 一次性触发代码exp
- exp:执行语句,回调函数或字符串
- time:间隔时间,单位为毫秒
- 返回已经启动的定时器对象
-
clearInterval(tID) 停止启动的定时器
- tID:启动的定时器对象
-
案例
window.setTimeout('alert('hello');',3000)
// 或者
window.setTimeout(func,3000)
function func(){
alert('hello')
}
20.7.2、navigator对象
-
navigator 对象包含有关浏览器的信息
-
常用于获取浏览器和操作系统信息
for(var attr in navigator){
console.log(attr + ":" + navigator[attr])
}
-
常用属性和方法
- appCodeName 返回浏览器的代码名 例如:‘Mozilla’
- appMinorVersion 返回浏览器的次级版本
- appName 返回浏览器的名称 例如:‘Netscape’
- appVersion 浏览器的平台和版本的信息 例如:'5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
- browserLanguage 返回当前浏览器的语言
- cookieEnabled 返回当前浏览器中是否启用cookie的布尔值 例如:true
- cpuClass 返回浏览器系统的CPU等级
- onLine 返回指明系统是否处于脱机模式的布尔值
- platform 返回运行浏览器的操作系统平台 例如:‘Win32’
- systemLanguage 返回OS使用的默认语言
- userAgent 返回由客户机发送服务端的user-agent头部的值 例如 ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36’
- userLanguage 返回OS的自然语言设置
- plugin
20.7.3、location对象
-
location 对象包含有关当前URL的信息
-
常用于获取和改变当前浏览的网址
-
案例
//获取当前显示的页面URL
var url=location.href
console.log(url)
//更改要显示的页面URL--页面跳转
location.href="http://www,baidu.com"
-
常用属性和方法
- hash 设置或返回从井号(#)开始的URL(锚)
- host 设置或返回主机名和当前url的端口号
- hostname 设置或返回当前URL的主机名
- href 设置或跳转完整的 URL
- pathname 设置或返回当前URL的路径部分
- port 设置或返回当前URL的端口号
- protocol 设置或返回当前URL的协议
- search 设置或返回从问号(?)开始的URL(查询部分)
- assign() 加载新的文档
- reload() 重新加载当前文档
- replace() 用新的文档替代当前文档
20.7.4、history对象
- history对象包含用户(在浏览器窗口中)访问过的URL的历史记录
- 打开一个新网页location.href(),因为它是以push()方式放入history中,所以history可以后退和前进,用location.replace()会将history中的地址替换成新的地址,所以history不会前进和后退
- 常用属性和方法
-
length 返回浏览器历史列表中URL数量
-
back() 加载history列表中的前一个URL
-
forward() 加载history列表中下一个URL
-
go() 加载history列表中的某个具体页面
<a href="javascript:history.go(-1)">后退一次</a>
history.go(1) 前进
history.go(0) 刷新
history.go(-1) 后退
20.7.5、screen对象
- Screen 对象包含有关客户端显示屏幕的信息
- 常用于获取屏幕的分辨率和色彩
- JavaScript将利用这些信息来优化它们的输出,以达到需要的显示要求。 例如,一个程序可以根据显示器的尺寸选择使用大图像还是使用小图像,它可以根据显示器的颜色深度选择使用16位色还是使用8位色的图形。另外,JavaScript程序还能根据有关屏幕尺寸的信息将新的浏览器窗口定位在屏幕中间
- 常用的方法和属性
- availHeight 返回显示屏幕的高度(除Windows任务栏40之外)
- availWidth 返回显示屏幕的宽度(除Windows任务栏之外)
- bufferDepth 设置或返回调色板的比特深度
- colorDepth 返回目标设备或缓冲器上的调色板的比特深度
- deviceXDPI 返回显示屏幕的每英寸水平点数
- deviceYDPI 返回显示屏幕的每英寸垂直点数
- fontSmoothingEnabled 返回用户是否在显示控制面板中启用字体平滑
- height 返回显示屏的高度
- logicalXDPI 返回显示屏幕每英寸的水平方向的常规点数
- logicalYDPI 返回显示屏幕每英寸的垂直方向的常规点数
- pixelDepth 返回显示屏幕的颜色分辨率(比特每像素)
- updateInterval 设置或返回屏幕的刷新率
- width 返回显示器屏幕的宽度
20.7.6、Event对象
DHTML对象模型
-
当用户与web页面进行某些交互时,解释器就会创建相应的event对象以描述事件信息,常见的事件有
- 用户点击页面上的某项内容
- 鼠标经过特定的元素
- 用户按下键盘上的某个按键
- 用户滚动窗口或改变窗口大小
- 页面元素加载完成或加载完成
-
1995年IE4浏览器就已经定义了自己的事件模型;而DOM模型2004年才最终确定标准的事件模型,并被其他浏览器所支持。所以事件处理需要注意兼容性的问题
事件处理函数
-
事件处理函数,指用于响应某个事件而调用的函数
-
事件处理函数的本质是对象的一个特殊属性
-
每一个元素对象都可以触发各种事件
-
每个事件均对应一个事件处理函数
-
在程序执行时,将相应的函数或语句绑定给对象的某个事件处理函数,那么一旦该对象的指定事件触发,浏览器便会自动执行该对象的事件处理函数上绑定的操作。
- 常用鼠标事件
onclick
ondbclick
onmousedown
onmouseup
onmouseover
onmouseout
- 常用键盘事件
onkeydown
onkeyup
onkeypress
- 常用状态事件
onload
onunload
onchange
onfocus
onslect
onresize
onsubmit
onreset
onerror
事件定义
-
为特定事件定义监听函数有三种方式
-
直接在HTML中定义元素的事件相关属性(此语法违反了"内容与行为相分离"的原则,应尽可能少用)
<button onclick="console.log(123)">按钮</button>
<body onload="initData()"></body>
-
在JavaScript中为元素的事件相关属性赋值 onclick
- onclick不能追加方法,因为后续的方法会覆盖原来的方法,不同的事件不起冲突
- 取消事件:d1.οnclick=null
btnObj.onclick=function(){
// ....
}
document.body.onload=initData
function initData(){
//....
}
-
高级事件处理方式,一个事件可以绑定多个监听函数
addEventListener(‘事件名’,callback,true或false)
- 匿名函数方式
d1.addEventListener(‘click’,function(){})
- 具名函数方式
function fn(){} d1.addEventListener(‘click’,fn)
- 如果后续可能会取消某个事件,则在绑定事件的时候,不要绑定匿名事件,不确定是否取消的事件,也不要绑定匿名事件。取消事件:
d1.removeEventListener(‘click’,fn)
【为什么不是fn() 因为绑定的是方法名或者匿名函数,而不是执行方法】
btnObj.attachEvent('onclick',function(){}) //IE
btnObj.addEventListener('click',function(){}) //DOM
document.body.attachEvent('onload',initData) //IE
document.body.addEventListener('load',initData) //DOM
function initData(){
// .....
}
// 例如
btnShoot.onclick=function(){
// alert('疼')
console.log('发送普通子弹')
}
btnShoot.addEventListener('click',()=>{
alert('发送跟踪导弹')
})
事件触发产生的event对象以及其属性
- 任何事件触发后将会产生一个event对象
- event对象记录事件发生时的鼠标位置,键盘按键状态和触发对象等信息
- 事件对象
e/event/$event(vue)
的常用属性
-
srcElement(IE)/target(FireFox) 事件源对象
-
eventPhase 事件所处的传播阶段
-
clientX/offsetX/pageX/screenX/x 事件发y生的X坐标
-
clientY/offsetY/pageY/screenY/ 事件发生的X坐标
-
which/keyCode/charNdoe 键盘事件中按下的按键
-
button 鼠标哪个按键被按下了
-
cancelBubble 是否取消事件冒泡 e.cancalBubble=true ie浏览器写法
-
e.stopPropagation() 阻止事件向外冒泡 不阻止事件的执行
-
e.preventDefault 阻止默认事件 类似于超链接的跳转等事件
// e 为event对象
document.body.onclick=function(e){
alert(`点在body的x:${e.clientX},y:${e.clientY}位置`)
// 或
alert(`点在body的x:${event.clientX},y:${event.clientY}位置`)
}
// 兼容性写法
a1.onclick=function(e){
e=e||window.event
if(typeof e.stopPropagation === "function"){
e.stopPropagation()
}else{
e.cancelBubble=true //ie浏览器写法
}
alert('d1 到!')
}
- 对于event对象,经常需要获取事件源—目标对象
- 目标对象:始终保存最初触发事件的节点对象
- this:指代触发事件的当前节点对象,随事件冒泡而改变
- IE:event.srcElement ; Firefox:event.target
<div onclick="func(event)">div textdiv>
function func(e){
var src=e.srcElement
}
function func(e){
var src=e.target
}
事件坐标
-
event对象记录事件发生时的鼠标位置
- 相对于浏览器显示器坐标位置:clientX/x clientY/y
- 相当于网页左上角坐标位置:pageX pageY IE8不支持
- 没滚动时,以上两组值相等
- 相对于屏幕坐标位置:screenX,screenY
- 相对于目标元素:offsetX,offsetY
事件周期
-
解释器创建一个event对象后,会按如下过程将其在HTML元素进行传播
- 第一阶段:事件捕获,事件对象沿DOM树向下传播
- 第二阶段:目标触发,运行事件监听函数
- 第三阶段:事件冒泡,事件沿着DOM树向上传播
-
高级事件处理方式addEventListener(‘事件名’,callback,true或false)
第三个参数可以修改事件触发的顺序
- 默认为false,触发最内层元素事件时,外层事件的事件也会被触发。顺序由内到外,这个现象叫事件冒泡
- 为true,顺序由外到内,这个现象叫做事件捕获
-
【扩展】如果事件冒泡和事件捕获混在一起是什么顺序?
-
一套结构中的同一个事件分两个过程,先执行事件捕获中的事件(第三个参数是true,顺序由外向内,再执行事件冒泡中的事件(第三个参数是false)
<div id="d1">
<div id="d2">
<div id="d3">
div>
div>
div>
d1.addEventListener(
"click",
function(){},
true
)
事件委托
- 事件委托,只在父元素上绑定一次事件处理函数,所有子元素通过冒泡机制来触发父元素上公共的事件处理函数
- 普通事件绑定,this–>当前正在触发事件的目标元素
- 事件委托后,事件处理函数是在父元素上执行的,所以this不再指向实际点击的子元素目标元素,而是指向父元素div。所以,用this就无法获得父元素
-
今后只要用事件委托,必须用e.target代替this,来获取目标元素。
-
必须判断当前元素是否是想要的元素,只有当前元素是想要的元素,才继续触发后续的操作if(target.nodeName==""){}
event.target
从当前元素的标签结构到最内层元素的标签结构
event.target.tagName
获取标签名
event.target.innerHTML
获取标签的内容
取消事件
-
通常浏览器在事件传递并处理完后可能会执行与该事件关联的默认动作。例如
- 如果表单中input type 属性是"submit",点击后会自动提交表单
-
但事件处理函数过程中发生了错误,会不希望继续执行默认行为时,都可以取消事件或阻止默认行为继续执行
-
可采用下述方法阻止默认行为
if(event.preventDefault){
event.preventDefault() //DOM
}else{
event.returnValue=false //ie
}
-
如果使用HTML绑定事件处理函数,可使用两个return的方式取消事件
<form onsubmit="return valiAll()">
//-------------------------------------------------------
// 取消事件:2个return
function vailAll(){
return true/false
}
事件的内存与性能
-
页面中绑定的事件处理函数数量,直接影响页面的性能
- 解决:利用冒泡,如果多个子元素绑定了相同的事件处理函数,则仅需要在父元素统一定义一次即可
-
HTML中每个元素上绑定的事件处理函数都是一个从元素到代码位置的连接对象,移除元素,连接对象不释放
- 解决,在使用核心DOM移除或替换元素节点前,先移除元素对象上绑定的事件
-
用户可能频繁刷新页面,重复的在页面间跳来跳去。而页面卸载或刷新时,浏览器窗口中的事件连接对象不会释放,会越积越多
- 解决,在页面的onunload事件处理程序中,先接触页面中所有元素上绑定的事件,在卸载页面
二十一 、前端的优化方案
主要优化方案
-
减少请求次数和请求大小
-
代码优化,优化目标
- 利用SEO
- 利于拓展维护
- 提高性能
-
DNS及HTTP通信方式的优化
详细方案
-
尽量减少闭包的使用
-
进行js和css文件的合并,减少http请求次数,进行可能讲文件压缩,减少请求大小。
- webpack工具会自动实现这种操作
- 移动端开发过程中,代码量不多,则直接合并html css js 到一个文件中书写
-
使用字体图标和svg图标,代替传统的png格式
-
减少DOM操作:主要减少DOM的重绘和重排
-
js避免嵌套循环
-
采用图片懒加载,加快页面启动速度
- 加快页面时先不加载图片,使用一张背景图占位,等页面加载完毕后,再加载图片
-
利用浏览器和服务器的缓存技术(304缓存),把一些不经常变更的资源进行缓存,从而减少带宽,减少延迟,降低网络负荷,缩短网络请求的距离,例如缓存js和css文件等静态资源
-
尽可能用事件委托来处理绑定操作,减少DOM的频繁操作
- 事件委托:为父元素添加事件,利用冒泡机制,让父元素处理所有子元素的事件。
-
减少css表达式的使用
-
较少css标签选择器的使用
-
css 雪碧图技术
-
避免重定向(301:资源永久转移 302:暂时转移)
-
减少cookie的使用
-
页面数据获取方式: 采用异步和延迟分批加载
-
页面出现 音视频 标签,让这些资源懒加载
- 方案:只需设置
preload="none"
页面加载时就会开始加载
-
数据尽可能使用json格式传递,因为此格式比xml 小
-
进行js封装,尽量复用代码,减少代码冗余
-
css中设置定位后,最好使用z-index 改变层级,让盒子不在一个平面
-
css中尽量减少filter属性滤镜的使用
-
css的导入尽量减少@import 操作,此操作时同步的,而link是异步的
-
避免使用iframe
-
开启服务端的gzip压缩
你可能感兴趣的:(js,javascript,学习,开发语言)