基础开篇 书写格式 和css一样,有三种书写格式,行内式、内嵌式、外链式
1 2 3 4 5 6 7 8 9 10 11 <div onclick ="alert('hello world');" > 我是div</div > <head > <script > window .onload = function ( ) { alert("hello world" ); } </script > </head > <script type ="text/javascript" src ="01-js书写格式.js" > </script >
通常将js代码放在body的最后, 因为HTML是从上至下加载, 而js代码通常是给标签添加交互(操作元素), 所以需要先加载HTML, 否则如果执行js代码时HTML还未被加载, 那么js代码将无法添加交互(操作元素);HTML页面中出现<script>标签后,就会让页面暂停等待脚本的解析和执行。无论当前脚本是内嵌式还是外链式,页面的下载和渲染都必须停下来等待脚本的执行完成才能继续, 所以如果把js代码写在head中, 那么js代码没有执行完毕之前后续网页无法查看
注释代码
常见的输出方式 浏览器弹窗输出 1 2 3 alert("hello world" ); prompt("请输入内容:" ); confirm("你好吗?" );
在页面中显示内容 1 document .write("hello world2" );
在控制台中显示内容 1 2 3 console .log("hello world3" );console .error("错误信息" );console .warn("警告信息" );
常量和变量 常量 常量: 固定不能改变的数据
整型常量666、0b1001等各种进制整数
实型常量3.14、6.66等所有小数
布尔常量true、false
字符串常量a、”lnj”等使用单引号(‘)或双引号(“)括起来的一个或几个字符
变量 可以以变化的数据, 需要先定义后使用
定义变量
ES6之后都用let,新的方式;var num是老的定义方式。
初始化变量 第一次给变量赋值,称之为变量的初始化。
1 2 3 let num;num = 666 ; num = 888 ;
先定义后初始化
定义的同时初始化
变量默认值 JavaScript中变量没有初始化保存的是undefined
1 2 3 let num;console .log(num);
let 和 var 的区别
var 变量可以重复定义同名,后面的会覆盖前面的;let 不能,会报错
var 变量可以先使用后定义(预解析);let 不可以,不会预解析
无论是 var 还是 let 定义在 {} 外面都是全局变量;
关键字和保留字 关键字
指被赋予特殊含义的单词
关键字在开发工具中会显示特殊颜色
关键字不能用作变量名、函数名等
关键字严格区分大小写, var和Var前者是关键字, 后者不是
关键字
break
do
instanceof
typeof
case
else
new
var
catch
finally
return
void
continue
for
switch
while
default
if
throw
delete
in
try
function
this
with
debugger
false
true
null
保留字
JavaScript预留的关键字,他们虽然现在没有作为关键字,但在以后的升级版本中有可能作为关键字
保留字
class
enum
extends
super
const
export
import
implements
let
private
public
yield
interface
package
protected
static
标识符 指程序员在程序中自己起的名称,诸如: 变量名称,函数名称等
只能由26个英文字母的大小写、10个阿拉伯数字0~9、下划线_、美元符号$组成
不能以数字开头
严格区分大小写,比如test和Test是2个不同的标识符
不可以使用关键字、保留字作为标识符
JS底层保存标识符时实际上是采用的Unicode编码,所以理论上讲,所有的utf-8中含有的内容都可以作为标识符
变量的命名建议遵守***驼峰命名法***,首字母小写,第二个单词的首字母大写,例如: userName、myFirstName
数据类型 基本数据类型
String 字符串
Number 数值
Boolean 布尔值
Null 空值
Undefined 未定义
引用数据类型
数据类型和常量之间的关系,就像猫科动物和狮子老虎豹子之间的关系。
查看数据类型 格式:typeof 数据
1 2 3 console .log(typeof 123 ); var num = 10 ;console .log(typeof num);
1 2 3 4 5 var value= 10 ;var res = typeof value; console .log(typeof res);
运算符 算数运算符 加减乘除余运算符 + - * / %
1 2 3 4 5 6 7 8 9 10 11 let res1 = 1 + 1 ;let res2 = 1 - 1 ;let res3 = 2 * 2 ;let res4 = 10 / 3 ;let res5 = 10 % 3 ;console .log(res1); console .log(res2); console .log(res3); console .log(res4); console .log(res5); console .log(res5+res1*res1-1 );
😃任何值和NaN做运算都得NaN
1 2 var result = 2 + NaN ;console .log(result);
非Number类型的值进行运算时,会将这些值转换为Number然后在运算
1 2 3 4 5 6 var result = true + 1 ; console .log(result); result = true + false ; console .log(result); result = 2 + null ; console .log(result);
任何的值和字符串做加法运算,都会先转换为字符串,然后再和字符串做拼接的操作
1 2 3 4 var result = 1 + "123" ;console .log(result); result = 2 + "true" ; console .log(result);
任何的值和字符串做- * / %法运算, 都会先转换为字符串转换为Number
1 2 3 4 var result = 2 - "1" ; console .log(result); result = "2" - "1" ; console .log(result);
😃取余运算m%n, n等于0 返回NaN;m为正负结果为正负,和n正负无关
1 2 3 4 var result = 10 % 0 ;console .log(result); console .log(-5 %-2 );
正号+,对于非Number类型的值,会将先转换为Number,然后再运算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var bool = true ;var res = +bool;console .log(res); var str = "123" ;res = +str; console .log(res); var str2 = "123abc" ;res = +str2; console .log(res); var temp = null ;res = +temp; console .log(res);
负号-,可以对数字进行负号的取反
1 2 3 var num = 123 ;num = -num ;console.log(num );
赋值运算符 就是将等号右边的值存储到等号左边的变量中。优先级低于算数运算符,为右结合性(从右至左计算)
简单赋值运算符=,复杂赋值运算符 += -= *= /= %=
1 2 3 4 5 6 let res = 1 +1 ;let num1, num2;num1 num2 = 3 ;
1 2 3 4 5 let res = 5 ;res += 3 res -= 3 console .log(res);
自增自减运算符 自增运算符 ++ :可以快速的对一个变量中保存的数据进行+1操作
自增运算符 -- :可以快速的对一个变量中保存的数据进行-1操作
1 2 3 let num = 5 ;num++; console .log(num);
自增或者自减写在变量后面:变量先参与其它的运算,然后再自增或者自减
自增或者自减写在变量前面:变量先自增或者自减,然后再参与其它的运算
1 2 3 4 let num = 1 ;let res1 = num++ +1 ; let res2 = ++num +1 ; console .log(res1+res2);
注意1:自增自减运算符只能出现在变量的前后面,不能出现在常量或者表达式的前后面
注意2:企业开发中,自增自减运算符最好单独出现,不要出现在表达式中
1 2 3 4 5 6 7 8 9 let a, b;a = 10 ; b = 5 ; let res = a++ + b; let res = a + b; a++; console .log(res); console .log(a);
关系运算符 > < >= <= == != === !==
😃关系运算符只会返回true或false
😃优先级:前四个优先级高于后四个。
😃等等于 == 和 恒等于=== 的区别
等等于 == 只判断取值是否相等,不判断数据类型是否相等;恒等于=== 判断取值和数据类型是否相等。
不等于 != 和 !== 的区别一样,前者只判断取值,后者判断取值和数据类型
1 2 let res = 123 == "123" ; let res = 123 ==="123" ;
😃对于非数值进行比较时,会将其转换为数字然后在比较
1 2 3 4 console .log(1 > true ); console .log(1 > false ); console .log(1 > "0" ); console .log(1 > null );
如果符号两侧的值都是字符串时,不会将其转换为数字进行比较, 而会分别比较字符串中字符的Unicode编码
Unicode编码转换地址
1 2 3 4 5 6 7 8 9 console.log ("a" < "b" ); console.log ("ab" < "ac" );
null、undefined 、NaN比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 console .log(null == 0 ); console .log(undefined == 0 ); console .log(NaN == NaN ); var num = NaN ;console .log(isNaN (num)); console .log(null == undefined ); console .log(null === undefined ); console .log("123" == 123 ); console .log("123" === 123 );
逻辑运算符 逻辑与 && 逻辑或 || 逻辑非 !
一假则假 一真则真 真变假,假变真
😃逻辑运算符的返回值只有 true 和 false
😃优先级:逻辑与 && 高于 逻辑或 ||
1 2 let res = true || false && false ;console .log(res);
对于非Boolean类型的数值, 逻辑运算符会将其悄悄咪咪转换为Boolean类型来判断
1 2 3 4 let res1 = !0 ; let res2 = !123 ; console .log(res1); console .log(res2);
在逻辑与运算中,如果参与运算的不是布尔类型
如果条件A不成立, 则返回条件A的数值本身
如果条件A成立, 不管条件B成不成立都返回条件B数值本身
1 2 3 4 5 6 7 8 let result1 = null && 0 ; console .log(result1); let result2 = "123" && "abc" ; console .log(result2); let result3 = "123" && 0 ; console .log(result3);
在逻辑或运算中,如果参与运算的不是布尔类型
如果条件A不成立, 则不管条件B成不成立都返回条件B数值本身
如果条件A成立, 则返回条件A的数值本身
1 2 3 4 5 6 7 8 let result = null || 0 ; console .log(result); let result = "123" || "abc" ; console .log(result); let result = "123" || 0 ; console .log(result);
😃短路现象
格式:条件A && 条件B 格式:条件A || 条件B
在逻辑与运算中,由于一假则假,所以只要条件A是假,那么条件B就不会运算(即代码失效)
在逻辑或运算中,由于一真则真,所以只要条件A是真,那么条件B就不会运算(即代码失效)
1 2 3 4 5 6 7 8 9 let num = 1 ;let res = (10 > 20 ) && (++num > 0 );console .log(num); console .log(res); let num = 5 ;let res = (10 < 20 ) && (++num > 0 );console .log(num); console .log(res);
逗号运算符 逗号运算符一般用于简化代码;左结合性(从左至右运算);优先级是所有运算符中最低的
😃逗号运算符会从左至右依次取出每个表达式的值, 最后整个逗号表达式的值等于最后一个表达式的值
格式: 表达式1,表达式2,… …,表达式n;
1 2 3 4 5 6 7 8 9 10 let a, b, c, d; d = (a = 1 + 1 ,b = 3 * 4 , c = 10 / 2 ); console .log(d);
三目运算符-条件运算符 格式: 条件表达式 ? 结果A : 结果B;
当条件为真的时候,返回结果A;条件为假的时候,返回结果B
1 2 let res = (10 > 5 ) ? alert("语句1" ) : alert("语句2" ); let res = (10 < 5 ) ? alert("语句1" ) : alert("语句2" );
流程控制 选择结构:if 和 switch ;循环结构:
if结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 if (条件表达式){ 执行代码; } if (条件表达式){ 条件成立执行的代码; }else { 条件不成立执行的代码; } if (条件表达式A){ 语句块A; }else if (条件表达式B){ 语句块B; }else if (条件表达式C){ 语句块C; }else 语句块D; }
switch结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 switch (条件表达式){ case 表达式A: 语句A; break ; case 表达式B: 语句B; break ; case 表达式C: 语句C; break ; ....... default : 前面所有case 都不匹配后执行的代码; break ; } var num = 120 ;switch (120 ){ case num: console .log("120" ); break ; case 110 : console .log("110" ); break ; default : console .log("default" ); break ; }
😃能用if就用if。对区间进行判断,建议用if;对几个固定的值进行判断,建议用switch
1 2 3 4 5 6 7 8 9 10 let num = 110 ;if (num>100 ){ alert("这是大于100的数" ); }else { alert("这是不大于100的数" ); }
while循环 条件为真时,循环执行代码,直至不满足条件
1 2 3 while (条件表达式){ 条件满足执行的代码; }
1 2 3 4 5 6 7 8 9 let num = 1 ;while (num<=10 ){ alert("发射导弹" +num); ++num; }
dowhile循环 1 2 3 do { 需要重复执行的代码; }while (条件表达式)
😃while和dowhile循环是可以互换的;如果循环体中的代码无论如何都需要先执行一次,那么建议使用dowhile;其他的情况都建议使用while循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let pwd = prompt("请输入密码" );while (pwd !== "123456" ){ pwd = prompt("请输入密码" ); } alert("密码正确,欢迎回来" ); let pwd = -1 ;do { pwd = prompt("请输入密码" ); }while (pwd !== "123456" ); alert("密码正确,欢迎回来" );
for循环 1 2 3 4 5 6 7 8 for (A初始化表达式;B条件表达式;C循环后增量表达式){ D重复执行的代码; } for (let num = 1 ;num <= 10 ;num++){ consloe.log("发射子弹" + num); }
😃for循环和while循环如何选择?for循环结束之后的变量可以让外界使用也可以不让外界使用,更灵活;while循环结束之后,变量还可以被外界使用。所以能用for就用for。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let num = 1; while(num<=10){ alert("发射子弹"+num); num++; } console.log(num); //11 //for不给外界使用变量 for(let num = 1;num <= 10;num++){ consloe.log("发射子弹" + num); } console.log(num); //报错 //for给外界使用变量 let num = 1 for(;num <= 10;num++){ consloe.log("发射子弹" + num); } console.log(num); //11
三大跳转 break 关键字:可以用于switch语句和循环结构中,作用是立即结束当前的switch语句/循环结构
continue 关键字:只能用于循环结构,作用是跳过本次循环,进入下一次循环
1 2 3 4 5 6 7 let num = 1 ;while (num<10 ){ console .log("发射子弹" + num); num++; break ; }
如果是在循环嵌套结构中
break结束的是当前所在的循环结构;continue是跳过本次循环进入下一次循环
数组 数组就是专门用于存储一组数据的
Number/String/Boolean/Null/undefined 是基本数据类型
数组(Array)是引用数据类型(对象类型)
1 2 3 4 5 6 7 8 9 let 变量名称 = new Array (size);let bianliang = new Array (3 );变量名称[索引号] = 需要存储的数据 ; bianliang[0 ] = "1" ; 变量名称[索引号]; console .log(bianliang[0 ]);
数组注意点
js中数组对应的索引中没有存储数据,默认存储的是undefined
js中数组访问了数组不存在的索引,会返回 undefined
js中数组的存储空间不够时数组会自动扩容
js中数组可以存储不同类型的数据
😃创建数组的其它方式
通过构造函数创建数组
1 2 3 let 变量名称 = new Array (size);let 变量名称 = new Array (); let 变量名称 = new Array (data1,data2,...);
通过字面量创建数组-使用这个更方便
1 2 let 变量名称 = [] ; let bianliang = [data1,data2,....] ;
数组的遍历 变量.length 可以获取数组的长度
1 2 3 4 5 6 7 let bl = ["a" ,"b" ,"c" ] ;console .log(bl.length); for (let i=0 ; i<bl.length;i++){ console .log(bl[i]); }
数组的解构赋值 解构赋值是es6中新增的一种赋值方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let bl = [1 ,3 ,5 ];let a = bl[0 ]; let b = bl[1 ]; let c = bl[2 ]; let [a,b,c] = bl;let [a,b,c] = [1 ,3 ,5 ]; let [a,b] = [1 ]; let [a,b,c] = [1 ,3 ,5 ,7 ]; let [a,b,c] = [1 ,3 ,[5 ,7 ]]; let [a,b=999 ,c=777 ] = [1 ,3 ]; let [a,...b] = [1 ,3 ,5 ];
数组中的增删改查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 变量.splice(1 ,2 ,"data1" ,"data2" ) 变量.unshift("data1" ,"data2" ,"data3" ); 变量.push("data1" ,"data2" ,"data3" ); let res = bl.push("x" ,"y" );console .log(res); 变量.shift(); 变量.pop(); let res = bl.pop();console .log(res);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 let bl = ["a" ,"b" ,"c" ,"d" ];console .log(bl[1 ]);bl[1 ] = "m" ; bl.splice(1 ,2 ,"m" ,"n" ); bl.push("x" ,"y" ,"z" ); bl.unshift("o" ,"p" ); bl.pop(); bl.shift(); bl.splice(2 ,1 );
数组常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 let bl = [1 ,2 ,3 ,4 ,5 ];bl=[]; bl.length = 0 ; bl.splice(0 ,bl.length); bl.toString(); bl.join(); let str = bl.join("+" );console .log(str); console .log(typeof str); 变量1. contact(变量2 ); let bl1 = [1 ,2 ,3 ];let bl2 = [4 ,5 ,6 ];let res = bl1.contact(bl2);console .log(res); console .log(typeof res); console .log(bl1); console .log(bl2); let res = [...bl1,...bl2] ; bl.reverse(); let bl = ["a" ,"b" ,"c" ,"d" ];bl.slice(1 ,3 ); let bl = ["a" ,"b" ,"c" ,"d" ,"c" ];bl.indexOf(c); bl.lastIndexOf(c); bl.indexOf(c,3 ) let bl = ["a" ,"b" ,"c" ,"d" ]; bl.includes(a); bl.includes(x); let bl = "a,b,c" ;let res = bl.split("," ); console .log(res);
二维数组 二维数组就是数组的每一个元素又是一个数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 let bl = [[666 ,777 ],[888 ,999 ]];let bl1 = bl[0 ]; let bl2 = bl1[1 ] let bl3 = bl[0 ][1 ]; let bl = [[],[]];bl[0 ] = [666 ,777 ]; bl[0 ][1 ] = 888 ; let bl = [[666 ,777 ],[888 ,999 ]];for (let i=0 ; i<bl.length; i++){ let bl1 = bl[i]; for (let j =0 ; j<bl1.length; j++){ console .log(bl1[j]); } }
函数 函数是专门用于封装代码的,函数是一段可以随时被反复执行的代码块
1 2 3 4 5 6 7 8 9 10 11 12 13 function 函数名称(形参列表 ) { 被封装的代码; } function toLeft ( ) { console .log("打左转灯" ); console .log("向左打方向盘" ); console .log("回正方向盘" ); } toLeft();
注意点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 【1 】一个函数可以有形参也可以没有形参(0 个或多个);形参就是定义函数时函数()中的变量 function say (name ) { console .log("hello " + name); } say("zhangsan" ); 【2 】一个函数可以有返回值也可以没有返回值 function getSum (a,b ) { return a+b; } console .log(getSum(1 ,2 )) ; 【3 】函数没有通过return 明确返回值,默认返回undefiend function say ( ) { console .log("hello" ); } let res = say(); console .log(res); 【4 】return 的作用和break 相似,所以return 后面不能编写任何语句,永远执行不到 break 作用立即结束switch 语句或者循环语句 return 作用理解结束当前所在函数 【5 】调用函数时 实参的个数和形参的个数可以不相同;实参就是调用函数时传入的数据 function getSum (a,b ) { console .log(a,b) return a+b; } let res = getSum(10 ); let res = getSum(10 ,20 ,30 ); 【6 】JS中的函数和数组一样,都是引用数据类型;既然是一种数据类型,所以也可以保存到一个变量中 let say = function ( ) { console .log("hello" ); } say();
函数中的扩展运算符作用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let [a,...b] = [1 ,3 ,5 ]let arr1 = [1 ,3 ,5 ];let arr2 = [2 ,4 ,6 ];let arr = [...arr1,...arr2]; function getSum (a,...bl ) { console .log(a); console .log(bl); } getSum(10 ,20 ,30 ,40 );
案例
1 2 3 4 5 6 7 8 9 10 function getSum (...bl ) { let sum = 0 ; for (let i = 0 ; i<bl.length; i++){ let num = bl[i] sum += num ; } return sum ; } let res = getSum(10 ,20 ,30 );console .log(res);
函数形参默认值 从ES6开始,可以直接在形参后面通过 = 指定默认值
1 2 3 4 function getSum (a = "hello" , b = "world" ) { console .log(a,b); } getSum();
从ES6开始,默认值还可以从其他的函数中获取
1 2 3 4 5 6 7 8 function getSum (a = "hello" , b = getDefault() ) { console .log(a,b); } getSum(); function getDefault { return ("世界" ) }
函数作为参数和返回值 函数作为其他函数的参数
1 2 3 4 5 6 7 8 9 10 11 let say = function ( ) { console .log("hello" ); } say(); function test (fn ) { fn(); } test(say);
函数作为其他函数的返回值
1 2 3 4 5 6 7 8 9 function test ( ) { let say = function ( ) { console .log("hello" ); } return say; } let fn = test(); fn();
匿名函数 匿名函数:就是没有名称的函数
注意点:不能够只定义不使用,否则会报错,使用场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function test (fn ) { fn(); } test(function ( ) { console .log("hello" ); }); function test ( ) { return function ( ) { console .log("hello" ); }; } let fn = test(); fn(); ( function ( ) { console .log("hello" ); }) ();
箭头函数 箭头函数时es6中新增的一种定义函数的格式;目的就是为了简化定义函数的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function 函数名称(形参列表 ) { 需要封装的代码; } let 函数名称 = function (形参列表 ) { 需要封装的代码; } let 函数名称 = (形参列表 ) => { 需要封装的代码; } let 函数名称 = 形参列表 => 执行一句代码;
代码演示
1 2 3 4 5 6 7 8 9 10 11 function say (name ) { console .log("hello " + name); } let say = (name ) => { console .log("hello " + name); } let say = name => console .log("hello " + name);
普通函数/方法中的this,谁调用就是谁;而箭头函数中的this,是父作用域中的this,不是调用者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 function demo ( ) { console .log(this ); } demo(); let p = { name :"zs" , say :function ( ) { console .log(this ); }, hi :() => { console .log(this ); } } p.say(); p.hi(); console .log(this ); function Person ( ) { this .name = "zs" ; this .say = function ( ) { console .log(this ); } this .hi = () => { console .log(this ); } } let p = new Person();p.say(); p.hi(); function Person ( ) { this .name = "zs" ; this .say = function ( ) { console .log(this ); } this .hi = () => { console .log(this ); } } let p = new Person();p.say.call({name :"ww" }); p.hi.call({name :"ww" });
递归函数 递归函数就是在函数中自己调用自己;递归函数在一定程度上可以实现循环功能
能看懂就行,企业开发中用的不多
1 2 3 4 5 6 7 8 function login { let pwd = prompt("请输入密码" ); if (pwd !== "123456" ){ login(); } alert("密码正确,欢迎回来" ) } login();
login函数调用执行代码会分配新的存储空间执行代码,执行过程如下图:
闭包 闭包是一种特殊的函数。
如何生成一个闭包?
当一个内部函数引用了外部函数的数据(变量/函数)时,那么内部的函数就是闭包
所以只要满足 “是函数嵌套” 、“内部函数引用外部函数数据”
闭包特点
只要闭包还在使用外部函数的数据,那么外部的数据就一直不会被释放
也就是说可以延长外部函数数据的声明周期
闭包注意点
当后续不需要使用闭包的时候,一定要手动将闭包设置为null,否则会出现内存泄露
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function test ( ) { var i = 666 ; } console .log(i); function test ( ) { var i = 777 ; function demo ( ) { console .log(i); } } let fn = test();fn();
函数中变量作用域 在js中 {} 外面的作用域,称之为全局作用域
在js中函数后面{}的作用域,称之为局部作用域
在ES6中只要 {} 没有和函数结合在一起,那么应该叫块级作用域
块级作用域和局部作用域区别
在块级作用域中通过 var 定义的变量是全局变量
在局部作用域中通过 var 定义的变量是局部变量
😃简单理解
{}内的let全部都是局部变量;外的let都是全局变量
var 只有在函数后面的{}内才是局部变量,其它都是全局变量
面向对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 ###面向对象思想 面向对象(Object Oriented,OO)是软件开发方法 面向对象是一种对现实世界抽象的理解,是计算机编程技术发展到一定阶段后的产物 Object Oriented Programming-OOP ——面向对象编程 ###面向对象和面向过程区别 【面向过程】 强调的是功能行为 关注的是解决问题需要哪些步骤 回想下前面我们完成一个需求的步骤: 首先搞清楚我们要做什么 然后分析怎么做 最后我用代码体现 一步一步去实现,而具体的每一步都需要我们去实现和操作 在上面每一个具体步骤中我们都是参与者, 并且需要面对具体的每一个步骤和过程, 这就是面向过程最直接的体现 面向对象是基于面向过程而言;面向对象和面向过程都是一种思想 【面向对象】 将功能封装进对象,强调具备了功能的对象 关注的是解决问题需要哪些对象 当需求单一, 或者简单时, 我们一步一步去操作没问题, 并且效率也挺高。 可随着需求的更改, 功能的增加, 发现需要面对每一个步骤非常麻烦, 这时就开始思索, 能不能把这些步骤和功能再进行封装, 封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。这样结构就清晰多了, 用的时候, 找到对应的类就可以了, 这就是面向对象思想 ###示例 【面向过程】 了解电脑 了解自己的需求 对比参数 去电脑城 砍价,付钱 买回电脑 被坑 【面向对象】 找班长 描述需求 班长把电脑买回来
默认类 创建对象 面向对象的核心就是对象,那怎么创建对象?
现实生活中可以根据模板创建对象,编程语言也一样,也必须先有一个模板,在这个模板中说清楚将来创建出来的对象有哪些属性和行为
JavaScript中的类相当于图纸,用来描述一类事物。
JavaScript中可以自定义类, 但是也提供了一个默认的类叫做Object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 【通过 new Object () 创建对象】 var obj = new Object () obj.name = "lnj" ; obj.age = 33 ; obj.say = function ( ) { console .log("hello" );} console .log(obj.name);console .log(obj.age);obj.say(); 【通过字面量创建对象】 var obj = {}; obj.name = "lnj" ; obj.age = 33 ; obj.say = function ( ) { console .log("hello" ); } var obj = { name : 'lnj' , age : 33 , say : function ( ) { console .log("hello" ); } } console .log(obj.name);console .log(obj.age);obj.say();
工厂函数 创建对象 专门用于创建对象的函数我们称之为工厂函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function createPerson (myname, myage ) { var obj = new Object (); obj.name = myname; obj.age = myage; obj.say = function ( ) { console .log("hello" ); } var obj = { name : myname, age : myage, say : function ( ) { console .log("hello" ); } } return obj; } var obj1 = createPerson("lnj" , 33 );var obj2 = createPerson("zq" , 18 );console .log(obj1);console .log(obj2);
构造函数 创建对象 构造函数和工厂函数一样,都是专门用于创建对象的;构造函数本质上是工厂函数的简写;推荐使用,更专业
构造函数的函数名称首字母必须大写;并且只能通过new来调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function Person (myName,myAge ) { this .name = myName; this .age = myAge; this .say = function ( ) { console .log("hello" ); } } let obj1 = new Person("zs" ,34 ); let obj2 = new Person("ls" ,25 );console .log(obj1);console .log(obj2);
由于两个对象中的say方法实现的都是一样的,但是保存到了不同的存储空间中,所以有性能问题,需要优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function mySay ( ) { console .log("hello" ); } function Person (myName,myAge ) { this .name = myName; this .age = myAge; this .say = mySay; } let obj1 = new Person("zs" ,34 ); let obj2 = new Person("ls" ,25 );console .log(obj1);console .log(obj2);
当前代码仍然存在弊端:阅读性降低;函数mySay污染了全局的命名空间(在相同的作用域内,后定义的会覆盖先定义的,为了避免覆盖,这个名字以后就不能用了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let fns ={ mySay :function ( ) { console .log("hello" ); } } function Person (myName,myAge ) { this .name = myName; this .age = myAge; this .say = fns.mySay; } let obj1 = new Person("zs" ,34 ); let obj2 = new Person("ls" ,25 );console .log(obj1);console .log(obj2);
但是这个写法还不够专业
推荐的写法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function Person (myName,myAge ) { this .name = myName; this .age = myAge; } Person.prototype={ constructor :Person, say :function ( ) { console .log("hello" ); } } let obj1 = new Person("zs" ,34 ); let obj2 = new Person("ls" ,25 );console .log(obj1);console .log(obj2);
prototype属性 特点:
存储在prototype中的方法可以被对应构造函数创建出来的所有对象共享
prototype除了可以存储方法以外,还可以存储属性
prototype如果出现了和构造函数中同名的属性或者方法,对象在访问的时候,访问到的是构造函数中的数据
应用场景
prototype中一般情况下用于存储所有对象都相同的一些属性以及方法;如果是对象特有的属性或者方法,我们会存储到构造函数中。
因为prototype中保存的只会占用一份存储空间
对象三角恋关系 每个构造函数中都有一个默认的属性,叫做prototype; prototype属性保存着一个对象,这个对象我们称之为原形对象
每个原型对象中都有一个默认的属性,叫做 constructor ;constructor指向当前原型对象对应的那个构造函数
通过构造函数创建出来的对象我们称之为实例对象 ;每个实例对象中都有一个默认的属性,叫做 __proto__ ; __proto__指向创建它的那个构造函数的原型对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function Person (myName,myAge ) { this .name = myName; this .age = myAge; } Person.prototype={ say :function ( ) { console .log("hello" ); } } let obj1 = new Person("zs" ,34 ); console .log(Person.prototype);console .log(Person.prototype);console .log(Person.prototype.constructor);
函数对象完整关系 function关系图
object函数关系图
😃完整关系图
属性注意点 在给一个对象不存在的属性设置值得时候,不会去原型对象中查找,如果当前对象没有就会给当前对象新增一个不存在的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function Person (myName,myAge ) { this .name = myName; this .age = myAge; } Person.prototype={ constructor :Person, currentType :"人" , say :function ( ) { console .log("hello" ); } } let obj = new Person("zs" ,34 ); obj.currentType = "新设置的值" ; console .log(obj.currentType); console ,log(obj.__proto__.currentType);
JS三大特性 回顾1
局部变量和函数:无论是ES6之前还是ES6,只要定义一个函数就会开启一个新的作用域;只要在这个新的作用域中,通过 let/var 定义的变量就是局部变量;只要在这个新的作用域中,定义的函数就是局部函数。
回顾2
对象的私有变量和函数:默认情况修改对象中的属性和方法都是公开/共有的,只要拿到对象就能操作对象的属性和方法;外界不能直接访问的变量和函数就是私有变量和私有函数;构造函数的本质也是一个函数,所以也会开启一个新的作用域,所以构造函数中定义的变量和函数就是私有变量、私有函数。
封装 封装性就是隐藏实现希捷,仅对外公开接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function Person { this .name = "zs" ; let age = 34 ; this .setAge = function (myAge ) { if (myAge>=0 ){ age = myAge ; } } this .getAge = function ( ) { return age; } } console .log(obj.name);console .log(age); let obj = new Person(); obj.setAge(99 ); console .log(obj.getAge);
继承 回顾:this是什么?
谁调用当前函数或者方法,this就是谁。函数中默认的this是window,方法中的this是调用这个方法的那个对象
😃新知识: bind call apply 方法
bind方法作用 :修改函数或者方法中的this为指定的对象,并且会返回一个修改之后的新函数给我们
call方法的作用 :修改函数或者方法中的this为指定的对象,并且会立即调用修改之后的函数
apply方法的作用 :修改函数或者方法中的this为指定的对象,并且会立即调用修改之后的函数
这三种方法除了修改this以外,还可以传递参数,只不过参数必须卸载this对象的后面;并且只有apply必须通过数组的方式传递
1 2 3 js中继承的终极方法 在子类的构造函数中通过call借助弗雷的构造函数 将子类的原型对象修改为父类的实例对象
多态 多态是指事物的多种状态。父类型变量保存子类型对象,父类型变量当前保存的对象不同,产生的结果也不同。
强类型语言:一般编译型语言都是强类型语言,要求变量的使用严格符合定义。类如定义 int num; 那么num中将来就只能狗存储整型数据;
弱类型语言:一般解释性语言都是弱类型语言,不会要求变量的使用严格符合定义。例如let num; num中既可以存储整形,也可以存储布尔类型等。
由于js是弱类型的语言,所以我们不用关注多态。
类和对象-ES6 es6之前如何定义一个类?通过构造函数来定义一个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function Person (myName,myAge ) { this .name = myName; this .age = myAge; this .say = function ( ) { console .log(this .name,this .age); } Person.num= 666 ; Person.run = function ( ) { console .log("run" ) } } let p = new Person("zs" ,18 );p.say(); console .log(person.num);person.run();
😃从es6开始系统提供了一个名称叫做 class 的关键字,这个关键字就是专门用于定义类的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Person { constructor (name2,age2 ) { this .name2 = myName; this .age2 = myAge; this .name1 = "zs" ; this .age1 = 17 ; } say ( ) { consloe.log(this .name2,this .age2); } Person.num= 666 ; static run ( ) { consloe.log("run" ); } let p = new Person("ls" ,66 );p.say(); console .log(person.num);person.run();
es6中的继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Person { constructor (myName,myAge ) { this .name = myName; this .age = myAge; } say ( ) { console .log(this .name,this .age); } } class Student extends Person { constructor (myName,myAge,myFenshu ) { super (myName,myAge); this .fenshu = myFenshu; } study ( ) { console .log("day day up" ); } } let stu = new Student("zs" ,18 ,99 );stu.say(); console .log(stu.fenshu);stu.study();
获取对象类型 想知道一个对象是通过哪个函数传出来的,一个对象真实的类型,使用对象.constructor.name
1 2 3 4 5 6 7 8 9 10 11 12 let obj = new Object ();console .log(obj.constructor.name); let arr = new Array ();console .log(arr.constructor.name); function Person ( ) { this .name = "zs" ; } let p = new Person();console .log(p.constructor.name);
instanceof关键字:用于判断对象是否是指定构造函数创建出来的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 class Person { constructor ( ) { name = 18 ; } } let p = new Person();console .log(p instanceof Person); class Cat {} let c = new Cat();console .log(c instanceof Person);
判断对象属性
1 2 3 4 5 6 7 8 9 10 11 12 13 class Person { name = null ; } Person.prototype.height = 0 ; let p = new Person(); console .log("name" in p); console .log("age" in p); console .log("height" in p); console .log(p.hasOwnProperty("name" )); console .log(p.hasOwnProperty("age" )); console .log(p.hasOwnProperty("height" ));
对象增删改查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Person {}let p = new Person();p.name = "zs" ; p.say = function ( ) { console .log("hello" ); } p["name" ] = "zs" ; p["say" ] = function ( ) { console .log("hello" ); } delete p.name;delete ["name" ];delete p.say;delete ["say" ];p.name = "WW" ; p.say = function ( ) { console .log("HI" ); } p["name" ] = "WW" ; p["say" ] = function ( ) { console .log("HI" ); } console .log(p.name);console .log(p["name" ]);p.say();
对象遍历 依次取出对象中所有的属性和方法。在js中通过高级for循环来遍历对象
for(let key in 对象名称){}
含义:将指定对象中所有的属性和方法的名称取出来依次的复制给key这个变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function Person (myName,myAge ) { this .name = myName; this .age = myAge; this .say = function ( ) { console .log(this .name,this .age); } } let p = new Person("zs" ,18 );for (let key in p){ console .log(key); console .log(p[key]); } for (let key in p){ if (p[key] instanceof Function ){ continue ; } console .log(key); console .log(p[key]); }
对象解构赋值 对象的结构赋值和数组的解构赋值,除了符号不一样,其它的一摸一样。
对象结构使用{} ; 数组结构使用 []
1 2 3 4 5 6 7 8 9 10 11 12 13 let obj = { name :"zs" , age :18 } let {name,age} = {name :"ww" ,age :99 };let {name} = {name :"ww" ,age :99 };let {name,age,height} = {name :"ww" ,age :99 }; let {name,age,height=66 } = {name :"ww" ,age :99 };let {a,b} = {name :"ww" ,age :99 };let {age} = {name :"ww" ,age :99 };console .log(age);
应用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let obj = { name :"zs" , age :34 } function say (name,age ) { console .log(name,age) } say(obj.name,obj.age); let obj = { name :"zs" , age :34 } function say ({name,age} ) { console .log(name,age) } say(obj);
深拷贝和浅拷贝 浅拷贝:
对于基本类型属性无论是深浅拷贝,都会复制一份
对于引用类型属性浅拷贝拷贝的是引用类型的地址
简而言之, 浅拷贝修改引用类型属性, 拷贝前拷贝后的对象都会受到影响
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 var obj1 = { name : "lnj" , age : 33 , dog : { name : "wc" , age : 1 , } }; var obj2 = {}; function copy (o1, o2 ) { for (var key in o1){ o2[key] = o1[key]; } } copy(obj1, obj2); console .log(obj1);console .log(obj2);obj2.name = "zs" ; console .log(obj1.name);console .log(obj2.name);obj2.dog.name = "xq" ; console .log(obj1.dog.name);console .log(obj2.dog.name);
深拷贝
对于基本类型属性无论是深浅拷贝,都会复制一份
对于引用类型属性深拷贝会将引用类型复制一份
简而言之, 深拷贝修改引用类型属性, 只会影响当前修改对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 var obj1 = { name : "lnj" , age : 33 , dog : { name : "wc" , age : 1 , } }; var obj2 = {};function copy (o1, o2 ) { for (var key in o1){ var item = o1[key]; if (item instanceof Object ){ var o = {}; o2[key] = o; copy(item, o); } else if (item instanceof Array ){ var arr = []; o2[key] = arr; copy(item, arr); } else { o2[key] = item; } } } copy(obj1, obj2); console .log(obj1);console .log(obj2);obj2.name = "zs" ; console .log(obj1.name);console .log(obj2.name);obj2.dog.name = "xq" ; console .log(obj1.dog.name);console .log(obj2.dog.name);
数组高级API 数组遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let bl = [55 ,66 ,77 ,88 ,99 ];for (let i=0 ; i<bl.length; i++){ console .log(bl[i]); } for (let value of bl){ console .log(value); } bl.forEach(function (currentValue,currentIndex,currentArray ) { console .log(currentValue,currentIndex,currentArray); console .log(currentValue); });
数组的查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 let bl = [55 ,66 ,77 ,88 ,99 ];let index = bl.indexOf(3 );let index = bl.lastIndexOf(3 );let index = bl.includes(3 );let index = bl.findIndex(function (currentValue,currentIndex,currentArray ) { if (currentValue === 66 ){ return true ; } }) console .log(index);bl.find(function (currentValue,currentIndex,currentArray ) { if (currentValue === 66 ){ return true ; } }) console .log(index);
数组的映射 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let bl = [55 ,66 ,77 ,88 ,99 ];let newBl = bl.filter(function (currentValue,currentIndex,currentArray ) { if (currentValue % 2 ===0 ){ return true ; } }) console .log(newBl); let newBl = bl.map(function (currentValue,currentIndex,currentArray ) { if (currentValue % 2 ===0 ){ return true ; } }) console .log(newBl);
数组的删除 数组的排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 let bl = ['March' , 'Jan' , 'Feb' , 'Dec' ];bl.sort(); console .log(bl); bl.sort(function (a,b ) { if (a>b){ reture -1 ; }else if (a<b){ return 1 ; }else { return 0 ; } }) console .log(bl); let bl = ['4' , '1' , '3' , '2' ];bl.sort(); console .log(bl); bl.sort(function (a,b ) { return b-a; }) console .log(bl); let bl = [ {name :"zs" ,age :18 }, {name :"ls" ,age :66 }, {name :"ww" ,age :17 }, {name :"mz" ,age :42 }, ]; bl.sort(function (o1,o2 ) { return o1.age - o2.age; return o2.age - o1.age; })
字符串常用方法 在js中字符串可以看作一个特殊的数组,所以大部分数组的属性/方法 字符串都可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 let bl = "abcad" ;console .log(bl.length); let bl1 = bl.charAt(1 );console .log(bl1); let bl1 = bl.indexOf("a" ); let bl2 = bl.lastIndexOf("a" ); let bl3 = bl.includes("a" ); let bl = "1-3-5" ;let arr = bl.split("-" );let bl = "http://www.baidu.com" ;let str1 = bl.startWith("http" );let str2 = bl.endtWith(".com" );let bl = `...` ; let bl = "<ul>\n" + "<li>我是2</li>\n" + "<li>我是3</li>\n" + "<li>我是4</li>\n" + "<li>我是5</li>\n" + "</ul>" ; let bl = ` <ul> <li>我是2</li> <li>我是3</li> <li>我是4</li> <li>我是5</li> </ul> ` ; let name = "zs" ; let age = 18 ; var str = "我的名字是" + name+ ",我的年龄是" + age+ "岁。" ; var str = `我的名字是${name} ,我的年龄是${age} 岁。` ;
正则表达式 正则表达式就是对字符串操作的一种逻辑公式.JavaScript RegExp 参考手册
1 2 3 4 let reg = new RegExp ("正则表达式" ,"修饰符i/g/m" );let reg = /正则表达式/ 修饰符igm ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 let str = "123abc4562022-12-1abc2022-1-11" ;let reg1 = new RegExp ("abc" ); let reg2 = new RegExp ("abc" ,"i" ); reg1.test(str); let reg = RegExp ("\\d{4}-\\d{1,2}-\\d{1,2}" ); let dpp = /\d{4}-\d{1,2}-\d{1,2}/g ;reg.test(str); let dpp = /\d{4}-\d{1,2}-\d{1,2}/g ; str.match(dpp); let dpp = /\d{4}-\d{1,2}-\d{1,2}/g ;str.replace(dpp,"www.baidu.com" )
常用正则表达式合集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 验证数字:^[0-9]*$ 验证n位的数字:^\d{n}$ 验证至少n位数字:^\d{n,}$ 验证m-n位的数字:^\d{m,n}$ 验证零和非零开头的数字:^(0|[1-9][0-9]*)$ 验证有两位小数的正实数:^[0-9]+(.[0-9]{2})?$ 验证有1-3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$ 验证非零的正整数:^\+?[1-9][0-9]*$ 验证非零的负整数:^\-[1-9][0-9]*$ 验证非负整数(正整数 + 0) ^\d+$ 验证非正整数(负整数 + 0) ^((-\d+)|(0+))$ 验证长度为3的字符:^.{3}$ 验证由26个英文字母组成的字符串:^[A-Za-z]+$ 验证由26个大写英文字母组成的字符串:^[A-Z]+$ 验证由26个小写英文字母组成的字符串:^[a-z]+$ 验证由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$ 验证由数字、26个英文字母或者下划线组成的字符串:^\w+$ 验证用户密码:^[a-zA-Z]\w{5,17}$ 正确格式为:以字母开头,长度在6-18之间,只能包含字符、数字和下划线。 验证是否含有 ^%&',;=?$\" 等字符:[^%&',;=?$\x22]+ 验证汉字:^[\u4e00-\u9fa5],{0,}$ 验证Email地址:^\w+[-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$ 验证InternetURL:^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$ ;^[a-zA-z]+://(w+(-w+)*)(.(w+(-w+)*))*(?S*)?$ 验证电话号码:^(\d3,4|\d{3,4}-)?\d{7,8}$:--正确格式为:XXXX-XXXXXXX,XXXX-XXXXXXXX,XXX-XXXXXXX,XXX-XXXXXXXX,XXXXXXX,XXXXXXXX。 验证身份证号(15位或18位数字):^\d{15}|\d{}18$ 验证一年的12个月:^(0?[1-9]|1[0-2])$ 正确格式为:“01”-“09”和“1”“12” 验证一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$ 正确格式为:01、09和1、31。 整数:^-?\d+$ 非负浮点数(正浮点数 + 0):^\d+(\.\d+)?$ 正浮点数 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$ 非正浮点数(负浮点数 + 0) ^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 负浮点数 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$ 浮点数 ^(-?\d+)(\.\d+)?$
JavaScrtpt三大对象 JavaScript中提供三种自带的对象,分别是“本地对象” / “内置对象” / “宿主对象”
1.本地对象
与宿主无关,无论在浏览器还是服务器中都有的对象,就是ECMAScript标准中定义的类(构造函数)。 在使用过程中需要我们手动new创建,例如:Boolean、Number、String、Array、Function、Object、Date、RegExp等。
2.内置对象
与宿主无关,无论在浏览器还是服务器中都有的对象,ECMAScript为我们创建好的对象。 在使用过程中无需我们手动new创建,例如:Global、Math、JSON。
3.宿主对象
对于嵌入到网页中的JS来说,其宿主对象就是浏览器,所有宿主对象就是浏览器提供的对象。 包含:Window和Document等。 所有的DOM和BOM对象都属于宿主对象。
内置对象Math Math 是一个内置对象,它拥有一些数学常数属性和数学函数方法。Math 不是一个函数对象。Math MDN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 let num = 3.9 ;let num1 = Math .floor(num); let num = 3.1 ;let num1 = Math .ceil(num); let num= 3.45 ;let num1 = Math .round(num); let num = -3 ;let num1 = Math .abs(num); let num = Math .random(); function getRandomIntInclusive (min, max ) { min = Math .ceil(min); max = Math .floor(max); return Math .floor(Math .random() * (max - min + 1 )) + min; } let num = getRandomIntInclusive(1 ,100 );
宿主对象 DOM 什么是window ?
window:是一个全局对象,代表浏览器中一个打开的窗口,每个窗口都是一个window对象
什么是document ?
什么是DOM?
DOM定义了访问和操作 HTML文档的标准方法
DOM全称:Document Object Model,即文档模型对象
所以学习DOM就是学习如何通过Document对象操作网页上的内容
1 2 3 console .log(window .document);console .log(typeof window .document); console .log(window .document.title);
获取DOM 在JS中HTML标签也称之为DOM元素;使用document的时候前面不用加window
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <div class ="father" > <form > <input type ="text" name ="test" > <input type ="password" name ="test" > </form > </div > <div class ="father" id ="box" > 我是div</div > <script > document .querySelector(".father" ); document .querySelector(".father" ); document .getElementById("box" );document .getElementsByClassName("father" ); document .getElementsByName("test" ); document .getElementsByTagName("div" ); </script >
获取指定元素的子元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <div > <h1 > 1</h1 > <h2 > 2</h2 > <p > 3</p > <p > 4</p > <span > 5</span > <div > <div > 6</div > </div > </div > <script > let oDiv = document .querySelector("div" );console .log(oDiv.firstChild); console .log(oDiv.firstElementChild);console .log(oDiv.lastChild);console .log(oDiv.lastElementChild);let item = document .querySelector("h2" )console .log(item.parentElement); console .log(item.parentNode); console .log(item.previousSibling); console .log(item.previousElementSibling); console .log(item.nextSibling); console .log(item.nextElementSibling);</script >
节点增删改查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <div > <h1 > 标题</h1 > <p > 我是段落</p > </div > <script > let oDiv = document .querySelector("div" );let oH1 = document .querySelector("h1" );let oP = document .querySelector("p" );let oSpan = document .createElement("span" );oDiv.appendChild(oSpan); oDiv.insertBefore(oSpan,oP); oSpan.parentNode.removeChild(oSpan); oDiv.parentNode.removeChild(oDiv); oDiv.cloneNode(); oDiv.cloneNode(true ); </script >
属性增删改查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <img src ="/xx.img" alt ="我是alt" title ="我是title" zdy ="by66" > <script > let oImg = document .querySelector("img" );oImg.src; oImg.getAttribute("zdy" ); oImg.title = "新的Title" oImg.setAttribute("zdy" ,"by999" ); oImg.setAttribute("xinzeng" ,"9898" ); oImg.className += " zhuijia" ; oImg.alt = "" ; oImg.removeAttribute("zdy" ); </script >
操作元素内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <div > 我是div <h1 > 我是标题h1</h1 > <p > 我是段落p</p > </div > <script > let oDiv = document .querySelector("div" );oDiv.innerHTML; oDiv.innerText; oDiv.textContent; oDiv.innerHTML = "<span>123</span>" ; oDiv.innerText = "<span>456</span>" ; oDiv.textContent = "<span>789</span>" ; function setText (obj,text ) { if ("textContent" in obj){ obj.textContent = text; }else { obj.innerText = text; } } setText(oDiv,"www.baidu.com" ); </script >
操作元素样式 1 2 3 4 5 6 7 8 9 10 11 12 13 <div > </div > <script > let oDiv = document .querySelector("div" );oDiv.className = "box" ; oDiv.style.width = "50px" ; oDiv.style.height = "50px" ; oDiv.style.backgroundColor = "#555" ; </script >
【获取元素样式】
style属性
getComputedStyle() 方法
currentStyle属性
offsetWidth / offsetHeight属性
clientWidth / clientHeight属性
获取行内样式
全部
全部
全部
全部
纯宽高
纯宽高
纯宽高
宽/高+内边距+边框
宽/高+内边距
能获取,能设置
只能获取
只能获取
只能获取
都支持
IE9以下不支持
IE都支持(谷歌火狐不支持)
都支持
scrollWidth / scrollHeight
当内容没有超出元素范围时,宽/高+内边距 ,即和clientWidth / clientHeight 一样
当内容超出元素范围时,宽/高+内边距+超出的宽/高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <div style = "width: 2px;" > </div > <style > div { height : 100px ; width : 100px ; padding : 50px ; border : 50px solid #000 ; background-color : red; background-clip : content-box; } </style > <script > let oDiv = document .querySelector("div" );oDiv.style.height = "4px" ; console .log(oDiv.style.width); let gestyle = window .getComputedStyle(oDiv);console .log(gestyle);console .log(gestyle.width); console .log(gestyle.padding); console .log(oDiv.currentStyle);console .log(oDiv.offsetWidth); console .log(oDiv.offsetHeight); </script >
获取网页可视宽高 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 var browserWidth = window .innerWidth || document .documentElement.clientWidth || document .body.clientWidth;var browserHeight = window .innerHeight || document .documentElement.clientHeight || document .body.clientHeight;function getScreen ( ) { let width,height ; if (window .innerWidth){ width = window .innerWidth; height = window .innerHeight; }else if (document .compatMode === "BackConpat" ){ width = document .body.clientWidth; height = document .body.clientHeight; }else { width = document .documentElement.clientWidth; height = document .documentElement.clientHeight; } return { width :width, height :height } } let {width,height} = getScreen();console .log(width);console .log(height);window .innerHeightwindow .innerWidthdocument .documentElement.clientWidth document .documentElement.clientHeightdocument .body.clientWidthdocument .body.clientHeightdocument .compatMode
获取网页滚动距离 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 var scrollX = window .pageXOffset || document .documentElement.scrollLeft || document .body.scrollLeft || 0 ;var scrollY = window .pageYOffset || document .documentElement.scrollTop || document .body.scrollTop || 0 ; function getScroll ( ) { return { x : window .pageXOffset || document .documentElement.scrollLeft || document .body.scrollLeft || 0 , y : window .pageYOffset || document .documentElement.scrollTop || document .body.scrollTop || 0 }; } let {scrollX,scrollY} = getScroll();function getScroll ( ) { let scrollX,scrollY ; if (window .pageXOffset){ scrollX = window .pageXOffset; scrollY = window .pageYOffset; }else if (document .compatMode === "BackConpat" ){ scrollX = document .documentElement.scrollLeft; scrollY = document .documentElement.scrollTop; }else { scrollX = document .body.scrollLeft; scrollY = document .body.scrollTop; } return { scrollX :scrollX, scrollY :scrollY } } window .pageXOffsetwindow .pageYOffsetdocument .documentElement.scrollLeftdocument .documentElement.scrollTopdocument .body.scrollLeftdocument .body.scrollTop
回到顶部
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function getScroll ( ) { return { x : window .pageXOffset || document .documentElement.scrollLeft || document .body.scrollLeft || 0 , y : window .pageYOffset || document .documentElement.scrollTop || document .body.scrollTop || 0 }; } window .onscroll = function ( ) { console .log(getScroll().y); if (getScroll().y > 10 ) { xxx.onclick = function ( ) { window .scrollTo(0 , 0 ); } } }
JS三大家族 offset client scroll 三大属性并称为js三大家族
【 offsetLeft / offsetTop 】
获取元素 到第一个定位祖先元素之间的偏移位;如果没有祖先元素是定位的,那么就是获取到body的偏移位
【 offsetParent 】
获取元素的第一个定位的祖先元素,如果没有祖先元素是定位的,那么获取到的就是body
即爷爷定位了,获取的是爷爷;爸爸也定位了,获取到的就是爸爸
【 clientLeft / clientTop 】
【 scrollLeft / scrollTop 】
内容发生滚动,滚动的距离(距离顶部)就是scrollLeft / scrollTop
添加事件的三种方式 用户和浏览器之间的行为我们就称之为事件,比如 点击/移入/移出
完整事件见W3school
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <button > 我是按钮</button > <a href ="http://baidu.com" > 链接</a > <script > let oBtn = document .querySelector("button" );oBtn.onclick = function ( ) { alert("我被点击了" ); } let oA = document .querySelector("a" );oA.onclick = function ( ) { alert("我也被点击了" ); return false ; } </script >
添加事件的三种方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <button id ="btn" > 我是按钮</button > <script > let myBtn = document .querySelector("#btn" );myBtn.onclick = function ( ) { alert("onxxx属性" ); } myBtn.addEventListener("click" ,function ( ) { alert("至少传递两个参数" ) alert("第一个参数是绑定的事件的名称" ); alert("第二个参数是触发之后的回调函数" ); }) myBtn.attachEvent("onclick" ,function ( ) { alert("和addEventListener差不多,只是兼容性的区别" ) }) function addEvent (ele,name,fn ) { if (ele.attachEvent){ ele.attachEvent("on" +name,fn) }else { ele.addEventListener(name,fn); } } addEvent(myBtn,"click" ,function ( ) { alert("这里是使用封装的方法" ) }); </script >
事件对象 事件对象就是一个系统自动创建的对象;当注册的事件被触发的时候,系统会自动创建事件对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <button id ="btn" > 我是按钮</button > <script > let myBtn = document .querySelector("#btn" );myBtn.onclick = function ( ) { alert("事件触发" ); } myBtn.onclick = function (event ) { event = event || window .event; alert("事件触发" ); console .log(event); console .log(typeof event) } </script >
事件对象常用属性
1 2 3 4 event.offsetX / event.offsetY :事件触发相对于当前元素自身的位置 event.clientX / event.clientY :事件触发相对于浏览器可视区域的位置 event.pageX / event.pageX :事件触发相对于整个网页的位置
事件执行的三个阶段 三个阶段只有两个会被同时执行,要么捕获和当前,要么当前和冒泡。(历史遗留问题,W3C为了兼容)
捕获阶段(从外向内的传递事件)
当前目标阶段(执行回调函数)
冒泡的阶段(从内向外的传递事件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <div id ="father" > <div id ="son" > </div > </div > <script > let fatherDiv = document .querySelector("#father" );let sonDiv = document .querySelector("#son" );fatherDiv.addEventListener("click" ,function ( ) { console .log("father" ); }, true ); sonDiv.addEventListener("click" ,function ( ) { console .log("son" ); }, true ); fatherDiv.addEventListener("click" ,function ( ) { console .log("father" ); }, false ); sonDiv.addEventListener("click" ,function ( ) { console .log("son" ); }, false ); sonDiv.addEventListener("click" ,function ( ) { event.stopPropagation(); console .log("son" ); }, false ); </script >
移入移出事件区别 onmouseover 移入到子元素,父元素的移入事件也会被触发
onmouseenter 移入到子元素,父元素的移入事件不会被触发
onmouseout 移出到子元素,父元素的移出事件也会被触发
onmouseleave 移出到子元素,父元素的移出事件不会被触发
定时器 1 2 3 4 5 6 7 8 9 10 window .setInterval(function ( ) { console .log("每隔1000毫秒执行这段函数" ); }, 1000 ); window .setTimeout(function ( ) { console .log("2000毫秒之后执行这段函数一次" ); }, 2000 );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <button id ="start" > 开始</button > <button id ="close" > 结束</button > <script > let startBtn = document .querySelector("#start" );startBtn.onclick = function ( ) { id = setInterval (function ( ) { console .log("随便写点" ); }, 1000 ); } let closeBtn = document .querySelector("#close" );closeBtn.onclick = function ( ) { clearInterval (id); } </script >
函数防抖 函数防抖是优化高频率执行js代码的一种手段,可以让被调用的函数在一次连续的高频操作过程中只被调用一次
作用:减少代码执行次数,提升网页性能
应用场景 oninput onmousemove onscroll onresize 等事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <input type ="text" > <script > let myInput = document .querySelector("input" );myInput.oninput = function ( ) { console .log("发送网络请求代码" ); } let timeId = null ;myInput.oninput = function ( ) { timeId && clearTimeout (timeId); timeId = setTimeout (function ( ) { console .log("发送网络请求代码" ); },1000 ); } </script >
函数防抖封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function debounce (fn,delay ) { return function ( ) { let self = this ; let args = arguments ; let timeId = null ; timeId = setTimeout (function ( ) { fn.apply(self,args); }, delay || 1000 ); } } function test ( ) { console .log("发送网络请求代码" ); console .log(this ); } myInput.oninput = debounce(test,3000 );
函数节流 函数节流也是优化 高频率执行js代码的一种手段;可以减少高频调用函数的执行次数;作用和场景等同于函数防抖
函数节流是减少连续的高频操作函数执行次数(例如调用10次,可能只执行3-4次)
函数防抖是让连续的高频操作时函数只执行一次(例如连续调用10次,但只执行1次)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <script > let myDiv = document .querySelector("div" );function resetSize ( ) { let {width,height} = getScreen(); myDiv.style.width = width / 2 + "px" ; myDiv.style.height = height / 2 +"px" ; } resetSize(); window .onresize = function ( ) { resetSize(); console .log("尺寸变化了A" ); } var timerId = null ;window .onresize = function ( ) { timerId && clearTimeout (timerId); timerId = setTimeout (function ( ) { resetSize(); console .log("尺寸变化了B" ); }, 500 ) } var timerId = null ;let flag = true ;window .onresize = function ( ) { if (!flag){ return ; } flag = false ; timerId && clearTimeout (timerId); timerId = setTimeout (function ( ) { flag = true ; resetSize(); console .log("尺寸变化了C" ); }, 500 ) } </script >
函数节流封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function throttle (fn,delay ) { let timerId = null ; let flag = true ; return function ( ) { if (!flag) {return } flag = false ; let self = this ; let args = arguments ; timerId = setTimeout (function ( ) { flag = true ; fn.apply(self,args); }, delay || 1000 ); } } window .onresize = throttle(keshiquyu,500 );function keshiquyu ( ) { resetSize(); console .log("尺寸变化了D" ); } window .onresize = throttle(function ( ) { resetSize(); console .log("尺寸变化了D" ); },500 )
宿主对象 BOM 什么是BOM?
BOM就是一套操作浏览器的API(接口/方法/属性)
DOM就是一套操作HTML标签的API(接口/方法/属性)
BOM中常见的对象
window:代表整个浏览器窗口;window是BOM中的一个对象,并且是一个顶级的对象(全局)
Navigator:代表浏览器的信息,通过Navigator我们就能判断用户当前是什么浏览器
Location:代表浏览器地址栏的信息,通过Location我们就能设置或者获取当前地址信息
History:代表浏览器的历史信息,通过History来实现刷新、上一步、下一步
注意点:出于安全考虑,我们并不能拿到用户所有的历史记录,只能拿到当前历史记录
Screen:代表用户的屏幕信息
Navigator 代表浏览器的信息,通过Navigator我们就能判断用户当前是什么浏览器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 console .log(typeof window .navigator); window .navigator: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" let agent = window .navigator.userAgent;if (/chrome/i .test(agent)){ alert("当前是chrome浏览器" ); }else if (/firefox/i .test(agent)){ alert("当前是火狐浏览器" ); }else if (/msie/i .test(agent)){ alert("当前是低级IE浏览器" ); }else if ("ActiveXObject" in window ){ alert("当前是高级IE浏览器" ); }
Location 代表浏览器地址栏的信息,通过Location我们就能设置或者获取当前地址信息
1 2 3 4 5 6 7 8 9 10 11 window .location.href window .location.href = "www.baidu.com" window .location.reload();window .location.reload(true );
History 代表浏览器的历史信息,通过History来实现刷新、上一步、下一步(浏览器地址栏左侧的 左右按钮即使前进 后退)
1 2 3 4 5 6 7 8 9 10 window .history.forward();window .history.go(3 );window .history.back();window .history.go(-2 );window .history.go(0 );
自己封装工具类 就是把兼容写法封装到工具类当中,方便以后直接调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 (function ( ) { function getScreen ( ) { let width,height ; if (window .innerWidth){ width = window .innerWidth; height = window .innerHeight; }else if (document .compatMode === "BackConpat" ){ width = document .body.clientWidth; height = document .body.clientHeight; }else { width = document .documentElement.clientWidth; height = document .documentElement.clientHeight; } return { width :width, height :height } } function getScroll ( ) { let x, y ; if (window .pageXOffset){ x = window .pageXOffset; y = window .pageYOffset; }else if (document .compatMode === "BackConpat" ){ x = document .documentElement.scrollLeft; y = document .documentElement.scrollTop; }else { x = document .body.scrollLeft; y = document .body.scrollTop; } return { x :x, y :y } } function addEvent (ele,name,fn ) { if (ele.attachEvent){ ele.attachEvent("on" +name,fn) }else { ele.addEventListener(name,fn); } } function getStyleAttr (obj,name ) { if (obj.currentStyle){ return obj.cerrentStyle[name]; }else { return getComputedStyle(obj)[name]; } } function debounce (fn,delay ) { let timerId = null ; return function ( ) { let self = this ; let args = arguments ; timerId = setTimeout (function ( ) { fn.apply(self,args); }, delay || 1000 ); } } function throttle (fn,delay ) { let timerId = null ; let flag = true ; return function ( ) { if (!flag) {return } flag = false ; let self = this ; let args = arguments ; timerId = setTimeout (function ( ) { flag = true ; fn.apply(self,args); }, delay || 1000 ); } } window .getScreen = getScreen; window .getScroll = getScroll; window .getScreen = getScreen; window .getStyleAttr = getStyleAttr; window .debounce = debounce; window .throttle = throttle; })();
封装完成如何使用?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <input type ="text" > <script > let myInput = document .querySelector("input" );let screenHeight = getScreen().height;console .log(screenHeight); addEvent(myBtn,"click" ,function ( ) { alert("这里写触发事件的代码" ); }); let scrollY = getScroll().y;console .log(scrollY); myInput.oninput = debounce(function ( ) { console .log("发送网络请求代码" ); }, 5000 ) </script >