简介
ECMAScript是JavaScript的标准,JavaScript实现了ECMAScript,ECMAScript规范了JavaScript。
JavaScript体系
- 语言核心
- DOM
- BOM
JavaScript书写位置
body里面写script标签,例如:
<body>
<script>
alert('hello, world');
</script>
</body>
或者
<script src=" .js" type="text/javascript" charset="utf-8"></script>
认识输出、输入语句
alert( ) 语句 弹窗
console.log( ) 语句 控制台输出
比如:
alert('haha');
alert( ) 是内置函数,函数就是功能的封装,调用函数需要使用小括号。
'haha' 是函数的参数,因为是字符串,所以要用引号包裹。
console.log('test');
console是js的内置对象,通过 打点 可以调用它内置的 log 方法。
所谓方法就是对象能够调用的函数。
var input = prompt('请输入内容');
输入语句 prompt( ) 函数,弹出框输入,输入的内容会转为字符串类型。
学会处理报错
常见两个错误:
Uncaught SyntaxError: Invalid or unexpected token
未捕获的语法错误:不合法或错误的符号。
Uncaught ReferenceError: a is not defined
未捕获的引用错误:a没有被定义。
函数名拼写错误也会引发 ReferenceError。
控制台
控制台也是一个REPL环境,可以使用它临时测试。
REPL:read 读 eval 执行 print 打印 loop 循环
变量
变量能储存计算结果 或 表示一个值;
变量不是数值本身,只是储存值的容器。
定义变量
var 关键字, 定义变量后,就可以使用了,可以用等号赋值。如果没有负值,默认值为undefined。可以同时声明多个变量,用逗号隔开。
var a;
console.log(a); //此时打印undefined
a = 7;
var b,
c,
d;
变量的合法命名
变量命名可以用多种风格,例如:
- 小驼峰命名法:首字母小写,后面每个单词头字母大写。
- C风格命名法:每个单词用下划线分隔。
变量命名符合JS标识符命名规则,变量、函数、类名、对象的属性等都要遵守。
标识符命名规则
- 只能由字母、数字、下划线、美元符号$组成,不能以数字开头。
- 不能是关键字或保留字,例如float等。
- 变量名大小写敏感,例如a和A不同。
全局变量声明提升
全局预编译
- 生成AO对象。
- 找变量声明,将变量名作为GO的属性名,值为undefined。
- 在函数体里找函数声明。
数据类型
- 基本数据类型:number、string、boolean、undefined、null
- 复杂数据类型:Array、function、object 等...
typeof 运算符
- 使用 typeof 运算符可以检测值或变量的类型,检测复杂类型可能会有出入。
- typeof 返回值是字符串类型的值。
- typeof 检测5种基本类型的的结果,除了null会返回object外,其他同名。
- typeof 可接未定义的变量不会报错。
基本数据类型
number 数字类型
所有数字不分大小、不分整数小数、不分正负,都是数字类型。
零点几的0可以省略。
科学计数法:3e7 => 3*10⁷。
不同进制的数字
二进制数值以 0b 开头:0b10 => 2;
八进制数值以 0 开头:017 => 15;
十六进制数值以 0x 开头。
一个特殊的数字类型值NaN
- not a number ,不是一个数,但它是一个数值类型的值。
- 0除以0的结果是 NaN ;运算结果不是数字的数学运算,返回值都是NaN。任何涉及NaN的运算都返回NaN。
- NaN 不自等,即 NaN == NaN; 返回 false。
- 用isNaN( ) 函数可以用来判断变量值是否为NaN,返回值是boolean布尔值,函数里的参数也会隐式转换为数字类型,所以判断不了undefined。
一个特殊的数字类型值Infinity
- Infinity 也是数值类型的的值,正数除以0得Infinite,负数除以0得-Infinity。
string 字符串类型
- 字符串要用引号包裹,双引号或者单引号均可。
- 加号可以用来拼接多个字符串(拼串儿)。
- 直接书写闭合的引号可得到空串。
字符串的length属性:表示字符串的长度。
字符串的常用方法:
方法名 | 功能 |
---|---|
charAt( ) | 得到指定位置字符 |
substring( ) | 提取子串 |
substr | 提取子串 |
slice | 提取子串 |
toUpperCase | 将字符串变为大写 |
toLowerCase | 将字符串变为小写 |
indexOf | 检索字符串 |
charAt( )方法
- 可以得到指定位置的字符,比如:
'abcdef'.charAt(0)
返回a。 - 得到指定位置字符串还可以用方括号方法,例如:
'abcdef'[3]
返回d。就是直接读取可遍历对象方括号里写索引。 - charCodeAt() 返回相应索引号的字符ASCII值
substring( )方法
sub 子,string字符串。
substring(a,b) 得到从 a 开始到 b 结束(不包括 b 处)的子串。
如果省略第二个参数,返回的子串会一直到字符串的结尾。
参数会自动从小排到大,比如 (5,3) 会自动变为 (3,5)。(慎用,可读性差)
substr( )方法
substr(a,b) 得到从a开始的长度为b的子串。
a可以是负数,表示倒数位置。b不能是负数,否则会返回空串。
slice( )方法
slice(a,b) 得到从a开始到b结束(不包括b处)的子串。
a 可以是负数,表示倒数,倒数的下标从-1开始。
参数 a 必须小于参数 b。
toUpperCase( )转为大写,toLowerCase( )转为小写
indexOf( )方法
返回某个指定的字符串值再字符串中首次出现的位置(索引值)。
如果要检索的字符串没有出现,则返回-1。
传入的参数是字符串,例如'abcdef'.indexOf('de')
返回3。
此方法还可以传入第二个参数,代表开始检索、搜索的索引位置。
boolean 布尔类型
小知识:布尔(Boole.George)英国19世纪数学家及逻辑学家。
布尔类型 值只有两个:true 和 false ,分别表示真假。
undefined 类型
undefinded 又是值,又是一种类型。
undefined 是全局对象上的一个属性,window.undefined
不可写 writable: false,比如window.undefined = 1
无效。
不可配置 configurable: false,比如delete window.undefined
无效。
不可枚举 enumerable: flase ,比如
for(var k in window){
if(k === undefined){
console.log(k);
}
}
不打印内容
不可重新定义 cannot redefine property: undefined;
系统会给一个未赋值的变量自动赋值为undefined,类型也是undefined 。
函数内部不写返回值的时候,返回的也是一个undefined。
在函数内部可以把undefined当作一个变量名来用,但尽量别用。例如:
function test(){
var undefined = 1;
console.log(undefined);
}
test();
会打印1
void(0) 会返回undefined
null
null是基本数据类型的值,typeof返回类型是object。
代表空、空对象。
可用于对象销毁,数组销毁,事件清空。
显式类型转换
其他类型 转 数字
- 使用 Number( ) 函数
字符串 → 数字:纯数字字符串能变为数字,不是纯数字的转为NaN,空串转为0.
布尔值 → 数字:true转为1,false转为0.
undefined → 数字:undefined转为NaN。
null → 数字:null转为0。 - 使用 parseInt( ) 函数。
用在字符串上时:将自动截断第一个非数字字符之后的所有字符,点也算,不会四舍五入。 - 使用 parseFloat( ) 函数。
将字符串转为浮点数,只会识别到第一个点。
其他类型 转 字符串
- 使用 String( ) 函数,变为长得相同的字符串。
- 使用 toString( ) 方法,数字使用 toString 要加括号,例如
(7).toString();
其他类型 转 布尔值
使用 Boolean( ) 函数。
数字→布尔:除了0和NaN是false,其他true。
字符串→布尔:空窗false,其他true。
undefined和null:转成布尔值都是false。
表达式 和 运算符 和 隐式类型转换
表达式
表达式,由运算元和运算符构成,并产生运算结果的语法结构。
表达式的种类有:算术、关系、逻辑、赋值、综合。
算术运算符
符号 | 功能 |
---|---|
+ | 加法、正数、连字符 |
- | 减法、负数 |
* | 乘法 |
/ | 除法 |
% | 取余 |
取余,也叫求模运算,用百分号 % 表示,小数模大数得小数。
乘除余运算优先级高于加减运算。
算术运算符的隐式类型转换
如果参与数学运算的值不是数值型,会自动转换为数值型然后运算,加号例外因为有拼串功能。其本质是内部调用 Number( ) 函数,例如:
true + true;
//Number(true) + Number(true) 然后返回一个2
+'1' //会返回一个数字1
3*'2天' //会返回NaN
算术运算符中浮点运算问题IEEE754
在JavaScript中,有些浮点运算不是很精准,因为采用的是IEEE754二进制浮点数算术标准,会产生“精度丢失”问题。
解决方法:可以用数字的 toFixed() 方法保留指定的小数位数,类型会变成字符串。例如:
(3.141592654).toFixed(2)
其他运算的Math方法
-
幂和开根:
幂和开根没有运算符,需调用Math对象的方法实现。
幂:Math.pow(底数,次方数);
开方:Math.sqrt(开根数);
-
向上取整和向下取整:(相对数轴而言)
向上取整:Math.ceil(取整的数);
向下取整:Math.floor(取整的数);
-
随机数:
Math.random( );
得到0到1的随机小数。(大于等于0小于1)
想得到[a,b]区间的整数,公式为:
parseInt(Math.random()*(b-a+1))+a;
封装成函数就是
function getRandom(min,max){
return parseInt(Math.random()*(max-min+1))+min;
}
-
绝对值:
Math.abs();
关系运算符
符号 | 意义 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
== | 等于 |
!= | 不等于 |
=== | 全等于 |
!== | 不全等于 |
进行关系运算符运算候的结果返回值是boolean布尔值。
除了全等和不全等以外,其他关系运算符都会隐式转换两边值为数字类型。
例如:
1 == '1'; //返回true
undefined == null; //这是个例外,undefined转数字是NaN的,但返回true
undefined === null; //返回false
NaN == NaN; //返回false,NaN不自等,所以要用isNaN( ) 函数判断
没有数学书写上的连比3<=16<=15
,要连比只能用逻辑运算符
逻辑运算符
符号 | 意义 |
---|---|
! | 非 |
&& | 与 |
|| | 或 |
! 非
- 也可以称为“置反运算”。
- 是一个“单目操作符”,只需要一个操作数。
- 置反运算的结果一定是布尔值,要置放的值会先隐式转换为布尔值。所以用双非
!!
可以用来隐式转换一个值为布尔值。
&& 与运算
找假返回假,如果没有假则返回最后一个真。(都真就真)
|| 或运算
找真返回真,如果没有真则返回最后一个假。(有真就真)
逻辑运算符优先级:非>与>或
赋值表达式
赋值运算符
符号 | 意义 |
---|---|
= | 赋值 |
+= | 快速赋值 |
-= | 快速赋值 |
*= | 快速赋值 |
/= | 快速赋值 |
%= | 快速赋值 |
++ | 自增运算 |
-- | 自减运算 |
赋值运算也会产生值,例如:
var a;
console.log(a=7);
//a=7会返回7,所以会打印7而不是a=7。
所以可以连续使用赋值运算(实际开发禁止使用),例如:
var a,b,c;
a=b=c=7;
//先c=7返回7,再b=c=77,再a=b=c=77
快速赋值:+= 、-= 、*= 、 /= 、%=
在原数值基础上进一步运算。
自增自减:++、--
自身基础上加一或减一。
a++和++a区别:a++先用再加,++a先加再用。例如:
var n1=1,n2=2;
var n3 = (n1++)+n2
//此时n3等于3
综合表达式
综合运算顺序:非运算>数学运算>关系运算>逻辑运算 (非数关逻)
流程控制语句
条件语句:if、if elseif else、switch、三元运算符。
循环语句:for、while、break和continue、do while。
条件语句
if语句
if(条件){
语句块1,当条件为真时执行
}else{
语句块2,当条件为假时执行
}
if语句执行流程图
graph TD
A[程序执行] -->| | B(执行)
B --> C{测试条件}
C -->|条件1| D[语句块1]
C -->|条件2| E[语句块2]
- if语句中的else可以省略
- 如果if语句体中只有一行语句,可以省略大括号和换行,且结尾加分号
else if 否则如果
else if( ) 条件分支“暗含”不符合之前的所有条件。
if( 测试表达式1){
当测试表达式1为true时执行
}else if(测试表达式2){
当测试表达式2为true时执行
}else{
所有表达式为false时执行
}
if 语句算法题
第一题: 判断水仙花数。
如何拆位?
- 数学方法:百位是原数值除以100取整。十位是原数字除以10取整,再与10求模。个位是原数字与10求模
- 字符串方法:直接将原数字变为字符串,然后使用charAt( )方法。
第二题: 游乐园门票计算。
年龄大于等于10 | 年龄小于10岁 | |
---|---|---|
平日 | 300 | 140 |
周末 | 500 | 210 |
switch 语句
用途:当一个变量被分类讨论的情形。
- switch( ) 括号内一般是一个变量名,这个变量被分类讨论。
- case表示情况,后面直接跟一个值,程序会将这个值依次与switch括号内的变量匹配,如果全等,则执行case后的语句,多条case可以共用一个语句体。
- default 表示默认情况。
- break 跳出switch语句。
三元运算符
条件表达式 ? 表达式1 : 表达式2
当条件为真执行1,为假执行2。
循环语句
for 循环语句
for的小括号有三个表达式:
表达式1:for循环内可用的变量,循环变量一般定为i,意为iterator迭代器。
表达式2:只要这条返回true,就会继续执行后面的语句块。
表达式3:一般用来更新循环变量。
for循环执行机理:
for(var i=1①; i<=10②; i++④){
console.log(i)③;
}
//①→②→③→④
也就是说,for循环小括号内的第三条语句,是先执行中括号内的语句块,再执行的。
for循环算法题
- 累加器
- 1到100哪个数除3余1,除4余2,除5余3.(穷举法)
while 循环语句
是一种“不定范围”循环。先不指定循环开始和结束范围。只要满足测试条件,就一直执行循环体。
while(测试条件){
循环执行的语句块;
}
循环注意: 当循环条件终止值与更新的循环体内的值不是同一个时,当要求的值是循环值时,注意“出一错误”
循环语句中的break和continue
执行 break; 会立即终止循环。
执行 continue; 会跳过循环中的一个迭代,跳过其中一次循环,这一次循环体内的continue后面的其他语句将跳过执行一次。
do while 循环
先执行一次循环体,再判断测试条件是否满足。
do{
循环执行的语句块(会先执行一次);
}while(循环执行条件)
while循环算法题 猜数字
流程语句经典算法题
可以先用伪代码写下解决思路,再翻译成js语法。
累加器与累乘器
- 计算 3/2 + 4/3 + 5/4 + 6/5 + ...... + (n+1)/n 的结果
- 由用户输入数字n,计算n的阶乘。
for(var i=n; i>=1; i--){result*=i;}
result要从1开始- 圆周率可以由莱布尼茨级数公式计算,用户输入n,计算圆周率Π。
Π/2= 1 + 1/3 + 1*2/3*5 + 1*2*3/3*5*7 + 1*2*...n/3*5*...(2n-1)
穷举法
- 100以内,能整除3也能整除5的数。
- 输入一个数,在控制台显示这个数的全部约数。
- 寻找全部水仙花数。(每个数位的立方和等于它本身)。
综合算法题
- 寻找100以内的质数。(用到给外层for循环套label,然后用continue label)
- 鸡兔同笼,35头,94足。
数组
Array ,存储一组值。
如何定义数组
- 使用方括号:
var arr = ['A','B','C'];
- 使用 new Array 构造函数:
var arr = new Array('A','B','C');
当new Array函数只传入一个数字时,则定义一个长度为n的数组。
数组的每一项都有下标,下标从0开始。
使用 数组变量 接方括号中写下标 的形式,可访问数组项,例如:a[0];
当下标越界,会返回undefined而不会报错。
数组的length属性表示长度,数组的最后一项的下标时length-1
数组是可读可写的,如果更改的项下标越界,则会创造这项。
数组的遍历:
for (var i=0;i<arr.length; i++){
arr[i];
}
数组的不停写入,可用while循环(数组自增):
var arr = [];
while( ){
arr[arr.length]=
}
判断是不是数组
数组用typeof 检测结果是object,可用 Array.isArray( );
方法检测。
数组相关遍历算法题
- 求数组中每一项的总和、平均数。
- 求数组项的最大值和最小值。
数组的头尾操作方法
方法 | 功能 |
---|---|
push( ) | 在尾部插入新项 |
pop( ) | 在尾部删除 |
unshift( ) | 在头部插入 |
shift( ) | 在头部删除 |
push( ) 方法:推入新项,如果要推入多项,用逗号隔开。
pop( ) 方法:删除数组中的最后一项,该方法会返回被删除的项,可用一个变量接收。
unshift与shift特性与push、pop相似。以上方法均会改变原数组
数组的其他方法
会改变数组的:
方法(会改变原数组) | 功能 |
---|---|
reverse | 逆转排序 |
splice | 切割 |
sort | 改序 |
reverse( ) 方法
用于将数组中的全部项顺序置反。
splice( ) 方法
用于替换数组中的指定项。用法:
arr.splice(从下标为n的项开始 , 替换多少项 , 替换的新项可多项);
当第二个参数为0时,就会变为插入功能。
当第三个参数不写时,就会变为删除功能。
该方法会返回被删除的项组成的数组。
sort( ) 方法
arr.sort( );
sort开放了接口,可传入一个函数 arr.sort(function(){ });
- 必须写两形参!
arr.sort(function(a,b){ });
- 看返回值
1)返回值为正时,后面的数放前边。
2)返回值为负时,前面的数放前边。
3)返回值为0时,不做操作。
自己总结就是,正数交换位置,负数不换(正交负不换),所以:
arr.sort(function(a,b){return a-b;}) //升序
arr.sort(function(a,b){return b-a;}) //降序
arr.sort(function(a,b){return Math.random()-0.5;}) //乱序
不改变数组的方法:
方法(不改变原数组) | 作用 |
---|---|
slice( ) | 截取数组 |
concat( ) | 连接多个数组 |
数组的slice方法:
不会改变原数组,用于得到子数组。
slice(a,b) 截取从下标为a开始,到下标为b(不包括b)结束。
如果不传第二个参数。就会截取从a开始的后面所有项。参数允许为负数。
数组的concat方法: 合并连接多个数组,不改变原数组。
数组与字符串间转换的方法,以及更多通性
数组转字符串: join( )方法,将数组改变为字符串类型并以参数作为连接符输出,不传参数默认逗号。传递的参数要用引号括起来。
字符串转数组: split( )方法,以什么字符拆分字符串,参数为空时将拆为一个字符串。
更多相关性:
- 可以使用方括号写下标的形式访问某个字符,等价于charAt( )方法。
- 字符串的一些算法问题可以改为数组解决。
indexOf( ) 和 includes( )
indexOf( ) 方法:搜索数组中的元素并返回下标数,如果不存在返回-1。
includes( ) 方法:判断数组是否包含一个元素,返回布尔值。
数组经典算法题
冒泡排序:
for(var i=1; i<=arr.length-1;i++){
for(var j=0; j<arr.length-i; j++){
// j多功能担任遍历数组的下标
if (arr[j]>arr[j+1]) {
//因为j是访问不到最后一位的,j<arr.length-i,j最大只能到每一趟的倒数第二位。但是因为在if里面比较的是[j+1]位,所以第一轮刚好能访问到最后一位
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
数组去重:
思路:准备一个空数组,遍历原数组,如果遍历到的项新数组里没有(用indexOf判断或includes判断,后者是ES6),则push进去。
function unique(arr){
var newArr = [];
for(var i=0;i<arr.length;i++){
if(newArr.indexOf(arr[i])===-1){
newArr.push(arr[i]);
}
}
return newArr;
}
随机样本:
思路:准备一个空数组,遍历原数组,随机选择一项,推入新数组,并删除这项。
二维数组
- 数组里的项是数组时,就形成了二维数组
- 二维数组可以看作是矩阵。书写上最好写成矩阵。
- 遍历二维数组需要2层循环,n维数组则需要n层。
数组高阶函数map() reduce() filter()
map方法接收一个函数作为参数,这个函数会接收三个参数:参数一是这个数组里的每一项,参数二是每一项的下标索引值,参数三是调用map方法的这个数组本身,return的是指定的值进一个新的数组。最后map方法返回这个新的数组
var arr = [{name:'ming',age:18},{name:'he77',age:20},{name:'qi',age:24}]
var newArr = arr.map(function(item,index,arrSelf){
console.log(item,index,arrSelf)
return item.age
})
console.log(newArr) //[18,20,24]
reduce方法接收一个函数作为参数以及一个“开始计算的初始值”,这个函数会接收两个参数:参数一是上一次return的结果,参数二是这一次参与计算的数据。不断的计算最后return一个值。最后reduce方法返回这个值
var arr = [{name:'ming',age:18},{name:'he77',age:20},{name:'qi',age:24}]
var newValue = arr.reduce(function(previous,joining){
console.log(previous,joining)
return previous+joining.age
},0)
console.log(newValue) //62
filter方法接收一个函数作为参数,这个函数的参数会接受一个参数,参数一是这个数组的每一项,return符合判断为true的数据进一个新数组,最后filter方法返回这个新数组。
var arr = [{name:'ming',age:18},{name:'he77',age:20},{name:'qi',age:24}]
var newArr = arr.filter((item)=>{return item.name.includes('i')})
console.log(newArr) //[{name:'ming',age:18},{name:'qi',age:24}]
引用类型和基本类型
基本类型:number、string、boolean、undefined、null
引用类型:array、object、function、regexp...
相等判断:
- 基本类型进行相等判断,会比较 值 是否相等。
- 引用类型进行相等判断,会比较 址 是否相等。
所以浅克隆中如果克隆到数组,并不完全克隆,这个时候就需要深克隆。
函数
函数就是语句的封装,方便复用,具有“一次定义,多次调用”的优点。
函数的定义
使用function关键字,function是功能的意思。例如:
function fun( ){
}
function表示定义函数。
fun 是函数名,命名规则见js标识符命名规则。
圆括号()是形参列表,即使没有形参,也必须书写圆括号。
大括号{ }是函数执行的语句体
第二种定义函数方法是使用函数表达式:
var fun = function(){ }
调用函数
在函数名字后面书写圆括号即可,也可以称为函数执行符。。
函数声明提升
只有函数声明能提升,函数表达式不提升。提升相关详情见GO、AO预编译。
例题
fun();
var fun = function(){alert('a');}
function fun(){alert('b');}
fun();
//执行这段程序,会先弹出b,然后弹出a。
//所以函数只是声明提升,预编译后不再参与变量赋值。
函数的参数
函数声明时圆括号内定义形参(形式参数)
调用函数时传入实参(实际参数)
- 形参数量 = 实参数量 时:一一对应。
- 形参数量 < 实参数量 时:多出的实参不传入函数,但arguments仍可读到。
- 形参数量 > 实参数量 时:多出的形参值为undefined。
arguments
函数内的 arguments 表示它接收到的实参列表,他是一个类数组对象。
类数组对象:
是对象,可以当数组用。
和数组类似可以用方括号访问对象的属性值,但不能调用数组的方法,可以自己写入模拟数组方法。构成要素:
①属性要为索引(数字)属性
②必须要有length属性
return 函数的返回值
使用return关键字。
调用一个有返回值的函数,可以当一个普通值来用。
遇见return语句会立即退出函数。(结合if语句,往往不需要写else分支了)
return后不接内容则函数返回undefined,接内容则函数返回内容。
函数算法题
寻找喇叭花数(喇叭花数:三位数,每一位数字阶乘的和等于他本身)
函数的递归
函数内部语句调用这个函数自身,从而发起函数新一轮迭代,在新的迭代中又执行调用函数自身的语句,一直迭代下去,知道函数执行到某一次时不在迭代,然后一层一层返回,则形成函数递归。
递归的要素
- 边界条件:确定递归何时终止,也称递归出口。
①要么找到当参数等于某个值时候函数有结果
②要么函数执行次数有限,当循环到某一次的时候不再调用函数自身。 - 递归模式:大问题分小问题,递归体。
递归常见算法题
斐波那契数列:每项等于前两项的和。
function fibonacci(n){
if(n==0) return 0;
else if(n==1) return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
实现深度克隆数组:
function arrClone(arr){
var newArr=[];
for(var i=0;i<arr.length;i++){
if(!Array.isArray(arr[i])) newArr[newArr.length]=arr[i];
else newArr[newArr.length] = arrClone(arr[i]);
}
return newArr;
}
变量作用域
JavaScript是函数及作用域编程语言:变量只在其定义时所在的 function 内部有效,function 内的变量成为 function 的局部变量。
不见变量定义在任何函数里面,这个变量就是全局变量,或者 function 内不声明变量直接赋值也是全局变量。
遮蔽效应: 如果函数中也定义了与全局变量同名的变量,则函数内的变量会将全局变量“遮蔽”。
js运行三部曲
- 语法解析
- 预编译
- 解释执行
会发生以下几种情况:
函数声明整体提升
变量声明提升
imply global 暗示全局变量,window就是全局
预编译发生在函数执行前一刻。
全局预编译创建GO对象。
局部预编译先创建AO对象,再逐步执行(部分执行)。
全局预编译三部曲
- 生成GO对象。
- 找变量声明,将变量名作为GO属性名,值为undefined。
- 在函数体里面找函数声明。
局部预编译四部曲
- 生成AO对象(activation object)
AO{ } - 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined。
AO{
a: undefined;
b: undefined
}
- 将实参值与形参统一。
AO{
a: 1;
b: undefined
}
- 再函数体里找函数声明。
AO{
a: function a(){ };
b: undefined;
d: function d(){ }
}
闭包
JavaScript的函数会产生闭包(closure)。闭包是函数本身和该函数声明时所处的环境状态的组合。
函数能够“记忆住”其定义时所处的环境,即使函数不在其定义的环境被调用,也能访问定义时所处环境的变量。
在Javascript中,每次创建函数时都会创建闭包。
但是,闭包特性往往需要将函数“换一个地方”执行,才能被观察出来。
闭包非常实用,它允许我们将数据与操作该数据的函数关联起来。
用途:
- 记忆性:当闭包产生时,函数所处环境的状态会始终保持在内存中,不会在外层函数调用后自动清除。
- 模拟私有变量。
坏处:
滥用闭包会造成内存泄漏,引发性能问题。
立即执行函数 IIFE
IIFE全称:Immediately Invoked Function Expression,立即调用函数表达式,一被定义就立即被调用。
IIFE形成方法
只有表达式才能被执行符号(圆括号)执行。
表达式:由运算元和运算符构成,并产生运算结果的语句结构
能被执行符号执行的表达式会忽略这个函数的名字,所以立即执行函数写函数名会被忽略。
IIFE作用
- 为变量赋值,例如一个值需要经过一段流程控制语句后才产生,那么就可以把这堆流程控制语句封装成函数并立即执行然后返回值。
- 将全局变量变为局部变量(让IIFE里面的函数立即形成一个新的闭包)。
DOM
DOM是 Document Object Model 文档对下个模型,是Javascript操作HTML文档的接口。
DOM 节点树
节点的四个属性
属性名 | 作用 |
---|---|
nodeName | 元素的标签名,以大写形式表示,只读 |
nodeValue | Text节点或Comment节点的文本内容,可读可写 |
nodeType | 该节点类型,见下表 |
attributes | Element节点的属性集合 |
nodeType 常用属性值
值 | 属性 |
---|---|
1 | 元素节点,例如<p>和<div> |
3 | 文字节点 |
8 | 注释节点 |
9 | document节点 |
10 | DTD节点 |
延迟运行
- 在测试DOM代码时,通常JS代码要写在HTML节点后面,否则JS无法找到相应HTML节点。
- 可以使用
window.onload = function(){ }
或者document.addEventListener( 'DOMContentloaded',function(){ },false);
使页面加载完毕后,在执行指定的代码,推荐第二种
访问元素节点
寻找/遍历/访问主要依靠document对象。
几乎所有DOM的功能都封装在 document对象中。
document 对象也表示整个HTML文档,是DOM节点树的根
常用方法有:
方法 | 功能 |
---|---|
document.getElementById( ) | 通过ID得到元素 |
document.getElementsByTagName( ) | 通过标签名得到元素数组 |
document.getElementsByClassName( ) | 通过类名得到元素数组 |
document.getElementsByName( ) | 只有部分标签name可生效 |
document.querySelector( ) | 通过选择器得到元素 |
document.querySelectorAll( ) | 通过选择器得到元素数组 |
document.getElementById( )
当html元素有id属性的时候,可以用id获取该元素节点
注意事项:
- 如果页面上有相同id的元素,则只能得到第一个。
- 不管元素藏得多深,都能通过id找到
getElementsByTagName( )
通过标签名得到节点数组。
注意事项:
- 数组方便遍历,从而可以批量操控元素节点。
- 即使页面上只有一个指定标签名的节点,也将得到长度为1的数组。
- 任何一个节点元素可以调用getElementsByTagName方法,比如
box1.getElementsByTagName( 'p');
, 从而得到一个元素内部的子元素节点。
getElementsByClassName( )
通过类名得到节点数组。
注意事项:
- 从IE9开始兼容。
- 其他同上Tag特性
querySelector( )
通过选择器得到元素。例如:
var the_p = document.querySelector('#box1 .p')
注意事项:
- querySelector( )方法只能得到页面上一个元素,如果有多个元素符合条件,只能得到第一个元素。
- querySelector( ) 方法从IE8开始兼容,但从IE9开始支持CSS3的选择器,比如 :nth-child() 、:[src^='seven']等选择器形式支持良好。(伪类不行)
querySelectorAll( )
通过选择器得到元素数组。计师页面上只有一个符合选择器的节点,也将得到长度为1的数组
querySelector和querySelectorAll注意事项: 都不是实时的,例如节点是后期加上的会获取不到。
节点关系,节点之间的关系
关系 | 考虑所有节点 | 只考虑元素节点 |
---|---|---|
子节点 | childNodes | children |
父节点 | parentNode | parentElement |
第一个子节点 | firstChild | firstElementChild |
最后一个子节点 | lastChild | lastElementChild |
前一个兄弟节点 | previousSibling | previousElementSibling |
后一个兄弟节点 | nextSibling | nextElementSibling |
- DOM中,文本节点也属于节点,在使用节点关系时一定要注意。
- 在W3C标准规范中,空白文本节点也属于节点,IE8之前不把空文本当作节点。
- 可以用
父节点.hasChildNodes( );
判断有没有子节点
封装节点关系函数
//兼容IE8以下的获取所有子元素节点函数,类似children属性
function getChildren(node){
var children = [];
for(var i=0; i<node.childNodes.length; i++){
if(node.childNodes[i].nodeType == 1){
children.push(node.childNodes[i]);
}
}
return children;
}
//兼容IE8以下获取前一个兄弟元素节点
function getPreviousElementSibling(node){
while(node.previousSibling != null){
if (node.previousSibling.nodeType == 1) {
return node.previousSibling;
}
node = node.previousSibling;
}
}
//兼容IE8以下获取后一个兄弟元素节点
function getNextElementSibling(node){
while(node.nextSibling != null){
if (node.nextSibling.nodeType == 1) {
return node.nextSibling;
}
node=node.nextSibling;
}
}
//获取所有兄弟元素节点
function getAllElementSibling(node){
var o =node;
var preES = [];
var nexES = [];
while(o.previousSibling != null){
if (o.previousSibling.nodeType == 1) {
preES.unshift(o.previousSibling);
}
o = o.previousSibling;
}
o = node;
while(o.nextSibling != null){
if (o.nextSibling.nodeType == 1) {
nexES.push(o.nextSibling);
}
o = o.nextSibling;
}
return preES.concat(nexES);
}
节点操作
改变元素节点中的内容可以使用两个相关属性:
- innerHTML :能以HTML语法设置节点中的内容结构。
- innerText : 只能以纯文本的形式设置节点中的内容。写进去的标签也会变成字符串。
改变元素节点的CSS样式
用 元素.style.要修改的属性 = ***;的形式,例如:
div1.style.backgroundColor = 'blue';
div1.style.backgroundImage = 'url(images/1.jpg)';
div1.style.fontSize = '32px';
注意:
- js语句不能用横杠-书写标识符,所以用小驼峰形式书写css属性名。
- css属性值要设置成完整形式。
- 注意写单位,注意写单位,注意写单位。
- 是通过CSS行内属性设置上的。
- 写入值必须是字符串,必须是字符串,必须是字符串。
脚本化CSS
以上操作称为脚本化CSS,读写行间样式,如果是js保留字,前面应该加css,例如:cssFloat
如何查询计算样式,可以使用:
window.getComputedStyle(元素名,null);
//第一个值传的是需要获取计算样式的元素,第二个值传入是否获取的是伪元素。
//IE8以下用ELE.currentStyle,或者封装计算样式方法
function getStyle(obj, name) {
if(window.getComputedStyle) {
//正常浏览器的方式,具有getComputedStyle()方法
return getComputedStyle(obj, null)[name];
} else {
//IE8的方式,没有getComputedStyle()方法
return obj.currentStyle[name];
}
}
改变元素的css还可以用改变类名的写法
使用 DOMCUMENT.className += 需要样式的类型
或者使用dom的 classList 属性,classList是一个类数组,可以通过下标方式取得元素的class组,该属性有多种方法
方法名 | 功能 |
---|---|
DOMCUMENT.classList.add(class1,class2) | 在元素中添加一个或多个类名。如果指定的类名已存在,则不会添加 |
DOMCUMENT.classList.remove(class1, class2, ...) | 移除元素中一个或多个类名。注意: 移除不存在的类名,不会报错。 |
DOMCUMENT.classList.toggle(class,布尔值) | 在元素中切换类名。第一个参数为要在元素中移除的类名,并返回 false。 如果该类名不存在则会在元素中添加类名,并返回 true。 第二个是可选参数,是个布尔值用于设置元素是否强制添加或移除类,不管该类名是否存在。 |
DOMCUMENT.classList.item(index) | 返回元素中索引值对应的类名。索引值从 0 开始。如果索引值在区间范围外则返回 null |
DOMCUMENT.classList.contains(class) | 返回布尔值,判断指定的类名是否存在。可能值:true - 元素包已经包含了该类名false - 元素中不存在该类名 |
改变元素节点的引用地址
比如图片的引用地址和a标签的跳转地址用 元素.src 和 元素.href
更改元素节点的HTML属性 setAttribute( ) 和 getAttribute( )
不符合W3C标准的属性,例如自定义属性名的属性,要使用 setAttribute( ) 和 getAttribute( ) 来设置,读取。格式是:
元素.setAttribute(属性名,属性值);
元素.getAttribute(属性名); //返回属性名对应的值
例如:
div1.setAttribute('data-n',10);
div1.getAttribute('data-n');
setAttribute() 修改元素的类名用的是 class 而不是 className
removeAttribute() 删除元素节点的HTML属性
removeAttribute('属性名')
更改元素节点的自定义属性data
JavaScript的dataset属性。
元素节点.dataset.data后面的字符 = 'data-*的值';
如果后面的字符还是多个减号连接,那么data后面的字符用小驼峰式命名法。
节点的创建、插入、移动、移除、克隆
方法名 | 功能 |
---|---|
document.createElement( ) | 创建一个指定tag name的HTML元素 |
父节点.appendChild(孤儿节点) | 将新创节点挂载成最后一个子节点 |
父节点.insertBefore(孤儿节点,标杆节点 ) | 将新创节点挂载成标杆节点前一个子节点 |
父节点.removeChild( ) | 从DOM父节点中删除一个子节点 |
子节点.remove( ) | 从DOM中删除一个子节点 |
老节点.cloneNode( ) | 克隆出一个新的孤儿节点 |
节点.replaceChild(新节点,旧节点) | 替换节点 |
节点创建
document.createElement( )
方法用于创建一个指定标签名的HTML元素,例如:
var div1 = document.createElement('div');
此时新创建的节点是“孤儿节点”,只生成在内存中,需要挂在到DOM树上才能看见他,也就是要“孤儿上树”,此时就需要使用孤儿上树的两个插入方法。
其他节点创建方法
方法名 | 作用 |
---|---|
document.createTextNode( ) | 创建一个文本节点 |
document.createComment( ) | 创建一个注释节点 |
document.createDocumentFragment( ) | |
创建一个虚拟的节点对象 |
节点插入
孤儿就是孤儿,要挂数只能找父亲,然后让一个野生父亲使用 appendChild( )
和 insertBefore( )
方法即可。
appendChild( )
任何已经在DOM树上的节点,都可以调用appendChild( ) 方法,它可以将孤儿节点挂载到它的内部,使孤儿节点成为最后一个子节点。
父节点.appendChild(孤儿节点);
insertBefore( )
任何已经在DOM树上的节点,都可以调用insertBefore( ) 方法,它可以将孤儿节点挂载到它的内部,成为它的“标杆子节点”之前的节点。
父节点.insertBefore(孤儿节点,标杆节点);
创建和插入练习:九九乘法表
<body>
<table></table>
<script type="text/javascript">
var table = document.getElementsByTagName('table')[0];
for (var i = 1;i<=9;i++) {
var tbr = document.createElement('tr');
table.appendChild(tbr);
for (var j=1;j<=i;j++) {
var trd = document.createElement('td');
trd.innerText = i+'*'+j+'='+i*j;
tbr.appendChild(trd);
}
}
</script>
</body>
元素的移动
appendChild( ) 和 insertBefore( ) 还可以用作元素的移动,因为 一个节点不能同时位于DOM树的两个位置
如果将已经挂载到DOM树上的节点成为appendChild( ) 和insertBefore( ) 的参数,这个节点将会被移动,使用方法:
新父节点.appendChild(已经有父亲的节点);
新父节点.insertBefore(已经有父亲的节点,标杆子节点);
删除节点
父节点.removeChild(要删除的子节点);
子节点.remove(要删除的节点);
removeChild( ) 方法从DOM中删除一个子节点。
意味着节点不能主动删除自己,必须由父节点删除它。
克隆节点
var 孤儿节点 = 老节点.cloneNode( );
var 孤儿节点 = 老节点.cloneNode(true);
cloneNode( ) 方法可以克隆节点,克隆出的节点是孤儿节点。
参数可以传入一个布尔值,默认false,false意味着只克隆元素本身不管子节点。
如果是true则采用深度克隆,该节点的所有后代节点也都会被克隆。
替换节点
替换父节点中的子节点。用法:
父节点.replaceChild(新的子节点,旧的子节点)
此方法如果用在已经上树的节点上,会拉下来重新上树,还是那个道理,一个dom节点不能位于树上两个位置
事件
事件是实现交互的核心。用户与界面做出的交互动作都可以称为事件。例如“当用户点击元素时,当鼠标移动到元素上时,当文本框内容被改变时,当网页已加载完毕时。”
事件的三种模型
DOM0级:标签上直接绑定的onXXX事件和js用onXXX绑定的事件
IE:IE特有模型,解决了某三个DOM0级的问题。
DOM2事件模型:addEventListener方式添加事件,可以监听事件冒泡和捕获阶段阶段
常见事件
常见的鼠标事件
事件名 | 事件描述 |
---|---|
click | 鼠标单击 |
dblclick | 鼠标双击 |
mousedown | 鼠标按键按下 |
mouseup | 鼠标按键松开 |
mousemove | 鼠标按键移动 |
mouseenter、mouseover | 鼠标进入 |
mouseleave、mouseout | 鼠标离开 |
contextmenu | 右击鼠标时触发并打开上下文菜单 |
mousewheel | 鼠标滚轮 |
只有mouseup和mousedown区分鼠标键,button值0、1、2
如何解决mousedown和click冲突(例如a标签拖拽后会出发click事件):
利用时间戳和开关关闭click事件
mouseenter不冒泡,mouseover冒泡
常见的键盘事件
事件名 | 事件描述 |
---|---|
keydown | 键盘的键被按下(可响应所有按键,先于press执行) |
keypress | 键盘的键被按下,只能识别字符按键,例如方向键和功能键无法识别 |
keyup | 当某个按键被松开 |
keypress返回的ASCII码(例charCode)可转字符
常见的表单事件
事件名 | 事件描述 |
---|---|
input | 当用户改变域的内容 |
change | 当用户改变了域的内容后 |
focus | 当某元素获得焦点(比如tab键跳转到或鼠标点击) |
blur | 当某元素失去焦点 |
submit | 当表单被提交 |
reset | 当表单被重置 |
常见的页面(窗体)事件监听
事件名 | 事件描述 |
---|---|
load | 当页面或图像完全加载 |
unload | 当用户退出页面 |
scroll | 滚动滚动条 |
resize | 窗体大小改变时 |
事件监听
DOM允许我们书写JavaScript代码以让HTML元素对事件做出反应。
“监听”就是让计算机随时能够发现这个事件发生了,从而执行一些预先编写的语句。主要有两种方法。
- 元素.onXXX = function(){}
- 元素.addEventListener('',function(){},BOOLEAN);
两种监听区别
- onXXX只能监听冒泡阶段,addEventListener可以开启捕获阶段。
- onXXX只能绑定一个函数,后写的会覆盖。addEventListener可多次复写。
事件监听解除
- 元素.onXXX = null;
- 元素.removeEventListener('事件类型',函数,false);
- IE独有:元素.detachEvent('onXXX',函数);
事件传播
当元素形成嵌套结构时,事件就会发生传播,传播分为:
- 事件捕获:结构上的父子元素,开启捕获后,事件执行从父元素捕获到子元素。
- 事件冒泡:结构上的父子元素关系,时间会从子元素冒泡到父元素。
- onXXX监听方法只能监听到冒泡阶段
- addEventListener第三个参数为true时会开启捕获监听。
- 嵌套结构的最内部元素不再区分捕获和冒泡,冒泡写在前面先执行冒泡,捕获写在前面先执行捕获。
- 冒泡阶段的onXXX和addEventListener谁先写先执行哪个函数。
阻止事件传播: event.stopPropagation( ); 方法,或者ie678的event.cancelBubble=true;
事件对象
事件处理函数提供一个形式参数,他是一个对象,封装了本次事件的细节。这个形参通常用字母e或单词event表示:例如
div.onclick = function(e){ }
div.addEventListener('click',function(event){},false);
谁是实参呢,实参由浏览器提供,当事件触发时,浏览器会打包所有事件对象传入事件处理函数形参中。
实际上并不用形参实参的说法,实测有一个event对象就是事件对象。
鼠标位置属性
属性名 | 属性描述 |
---|---|
offsetX、offsetY | 鼠标指针相对于事件源元素的X轴、Y轴坐标 |
clientX、clientY | 鼠标指针相对于浏览器视口的X轴、Y轴坐标 |
pageX、pageY | 鼠标指针相对于整张网页的X轴、Y轴坐标 |
screenX、screenY | 鼠标指针相对于电脑屏幕的X轴、Y轴坐标 |
键盘charCode属性和keyCode属性
e.charCode属性通常用于onkeypress事件中,表示用户输入的字符的ASCII码。
e.keyCode属性通常用于onkeydown事件和onkeyup中,表示用户按下的按键的键码。
charcode字符码
字符 | 字符码 |
---|---|
数字0~数字9 | 48~57 |
大写字母A~Z | 65~90 |
小写字母a~z | 97~122 |
keycode键码
按键 | 键码 |
---|---|
数字0~数字9 | 48~57 |
字母不分大小a~z | 65~90 |
四个方向左上右下 | 37、38、39、40 |
回车键 | 13 |
空格键 | 32 |
元素offset系列属性
- 该系列属性只读,不可写,且读出来的数值是一个Number类型数值
- 可以得到元素的偏移位置,返回不带单位的数值。这个数值以开启定位的父元素为准。
ELE.offsetTop
ELE.offsetLeft - 可以得到元素的宽度和高度,包含padding和border,即元素盒子的大小。
ELE.offsetWidth
ELE.offsetHeight - 返回带有定位的父亲,否则返回body
元素的client系列属性
- clientWidth 和 clientHeight 得到元素盒子除了border以外的尺寸大小。
- clientLeft 和 clientTop 不常用,相当于左边框和上边框的大小
元素的scroll系列属性
- scroll系列属性要出现滚动条才更好理解更好用
- scrollWidth 和 scrollHeight 得到元素盒子除了border外的所有内容的尺寸大小。
- scrollTop 和 scrollLeft 得到元素盒子被卷去的尺寸长度。该属性可用于“滑到底部才可以点击确定”
获取元素在页面上的属性
- 可以用
window.getComputedStyle(元素名,null);
//第一个值传的是需要获取计算样式的元素,第二个值传入是否获取的是伪元素 - 可以用
ELE.getBoundingClientRect()
,例如ELE.getBoundingClientRect().left
事件的方法
preventDefault() 方法 阻止默认事件
e.preventDefault( ) 方法用来阻止事件产生的“默认动作”。
例如鼠标滚动的动作,表单提交,a标签跳转,右键菜单等。
一些特殊的业务需求,需要阻止事件的“默认动作”。
阻止默认事件还可以用return false;
stopPropagation( ) 方法 阻止事件传播
e.stopPropagation( ) 方法用来阻止事件继续传播
在一些场合,非常有必要切断事件继续传播,否则会造成页面特效显示出bug
事件委托
把事件绑定到父元素上,用 event.target 定位到子元素(利用了事件冒泡)
event.target 触发此事件的最早元素,即“事件源元素”
event.currentTarget 事件处理程序附加到的元素
事件委托的使用场景:
- 当有大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存开销。
- 当由动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听
注意事项:
- 不能委托不冒泡的事件给祖先元素
- 最内层元素不能再有额外的内层元素了
移动端事件
- 触摸事件:touch事件、pointer事件。
- 手势事件:gesture
- 传感器事件:sensor
移动端使用 tansform:translate3d();
会开启GPU加速,提高性能。
touch 事件:
touchstart、touchmove、touchend、touchcancel(比如操作时突然来电话)
touchstart 必须要在绑定事件的元素触发,触发start后,touchmove 和 touchend 手指移除元素依然可触发。
touch 事件的 TouchEvent 对象
- event.touches
- event.targetTouches
- event.changedTouches
这些对象都是一个 Touchlist,单指操作只需提取一个即可,比如 event.changedTouches[0]
- touches是正在触摸屏幕的所有手指的列表。
- targetTouches是正在触摸当前DOM元素的手指列表。
- 如果是绑在dom元素的事件以上两个是一样的
- changedTouches是手指状态发生了改变的列表,从无到有 或者 从有到无
touches 和 targetTouches 很多情况下在 touchend 事件里没有值,所以推荐使用 changedTouches
其他触摸事件
touch 只是基础事件,还有其他例如单击,长按,双指等事件。
可以自行封装这些高级事件,也可使用例如 hammer.js
等别人封装好的事件库。
定时器和延时器
定时器
setInterval( ) 函数可以重复调用一个函数,在每次调用之间具有固定的时间间隔,写法:
setInterval(function(){},1000)
第一个参数是函数,第二个参数是间隔时间,函数里的语句块将会在每过间隔时间后执行一次。
setInterval函数可以接受第3、4、5...个参数,它们将按顺序传入函数,例如。
setInterval(function(a,b){
},1000,1,2)
//第三个参数开始,表示传入函数内的实参
清除定时器
clearInterval( ) 函数,圆括号内传入要清除的定时器
延时器
setTimeout( ) 函数可以设置一个延时器,当指定时间到达后,会执行函数一次,不再重复执行,写法:
setTimeout(function(){
},1000)
清除延时器
clearTimeout( ) 函数可以清除延时器
初识异步语句
定时器和延时器是两个异步语句。
异步(asynchronous):不会阻塞CPU继续执行其他语句,当异步完成时,会执行“回调函数”(callback)
//例子:
setInterval(function(){
console.log('a');
},1000)
console.log('b');
//会先输出b,再输出aaaaaa
异步语句不会阻塞程序的正常执行。
(这里记个记号,异步这部分是我学前端的第一个大坎,原因是我看的好多教程都没有讲解异步底层原理,直到去看了pink老师的讲解才理解了异步,因为这点导致我后面的promise和ajax学得一知半解的)
函数节流
延时器可以用作函数节流,即加个开关,定时打开或关闭开关,需足够时间才能执行下一次函数,具体方法:
var lock = true;
function 需要节流的函数名(){
if(!lock) return;
lock = false;
setTimeout(function(){
lock =true;
},节流时间);
}
BOM
Browser Object Model 浏览器对象模型,是JS与浏览器窗口交互的接口。
一些与浏览器改变尺寸、滚动滚动条相关的,都要借助BOM技术。
window对象
window对象是当前js脚本运行所处的窗口,而这个窗口中包含DOM结构,window.document属性就是document对象。
在有标签页功能的浏览器中,每个标签拥有自己的window对象,不共用。
全局变量是window的属性
var a = 10;
//控制台输入window.a返回10
这意味着多个js文件之间是共享全局作用域的。
内置函数普遍是window的方法
如setInterva( ) 、alert( ) 等内置函数,普遍是window的方法
window属性
浏览器窗口尺寸相关属性
属性 | 意义 |
---|---|
innerHeight | 浏览器窗口的内容区高度,包含滚动条(如果有) |
innerWidth | 浏览器窗口的内容区高度,包含滚动条(如果有) |
outerHeight | 浏览器窗口的外部高度 |
outerWidth | 浏览器窗口的外部宽度 |
浏览器的滚动条是有宽度的,如果想要获得不包含滚动条的窗口宽度,要用
document.documentElement.clientWidth
窗口改变事件resize见window事件
浏览器窗口滚动条相关属性
属性 | 意义 |
---|---|
window.pageYOffset | 获取当前滚动条Y轴已滚动像素值 |
window.pageXOffset | 获取当前滚动条X轴已滚动像素值 |
window.scrollY | |
window.scrollX | |
document.documentElement.scrollTop | |
document.documentElement.scrollLeft |
document.documentElement.scroll可读可写,可用作返回顶部。
滚动条相关事件scroll见window事件
浏览器窗口滚动条相关方法
方法名 | 作用 |
---|---|
window.scoll( ) | |
window.scollTo( ) | |
window.scrollBy( ) |
传入(x,y)值滚动到输入的位置。
scroll和scrollTo一样,scrollBy是在现基础上累加
window方法
事件名 | 事件描述 |
---|---|
load | 当页面或图像完全加载 |
unload | 当用户退出页面 |
scroll | 滚动滚动条时 |
resize | 窗体大小改变时 |
navigator对象
属性 | 意义 |
---|---|
appName | 浏览器官方名称 |
appVersion | 浏览器版本 |
userAgent | 浏览器的用户代理(含有内核信息和封装壳信息) |
platform | 用户操作系统 |
history对象
history.back(); //等同于浏览器回退按钮
history.go(-1); //同上
history.go(1); //前进一个页面
history,forward(); //同上
a标签实现回退功能
<a href="javascript:history.back();">回退</a>
location 对象
location.href 返回当前页面URL
location.reload(true) 传入true强制从服务器重新渲染页面
location.search 当前浏览器的get请求查询参数
面向对象
认识对象
Object 是键值对的集合。
写法:键和值之间用冒号分割,每组键值之间用逗号分隔。
如果对象的属性键名不符合js标识符命名规则,这个键名必须用引号包裹。
//方法一:
var 变量 = {
键1 : 值1,
键2 : 值2
}
//方法二:
var 变量 = new Object();
属性的访问
用 点语法 访问对象中指定的值,
如果属性名不符合js标识符命名规则,则必须用方括号里加引号里写属性名。
如果属性名以变量形式存储,也必须用方括号。
可用 hasOwnProperty( )
方法判断属性是否是自己的,
或者使用 in
判断属性是否是自己和父级的。
属性的更改
直接用赋值运算符重新对属性赋值即可。
属性的创建
如果对象本身没有属性,直接用 点语法 写属性名,并用赋值运算符赋值即可,全局变量原理也是这样。
属性的删除
用delete操作符,例如:
delete 对象.属性;
对象的方法
如果某个属性值是函数,着它也被称为对象的方法。
方法的调用
使用点语法即可。
方法和函数
方法是对象的函数属性,需要用对象打点调用,函数也可以用window打点,所以二者在js里无差别。
对象的遍历
用 for(var 循环变量 in 对象){} 循环,例:
for(var k in obj){
console.log(obj[k]);
}
循环变量可随意命名,一般为k或prop,循环变量会一次变为对象的每一个键。在for in 遍历里,一定要写成 对象[] ,的形式,因为循环变量是变量,中括号形式才能没问题循环所有形式的变量。
对象的深浅克隆
慕课网方法:
function deepClone(o){
if (Array.isArray(o)) {
var result = [];
for (var i = 0;i < o.length;i++) {
result.push(deepClone(o[i]));
}
}else if(typeof o == 'object'){
var result = {};
for (var k in o) {
result[k] = deepClone(o[k]);
}
}else{
var result = o;
}
return result;
}
渡一方法
function deepclone(origin,target){
var target = target||{};
for(var prop in origin){
if(origin.hasOwnProperty(prop)){
if(typeof(origin[prop]) == 'object'){
if (Object.prototype.toString.call(origin[prop])=='[object object]') {
target[prop] = {};
} else{
target[prop] = [];
}
deepclone(origin[prop],target[prop]);
}else{
target[prop] = origin[prop];
}
}
}
return target;
}
认识函数的上下文
函数中可以用 this 关键字,他表示函数的上下文。
this 指向必须通过调用函数时的“前言后语”来判断。
函数的上下文由调用方式决定,函数只有被调用,才会产生上下文。
上下文规则
- 对象打点调用他的方法函数,则函数的上下文是这个打点的对象。
- 圆括号直接调用函数,则函数上下文是 window 对象。
- 数组(或类数组对象)枚举出函数进行调用,则上下文是这个数组(或类数组对象)。
- 在IIFE函数中,上下文是window对象。
- 定时器、延时器调用函数,上下文是 window 对象。
- 事件处理函数的上下文是绑定事件的DOM元素,所以通常需要备份上下文。
call 和 apply
能指定函数的上下文,例:
函数.call(上下文)
函数.apply(上下文)
区别: 当函数需要参数时,call 要用逗号罗列参数; apply 需把参数写在数组中。
bind
bind 和 call 差不多,不过它只改变this指向,不会调用函数,执行完bind之后会返回一个已经修改好this指向的函数,可以用一个变量接收
function fn(){}
var ojj = {}
var bii = fn.bind(o)
用new调用函数
用new接函数调用,会从函数内部返回一个对象。
JS规定,使用new操作符调用函数会进行“四步走”用new调用函数的四步走:
- 函数体内会自动创建出一个空白对象。
- 函数的上下文(this)会指向这个空白对象。
- 函数体内的语句会执行。
- 函数会自动返回上下文对象,即使函数没有return语句。
上下文规则总结表格
情况 | this指向 |
---|---|
对象.函数() | 对象 |
函数直接调用 | window |
数组[下标] () | 数组 |
IIFE函数 | window |
定时器、延时器 | window |
DOM事件处理函数 | 绑定DOM元素 |
call和apply | 任意指定 |
用new调用函数 | 秘密创建出的对象 |
构造函数
function 函数名(参数1,参数2){
this.参数1同名 = 参数1;
this.参数2同名 = 参数2;
}
然后用 new 调用函数执行:
构造函数开发者约定函数命名时首字母要大写,不用new调用不能正常工作。
构造函数用来“构造新对象”,它内部语句会为新对象添加属性和方法,完成对象初始化。
“类”与实例
JS中是没有类这个概念的,因为JS的对象与真正的面向对象语言有差别,叫“基于对象”。
构造函数可以看为是“类”,构造函数new出来的对象可以看作为“实例”。
判断这个实力是否是一个“类”构造出来的,可以用
A instanceof B
prototype 原型
任何函数都有prototype属性。
prototype属性是个对象,它默认拥有constructor属性指回函数。
普通函数的prototype属性没有任何用处,而构造函数的prototype非常有用,构造函数的prototype是它实例的原型。
原型链
[图片上传失败...(image-be2be0-1649463907747)]
hasOwnProperty 可以检查对象是否真正“自己拥有”某属性或方法。
in 可以检查对象是否能访问到这个属性或方法。
无法直接访问的意思是,构造函数自身无法直接调用或访问原型上的方法或属性,而实例可以,构造函数自身想访问原型上的方法必须先找原型再调用
在prototype上添加方法
将方法添加到构造函数上时,生成的每一个新对象就会多一个新占用内存的方法,会造成内存浪费。
解决方法就是将方法写道prototype上,只生成一个堆内存就够了。
重写constructor
prototype里面自带有一个constructor方法用于指回是哪个构造函数构造出来的,但通常一个构造函数的原型方法里需要塞下多个方法的时候我们通常需要用对象的形式,例如
function FFnn(){
}
FFnn.prototype = {
say(){}
sing(){}
}
通常会这样子通过对象的形式往里面添加多个方法,那么这个时候自带的construtor就没有了,所以这个时候如果你还想要有一个constructor方法指回构造函数,记得重新自己把constructor写上
FFnn.prototype = {
constructor:FFnn
say:function(){}
sing:function(){}
}
继承
两个无关的构造函数,但一个构造函数需要的属性在另一个函数上也有,就可以利用原型链原理达成继承。
方法:使子函数的原型等于另一个函数的实例:
Fun2.prototype = new Fun1();
继承——圣杯模式
function inherit(Target,Origin){
function F(){};
F.prototype = Origin.prototype
Target.prototype = new F();
Targer.prototype.constructor = Targer;
Targer.prototype.uber = Origin.prototype;
}
包装类
包装类的目的就是为了让基本类型值可以从他们的构造函数的prototype上获得方法。
包装类构造出来的值是一个对象,type of 返回 object ,可以参与运算。
Math 数学对象
除了之前介绍的那些 Math 方法以外,我们还有更多常用的Math方法。
四舍五入 Math.round()
实现四舍五入到小数点后某位:
数字 * 10的n次方 然后四舍五入 再 / 10的n次方。
得到参数列表最大最小值
Math.max() 得到参数列表最大值。
Math.min() 得到参数列表最小值。
如何求数组最大最小值:Math.max() 和 Math.min() 结合apply,例如:
var arr = [3,2,9];
var max = Math.max.apply(null,arr);
//ES6之后还可以
Math.max(...arr);
Date 日期对象
使用 new Date()
即可得到当前时间的日期对象,他是object类型值。如果什么参数都不加就是此时此刻的时间。
使用new Date(2021,6,12)
即可得到指定日期的日期对象,注意第二个参数是从0开始算的,6表示7月份。
也可以new Date('2021-7-12')
这样写,算时区。
常见日期方法
方法 | 功能 |
---|---|
getDate() | 得到日期1~31 |
getDay() | 得到星期0~6 |
getMonth() | 得到月份0~11 |
getFullYear() | 得到年份 |
getHours() | 得到小时数0~23 |
getMinutes() | 得到分钟数0~59 |
getSeconds() | 得到秒数0~59 |
时间戳
表示从1970年1月1日零点整距离某时刻的毫秒数。
可以通过 getTime()
方法或 Date.parse()
函数可将日期对象改为时间戳。
或者直接在date对象前面加个加号 + 即可,例如 var chuo = + new Date()
通过 new Date(时间戳)
可将时间戳改为日期对象
正则表达式
regular expression 描述了字符串的“构成模式”,经常被用于检查字符串是否符合预定的格式要求。
正则表达式“按位”描述规则,是指它是一位一位的描述子字符串的构成形式。
正则表达式的创建
使用 /内容/ 的语法形式,可以快速创建正则表达式
也可以使用 new RegExp('内容') 这种方法使用一些字符可能要用转义。
type of 检查正则表达式返回object。
元字符
元字符 | 功能 |
---|---|
\d | 匹配一个数字 |
\D | 匹配一个非数字字符 |
\w | 匹配一个单字字符 |
\W | 匹配一个非单字字符 |
\s | 匹配一个空白字符,包含空格制表符换行符 |
\S | 匹配一个非空白字符 |
. | 任意字符 |
^ | 匹配开头 |
# | 匹配结尾 |
量词
量词 | 意义 |
---|---|
* | 匹配前一个表达式0次或多次,等价于{0,} |
+ | 匹配前一个表达式1次或多次,等价于{1,} |
? | 匹配前一个表达式0次或1次 |
{n} | n是一个正整数,匹配了前面一个字符刚好出现了n次 |
{n,} | n是一个正整数,匹配前一个字符至少出现了n次 |
{n,m} | n和m都是正整数,匹配前面的字符至少出现n次,最多m次 |
方括号表示法
- 使用方括号,比如[xyz]可以创建一个字符集合,表示匹配方括号中的任意字符,一个方括号代表一位。
- 可以用短横 - 来指定一个字符范围,^ 表示否定
例如[0-9] [a-z] [A-Z] [0-9a-zA-Z_] [0-68-9]
修饰符
i | 不区分大小写搜索 |
g | 全局搜索 |
正则表达式的方法
test() 测试某字符串是否匹配正则表达式,返回布尔值
exec() 根据正则表达式,在字符串中进行查找,返回结果数组或者null。exec方法开启全局搜索后可逐条遍历(ES6迭代器概念)。
字符串的相关正则方法
方法 | 简介 |
---|---|
search() | 在字符串中根据正则表达式及逆行查找匹配,返回首次匹配到的位置索引,测试不到则返回 -1 |
match() | 在在字符串中根据正则表达式进行查找匹配,返回一个数组,找不到则返回null |
replace() | 使用替换字符串替换掉匹配到的子字符串,可以使用正则表达式 |
split() | 分割字符串为数组,可以使用正则表达式 |
本地存储
- 本地存储不会发送到服务器端,且二者大小有所区别,sessionStorage能存储5m左右,localstorage可以储存10m左右。(单个域名下)
- 二者都可以在浏览器F12中,Application-Storage 中找到,分为 key 和 value。
- 使用方法为
sessionStorage.setItem(key,value)
和localStorage.getItem(key,value)
- 二者存储的键和值只能是字符串类型,如果不是字符串类型,也会先转成字符串再存进去。或者存入JSON类型值
- 不同域名不能共用 sessionStorage 和 localStorage
- IE7及以下不支持
sessionStorage
sessionStorage 是一种浏览器本地存储数据的方式,只存储在本地,不发送到服务器端。当会话结束(例如关闭浏览器时),sessionStorage 中的数据会清空。基本用法:
- 增加数据setItem方法:
sessionStorage.setItem(key,value);
- 查询数据getItem方法:
sessionStorage.getItem(key);
- 删除数据removeItem方法:
sessionStorage.removeItem(key);
全部清空clear方法:sessionStorage.clear();
- 改更数据setItem方法:同key属性覆盖
其他属性:
- length 属性:
sessionStroage.length()
获取长度 - key方法,参数时数据,代表key的索引,根据索引获取key值。
sessionStorage.key(0)
localStorage
localStorage 时持久化的本地存储,除非手动清楚或者满了自动清除,否则数据永远不会过期。
- 增加数据setItem方法:
localStorage.setItem(key,value);
- 查询数据getItem方法:
localStorage.getItem(key);
- 删除数据removeItem方法:
localStorage.removeItem(key);
全部清空clear方法:localStorage.clear();
- 改更数据setItem方法:同key属性覆盖
- length 属性:
localStroage.length()
获取长度 - key方法,参数时数据,代表key的索引,根据索引获取key值。
loacalStorage.key(0)