JavaScript基础
0、计算机语言简介
高级语言都需要经过编译,代码需要转换为机器码然后由计算机执行
- 根据编译时机的不同,高级语言又分为两种:
编译型语言
- 代码编写完毕立刻进行编译转换为机器码,然后将机器码交给计算机执行
- 特点:
1.运行速度快
2.兼容性差
- 例子:
C、C++ ...
解释型语言
- 代码编写完毕不会转换为机器码,而是由计算机一边执行一边编译
- 特点:
1.运行速度慢
2.兼容性好
- 例子:
Java、Python、Ruby、JavaScript ...
1、JavaScript简介
-
JavaScript是 一个运行在浏览器的脚本语言
-
但是时至今日js的运行环境已经不仅仅局限于浏览器(Node.js)
-
js是一个动态类型的语言
-
js的标准被称为ECMAScript(ECMA262)
-
自2015年起,ES标准每年(6月)更新
ES2015(ES6)
ES2016
....
ES2020
1.0、这一阶段主要学习内容
-
ES标准
- 基本语法
-
DOM
- 文档对象模型,用来操作网页
-
BOM
- 浏览器对象模型,用来操作浏览器
1.1、输出语句
-
alert() 用来在窗口弹出一个警告框
alert('今天天气真不错!'); -
console.log()用来在控制台输出一个日志
console.log('你猜我在哪出现?'); -
document.write()用来像网页中输出一个内容
document.write('哈哈 哈哈!!');
1.2、js编写位置
-
可以直接将js代码编写在script标签中
<script> alert('Hello!'); </script> -
可以将js代码编写在外部的js文件中,然后通过script标签进行引入,script标签要么用来引入,要么用来写代码。可以添加defer
<script src="app.js"></script> -
还可以将js代码编写在标签的指定属性中
<button onmouseenter="alert('你点我干嘛!');">点我一下</button> -
可以将js代码编写在href属性后边的,以javascript: 开头
<a href="javascript:alert('hello');">我是一个超链接</a> <a href="javascript:">我是一个超链接</a> /不跳转链接
1.3、js基本语法
通过注释可以对代码进行解释说明,也可以禁止一些代码的执行。一定要养成一个良好的编写注释的习惯,注释要求要简单明了。
// 单行注释 ctrl+/
/**/多行注释 ctrl+shift+/
- JS中每一个语句都应该以分号结尾,在js中有自动添加分号机制,所以即使不写也不会报错
- JS中严格区分大小写
- JS会忽略多个空格和换行,所以可以通过空格和换行来对代码进行格式化
1.4、字面量和变量
字面量
- 字面量就表示的是一个值,它所表示的意思就是它的字面意思
- 比如:1, 2, 3 'hello' true ....
- 字面量可以在js中直接使用,但是通常不会这这么做
变量
- 变量可以用来存储字面量,一个变量可以存储任意类型的字面量
- 我们一般都是通过变量去存储字面量而不是直接使用字面量
// 创建一个变量
let a;
// 为变量赋值
a = 33;
// 声明和赋值同时进行
let age = 33;
注意:变量可以多组同时赋值
let b = xxx, c = yyy ;
const 用来声明一个常量,常量只能进行一次赋值无法修改
const b = 44;// b = 33;
// b为常量,无法赋值
alert(b);
1.5、标识符
- 在程序中所有的可以自主命名的内容都可以认为是标识符
- 比如:变量名、函数名、类名...
- 标识符需要遵循如下规范:
1. 标识符中可以含有字母、数字、_、$,但是不能以数字开头*
2. 标识符不能是JS中的关键字和保留字,也不建议使用js中内置函数名和变量名
3. 标识符应该要遵循驼峰命名法
* - 驼峰命名法:
* 首字母小写,每个单词开头字母大写
* maxlength --> maxLength
* xxxyyyzzz --> xxxYyyZzz
1.6、数据类型
1.6.1、基本数据类型
基本数据类型是构成整个js世界的基石*
- 字符串(string)
- 数值(number)
- 布尔值(boolean)
- 空值(null)
- 未定义(undefined)
注意:- 在JS中所有的基本类型都是不可变类型,* 值一旦创建,就不可修改!
补充:
- JS中的变量并不直接存储,而是存储值的内存地址*
当我们访问变量时,是通过地址找到其对应的值
- 所以JS中的变量,其实更像是一个值的别名
1.6.2、字符串(string)
- js中的字符串需要使用引号引起来*
- 双引号或单引号都行但是不能混合使用*
- 引号不能跨行使用,同类型的引号不能嵌套*
- js中使用
\ 作为转义字符* ' 表示 '* " 表示 "* \n 表示 换行* \t 表示 制表符* \ 表示 \
模板字符串(老版本的浏览器不支持)
- 使用反单引号 ` 来创建模板字符串* (反单引号在ESC下方)
- 特点:*
1.可以换行,并保留字符串中的格式*
2.在模板字符串中可以直接嵌入变量*
变量的语法 ${变量}
let name = '石天凯';
str = `我好喜欢${name}, 因为他好帅!`;
alert(str);
1.6.3、数值(number)
在JS中,所有数值包含整数和浮点数(小数)都属于number类型
-
在JS中可以确保大部分的整数运算得到一个精确的结果
-
在JS中,小数运算有可能得到一个不精确的结果
- 所以,在JS中不要直接对精度要求高的运算(尤其是涉及到钱的)
-
当数值超过一定范围后,会使用 Infinity 来表示
- Infinity 表示正无穷
- NaN也是一个特殊数字,表示 Not a Number 非法数字
let num1 = 10;
let num2 = 3.5;
let num4 = 0.1 + 0.2;
alert(num1);
-
typeof运算符
-
用来检查一个值的类型
-
使用typeof检查一个字符串时,会返回 string*
-
检查一个数值时,它会返回 number
console.log(typeof num1);
-
-
其他进制的数字:*
- 二进制 0b开头
- 八进制 0o开头
- 十六进制 0x开头
1.6.4、布尔值(boolean)
-
布尔值用来进行逻辑判断*
-
布尔值只有两个:
- true 真
- false 假
-
使用typeof检查一个布尔值会返回boolean
1.6.5、空值(null)
-
null 通常用来表示一个空的对象,一个不存在的东西
- null类型只有一个值 就是 null
-
使用typeof检查一个null时,它会返回 'object
1.6.6未定义(undefined)
-
undefined 表示未定义,当我们定义一个变量但是不赋值时它就是undefined
- undefined类型的值只有一个,就是undefined
-
使用typeof检查一个undefined时,会返回'undefined
3、类型转化
- 类型转换,指其他数据类型转换成string、number、boolean
3.1、转换为字符串
-
方式一
-
调用被转换类型的toString()方法
-
调用xxx的yyy方法--->xxx.yyy()
-
由于null和undefined中不含有tostring()方法,所以对它俩调用时会报错。
a = a.toString();
-
-
方式二
-
调用String()函数,来将被转换类型转化为字符串
-
调用xxx函数--->xxx()
-
原理:对于具有toString()方法的值,就是直接调用toString()对其进行转换,但是对于null来说,它是直接将其转换为'null',undefined直接转换为'undefined'.
a = String(a);
-
3.2、转换为数字
- 使用Number()函数来将一个其他类型转换为数字
| 类型 | 值 | 转换为 |
|---|---|---|
| 字符串 | 合法数字 | 对应数字 |
| 非法数字 | NaN | |
| 布尔值 | true | 1 |
| false | 0 | |
| 其他 | null | 0 |
| undefiend | NaN |
let a = '123'; // 123
a = '3.14'; // 3.14
a = 'abc'; // NaN
a = '0xff'; // 255
a = 'Infinity' // Infinity
a = '123px'; // NaN
a = true; // 1
a = false; // 0
a = null; // 0
a = undefined; // NaN
console.log(a, typeof a);
-
另外两个专门将一个字符转换为数字的
-
parseInt()
- 将一个字符串解析为一个整数
- 该函数会自左向右依次读取一个字符中的字符,直到找到字符串中所有的合法整数为止
- 还可以用来对一个数字进行取整
-
parseFloat()
- 将一个字符串加息为一个小数
-
3.3、转换为布尔值
-
使用Boolean()函数将其他类型转换为布尔值
- 所有表示没有或错误的都会转换为false
- 对于数值:除了0和NaN,都是true
- 对于字符串:空串是false,其他都是true
- null和undefined都是false
- 一般情况对象都会转换为true
-
总结:false的情况:0、NaN、' '、false、null、undefined
let a = 10; // true a = -44; // true a = 0; // false a = NaN; // false a = Infinity; // true a = 'Hello'; // true a = 'false'; // true a = ' '; // true; a = ''; // false a = null; // false a = undefined; // fals console.log(a, typeof a); a = Boolean(a); console.log(a, typeof a);
4、运算符(操作符)
-
通过运算符可以对一个值或多个值进行各种运算
- 对一个值进行运算的运算符,称为一元运算符
- 对二个值进行运算的,称为二元运算符
- 以此类推
4.1算数运算符
| 运算符 | 说明 |
|---|---|
| + | 对两个值进行加法运算 |
| - | 对两个值进行减法运算 |
| * | 对两个值进行乘法运算 |
| / | 对两个值进行除法运算 |
| ** | (幂运算)求一个值的几次幂 |
| % | (取模)两个数相除取余数 |
let result = 10 + 33;
result = 10 - 5;
result = 10 / 0; // Infinity
result = 2 * 10;
result = 16 ** 0.5; // 16的平方根
result = 10 % 4; // 10/4,余2
-
除了字符串的加法以外,对其他类型的值进行算数运算时,都会转换为数值然后再进行运算。
result = 1 + true; // 2 result = 456 * null; // 0 -
所以我们可以通过为一个任意值-0、*1等方式来将其转化为数字,这种方式称为隐形类型转换,它的原理和Number()函数一样,但更简单一些。
a = 0 + a; a = a - 0; a = a * 1; a = a + 0; -
任何值和NaN做任何运算结果都是NaN(除了字符串加法)
result = 33 - undefined; // NaN
4.2、字符串的加法
-
任何值和字符串做加法时都会转换为字符串,然后在和字符串进行拼串。
-
利用这个特点,可以通过为一个任意值加上一个空串('')的形式,来将其转换为字符串,它的原理和String()函数是一样的,但是这种方式要简单一些。
let a = 'hello' + 123; //hello123 a = 'hello' + NaN; //helloNaN a = 1 + '2' + 3; // '123'
4.3、一元运算符
- ( typeof )检查一个值得类型
- ( + )一元的+(正号),不会对数值产生任何影响
- ( - )一元的-(负号)。会对数值进行符号位取反
-
对于非数值类型的值进行正负运算式,它会先将其转换为数值然后再运算。
-
利用一元的+,可以将一个任意类型的值转换为数值,原理同Number()函数,但是更加简单。
let a = +10; //10 a = -true; //-1 a = +false; //0 a = +null; //0 a = 1 + +'2' + 3; //6 console.log(a);
4.4、自增和自减
-
自增(++)
- 自增分为前++(++a)和后++(a++)
- 无论是++a还是a++,对于a来说都是一样的,都会使变量a立即自增1
- a++和++a的返回结果不一样,
- a++返回的是变量自增前的值
- ++a返回的是变量自增后的值
-
自减(--)
- 原理同自增一样
4.5、赋值运算符
-
用来将一个值赋值给一个变量
- ( = )=号用来将负号右侧的值赋值给左侧的变量
- ( += )a += x 等价于 a = a + x
- ( -= )a -= x 等价于 a = a - x
- ( *= )a *= x 等价于 a = a * x
- ( /= )a /= x 等价于 a = a / x
- ( **= )a * *= x 等价于 a = a * * x
- ( %= )a %= x 等价于 a = a % x
那些情况会导致变量发生变化(为变量进行重新赋值)
-
对变量使用赋值运算符
a = xx* a += xx* a -= xx* ... -
对变量使用自增自减
a++ ++a a-- --a
4.6、逻辑运算符
-
( !)逻辑非
-
!可以对一个值进行非运算(取反操作)
-
如果值是true,则变成false,false变成true
-
如果对一个非布尔值进行逻辑非运算,它会先将其转换为布尔值,然后再取反
let bool = false; bool = !bool; //结果为true console.log('bool =', bool);
-
-
( && )逻辑与
-
可以对符号两侧的值进行与运算
-
与运算是找false的
-
与运算是短路的与,如果第一个值是false,则不会找第二个值
let result = true && true; // true result = true && false; // false result = false && true; // false result = false && false; // false
-
-
( || )逻辑或
-
可以对符号两侧的值进行或运算
-
或运算是找true的
-
或运算是短路的或,如果第一个值是true,则不会看第二个值
result = true || true; // true result = true || false; // true result = false || true; // true result = false || false; // false
-
-
对非布尔值进行逻辑运算(与运算/或运算),会首先将其转换成布尔值,然后再运算,最终返回原值。
-
与找false,或找true
let result = 'hello' && 1; // 1 result = 1 && 'hello'; // 'hello' result = 1 && NaN; //NaN result = NaN && 0; //NaN result = 0 && NaN; //0
-
4.7、关系运算符
-
用来比较两个值之间的大小等于关系,使用关系运算符时,如果关系成立则返回true,不成立则返回false
-
( > )比较左侧值是否大于右侧值
-
( >= )比较左侧值是否大于等于右侧值
-
( < )比较左侧值是否小于右侧值
-
( <= )比较左侧值是否小于等于右侧值
let result = 10 > 5; // true result = 10 > 10; // false result = 10 >= 10; // true result = 10 >= 7; // true result = 10 >= 17; // false
-
-
对于非数值类型的值进行大于小于比较时,浏览器会自动将其转换为数字然后再比较。
-
如果比较的是两个字符串的大小,则情况比较特殊
result = 10 < '55'; // true result = 10 < '5'; // false result = true < '5'; // true result = null <= undefined; // false
-
-
特殊情况:
- 当比较运算符的两侧都是字符串时,会诸位比较字符串的字符编码,**利用这个特点可以对一个字符串按照字母顺序进行排序。
4.8、相等和全等
-
相等运算符(==)
- 相等运算符用来比较两个值是否相等
- 相等运算符会对值进行自动的类型转换
- 如果比较的两个值的类型不同,会将其转换为相同类型然后再比较
- 通常情况下,不同类型都会转换为Number然后再比较
- null和undefined做相等比较时,会返回ture
-
全等运算符(===)
- 全等用来检查两个值是否全等
- 全等运算符不会发生自动类型转换(和相等的不同点)
- 如果两个值的类型不同,直接返回false
- null和undefined做全等比较时,返回false
-
不相等(!=)
- 比较两个值是否不相等
- 如果不相等返回true,相等返回false
- 会做自动类型转换,将两个值转换为相同的类型然后再比较
-
不全等(!==)
- 比较两个值是否不全等
- 如果不全等返回true,全等返回false
- 不会做自动的类型转换,如果两个值的类型不同,直接返回true
-
检查一个值是否是NaN时,需要使用函数 isNaN()
5、条件运算符
-
条件运算符(三元运算符,三目运算符)
-
语法:
- 条件表达式?语句1:语句2
-
执行流程:
-
条件运算符再执行时,会先对条件表达式进行求值判断
- 如果判断结果为true,则执行语句1
- 如果判断结果为false,则执行语句2
let num = 30; num > 20 ? alert('num比20大!') : alert('num比20小!');
-
6、代码优先级
-
和数学运算一样,js中运算也有优先级的问题
- 比如:先乘除后加减
- 遇到优先级不清的,可以通过()来改变优先级
7、代码块
-
在js中我们使用{}来为代码分组
- 一对大括号就是一个代码块
- 同一个代码块中的代码要么都执行,要么都不执行
- 代码块中使用let声明的变量,在代码块外部无法访问
- 但是使用var声明的变量没用块作用域,在代码块的外部也可以访问
8、流程控制语句
- 代码默认是按照自上向下的顺序一行一行执行的,但是仅仅是这样,并不能满足我们的开发需求,可以通过流程控制语句来改变程序执行的顺序。
8.1、条件判断语句(if语句)
-
语法:
- if ( 条件表达式 ){ 语句... }
-
执行流程:
-
if 语句在执行时,先对条件表达式求值判断
- 如果结果为true,则执行if后的语句
- 如果结果为false,则不执行
-
if 语句默认只会控制紧随其后的那条语句
let a = 123; if(a > 20){ alert('a比20大!'); //值为true,执行 }
-
8.2、if-else语句
-
语法:
-
if(条件表达式){ 语句... }else{ 语句... }
-
-
执行流程
-
if-else执行时,先对if后的条件表达式进行求值判断
- 如果值为true,则执行if后的语句
- 如果值为false,则执行else后的语句
-
8.3、if-else if-else语句
-
语法
-
if(条件表达式){ 语句... }else if(条件表达式){ 语句... }else if(条件表达式){ 语句... }else{ 语句... }
-
-
执行标准
- 它会自上向下依次对if后的条件表达式进行求值判断
- 如果判断结果为true,则执行当前if后的语句,执行完毕语句结束
- 如果判断结果为false,则继续向下判断,知道找到true为止
- 如果所有的判断结果都是false,则执行else后的语句
-
if-else if-else语句中,只会有一个代码块执行,代码块执行后,其他语句不会再判断,语句立即结束。
8.4、switch语句
-
语法
-
switch(条件表达式){ case 表达式: 语句... case 表达式: 语句... case 表达式: 语句... default: 语句... }
-
-
执行流程
-
switch-case语句在执行时,会自上向下依次将switch后的条件表达式和case后的表达式进行全等验证
-
如果全等,则自当前case处开始向下执行代码
-
如果不全等,则继续向下比较,知道找到全等的为止
-
如果所有的的比较都不成立,都是false,则自default处开始执行代码
let num = 2; switch (num) { case 1: alert('壹'); case 2: alert('贰'); //开始执行 case 3: alert('叁'); //执行 default: alert('输错了!'); //执行 }
-
8.5、循环语句
-
循环语句可以让指定的代码反复执行多次
-
循环有三种
- while语句
- do-while语句
- for语句
-
循环的三要素
-
初始化表达式,创建一个变量来控制循环的执行
-
条件表达式,设置循环的执行条件
-
更新表达式,修改初始化变量
let i = 0; while(i < 10){ console.log(i); i++; }
-
-
while语句
while(条件表达式){ 语句.... }-
执行流程
- while语句在执行时,会先对条件表达式进行求值判断
- 如果结果为false,则语句直接结束
- 如果结果为true,则执行while后的代码(循环体)
- 执行完毕,继续对条件单表达式进行求值判断,以此类推
-
条件表达式恒为true的循环,叫做死循环,会一直执行下去(慎用)
while(true){ alert('hello'); }
-
-
do-while循环
-
语法:
-
do{ 语句... }while(条件表达式)
-
-
执行流程:
- do-while循环在执行时,会先执行循环体,执行完毕对条件表达式求值判断
- 如果为false,则语句结束。
- 如果为true,则继续执行循环体
- 以此类推
-
do-while是先执行后判断,while是先判断后执行
-
do-while可以确保循环至少执行一次,其他时候和while没有区别
let i = 5; do{ console.log(i); i++; }while (i < 5)
-
-
for循环
-
语法:
-
for(①初始化表达式; ②条件表达式; ④更新表达式){ ③语句... }
-
-
执行流程:
- 先执行①初始化表达式,初始化一个变量(只会执行一次)
- 执行②条件表达式,判断循环是否执行,如果为false、则循环结束
- ③如果为true、则执行循环体
- 执行更新表达式对变量进行更新
- 重复②
for(let i=0; i<5; i++){ console.log(i); }
-
-
嵌套循环
-
当循环发生嵌套时,外层循环每执行一次,内层循环就执行一圈
for(let i=0; i<5; i++){ for(let j=0; j<5; j++){ document.write('*'); } document.write('<br>'); }
-
8.6、结束和跳过循环
-
break
- break用来结束switch和循环语句
- break默认只会影响到离它最近的循环
-
continue
- continue用来跳过当次循环
9、开启计时器
-
计时器可以用来测试程序的执行时间:
-
console.time() 开始计时器
-
console.timeEnd() 结束计时器
// 开启一个计时器 console.time(); //中间是执行的代码 // 关闭计时器 console.timeEnd();
-
10、对象(Object)
- 对象是一种复合数据类型,
- 在对象中可以存储其他的数据-
- 对象实际上就是数据的容器-
- 对象中所存储的数据称为属性
-
创建对象:
-
使用typeof 检查一个 对象时会返回 'object'
let obj = Object(); let obj = new Object(); let obj = {}; *****
-
-
向对象中添加属性
-
语法:
- 对象.属性名 = 属性值;
- 对象['属性名'] = 属性值;
-
-
读取对象中的属性:
-
语法:
- 对象.属性名 --> 简洁
- 对象['属性名'] --> 灵活
-
-
删除对象中的属性
-
语法:
- delete 对象.属性名
- delete 对象['属性名']
-
-
创建对象时,直接指定对象中的属性
-
语法:
-
{ 属性名:属性值, 属性名:属性值, 属性名:属性值}
-
-
-
注意:
- 当我们访问一个对象中没有的属性时,不会报错,而是返回undefined
- 比较两个对象时,无论是相等还是全等,比较的都是对象的内存地址
- 声明常量是禁止修改变量,不会影响我们修改对象
10.1、方法(method)
-
对象的属性值可以是任意类型,也可以时一个函数
- 如果对象的属性值是一个函数,这个函数我们称为是对象的方法
- 调用函数称为调用对象的方法
-
函数和方法没有本质的区别,只是称呼不同
10.2、in运算符
-
in用来检查对象中是否含有某个属性
-
语法:
- '属性名' in 对象
-
console.log('name' in obj);
10.3、for-in
-
枚举对象中的属性
-
语法:
-
for(let n in obj){ console.log(n,'=',obj[n]); }
-
10.4、可变类型
-
对象是一个可变类型,对象中存储的属性可以被修改
-
如果修改的是对象的属性,那么其它的所有的指向该对象的变量都会受到影响
-
变量和变量之间是相互独立的,修改一个变量不会影响其他变量
-
什么时候是改对象
- 对象.属性 = xxx
- 对象['属性'] = xxx
-
什么时候是改变量
- 对象 = xxx
- 对象 += xxx
- 对象++
11、函数(function)
-
函数也是一个对象
-
函数可以用来存储JS的代码,并且在需要时对其进行调用
-
使用typeof检查一个函数时,会返回'function'
-
创建函数:
-
函数声明:
function 函数名([形参...]){ 语句... } -
函数表达式:
let 函数名 = function([形参...]){ 语句... }; -
立即执行函数(IIFE)
//第一种写法 (function(){ 语句... })(); //第二种写法 (function(){ 语句... }());
-
-
函数调用
-
函数调用就是将函数中储存代码执行
-
语法:
-
函数对象();
function fn() { alert('你好!'); } fn();
-
-
11.1参数
-
形参(形式参数)
- 在定义函数时,可以在函数中指定数量不等的形参,定义形参就相当于在函数中声明了变量,但是没有赋值
-
实参(实际参数)
-
在调用函数时,可以在函数中传递实参,实参将会赋值给对应的形参
-
JS不会检查实参的类型和数量
-
可以传递任意类型的值作为参数
-
可以传递任意数量的值作为参数
- 等于形参数量 --> 一一对应
- 小于形参数量 --> 没有的就是undefined
- 大于形参数量 --> 多了的不用
-
-
-
返回值
-
返回值是函数的执行结果,通过return关键字来设置返回值
-
语法:
- return 值;
-
注意:
- 任何值都可以成为函数的返回值
- return后不跟值或不写return,相当于return undefined;
- return一旦执行,函数直接结束
-
12、作用域(scope)
-
作用域就是变量的作用区域
-
作用域分为两种:
-
全局作用域
-
局部作用域
- 块作用域(代码块中使用let声明的变量)
- 函数作用域
-
12.1、全局作用域(global)
- 全局作用域在页面加载时创建,在页面关闭时销毁
- 所有的直接写在script标签内部的变量(函数)都属于全局作用域
- 全局作用域中的变量是全局变量,函数是全局函数,可以在页面的任意位置被访问。(开发时很少在全局作用域中编写代码!)
- 全局作用域中有一个全局对象(global object)window, window对象代表浏览器的窗口
- 在全局中使用var声明的变量都会作为window对象的属性保存,函数都会作为window对象的方法保存
- 如果声明一个变量不使用var关键字,相当于向window中添加属性
12.2、函数作用域
- 函数作用域在函数调用时创建,调用结束即销毁。
- 函数每次调用都会产生一个新的作用域,作用域与作用域间相互独立。
- 在函数作用域声明的变量是局部变量,声明函数是局部函数,只能在函数内部访问,函数外部无法访问。
- 如果声明变量不使用var或let,则变量会成为全局变量。
- 变量和函数的提升在函数作用域中同样适用,函数作用域就是小全局作用域,全局作用域就是一个小的函数作用域
12.3、变量的提升
- 使用var声明变量,会在所有的代码执行前被声明,但是不会赋值。
- 赋值会在赋值语句执行时才进行。
- 所以我们可以在一个变量声明前,就对其进行访问。
12.4、函数的提升
- 使用function开头的函数,会自所有的代码执行前被创建。
- 所以我们可以在一个函数声明前就对其进行调用。
12.5、作用域链
-
当我们访问一个变量或函数时
- JS会先在当前作用域中寻找,如果有则直接使用,
- 如果没有则去它的上一层作用域中寻找,有则使用
- 没有则继续去上一层寻找,以此类推
- 直到找到全局作用域,依然没有找到,则报错。
-
函数外层作用域在函数定义时就已经确定,和函数的调用位置无关。
13、this
-
this是函数中的隐含参数,由浏览器自动传递-
-
根据函数的调用方式不同,this的值也不同:
- 以函数形式调用,this是window
- 以方法的形式调用,this是调用方法的对象
- 以构造函数的形式调用,this是新建的对象
- 以call和apply的形式调用,this是它们的第一个参数
- 箭头函数中的this由它外层作用域决定
- 事件的回调函数中,this是绑定事件的对象
14、构造函数(constructor)
-
构造函数就是专门用来创建对象的函数
-
构造函数的定义方式和普通函数没有区别,只是构造函数的名字首字母需要大写
-
构造函数和普通函数主要区别在于调用方式:
-
普通函数是直接调用
-
构造函数需要使用new关键字来调用
function Person(name, age) { this.name = name; this.age = age; } let per = new Person('孙悟空', 18);
-
-
构造函数的执行的流程:
- 构造函数执行时,会先创建一个新的对象
- 将this设置为新的对象
- 执行函数中的代码
- 将新建的对象作为返回值返回
-
一个构造函数也称为一个类(class)
- 通过构造函数所创建的对象,我们称其为该类实例
- 通过同一个类创建的对象,称为同类对象
15、运算符:instanceof
-
作用:
- 检查一个对象是否是一个类的实例:
-
语法:
- 对象 instanceof 类
16、原型(prototype)
-
对象的两个层面:
- 狭义:在JS中对象指 Object
- 广义:在JS中所有的东西都是对象
-
原型
-
每一个函数都有一个属性叫做prototype(显示原型),该属性指向的是一个对象,这个对象就是原型对象
-
如果函数作为一个构造函数去调用,那么它所创建的实例中都会有一个隐含的属性(proto,隐式原型)指向函数的显示原型。
- 实例的隐式原型指向类的显示原型。
-
原型对象就相当于一个公共的区域,可以被所有的该类实例所共享,
- 所以我们可以将实例中共有的属性统一存储到原型中
- 这样我们只需创建一个属性(方法),即可使所有实例拥有该属性(方法)
-
-
原型链
-
当访问一个对象的属性时,JS会首先在对象自身中寻找
- 如果找到了,则使用
- 如果没有找到,则去对象的原型(proto)中寻找
- 如果找到了,则使用,没找到继续去原型的原型中寻找,以此类推
- 直到找到Object的原型,它所有原型的原型,它的原型是null
- 如果找到Object的原型,依然没有则返回undefined
-
17、对象的分类:
-
内置对象
- 由ES标准所提供的对象
-
宿主对象
- 由运行环境提供的对象DOM和BOM
-
自定义对象
18、数组(Array)
- 数组也是一个对象
- 数组用来储存一组有序的数据
- 数组中储存的数据成为元素(element)
- 数组中的每一个元素都有一个唯一的索引(index)与其对应
- 索引是一组从0开始的整数
18.1、创建数组
let 数组 = new Array();
let 数组 = [];
- 创建一个指定大小的数组
let 数组 = new Array(长度);
- 创建数组同时,向数组中添加元素
let 数组 = [元素1, 元素2, 元素3, ....元素N]
let 数组 = new Array(元素1, 元素2, 元素3, ....元素N);
-
使用typeof检查一个数组时,会返回'object'
- 可以使用类方法(静态方法)Array.isArray(对象) 检查一个对象是否是数组
18.2、数组的属性和方法:
-
length
- 获取数组的长度,元素的数量
- 实际值是数组的最大索引+1
- Length的值可以修改
-
push()
- 向数组的末尾添加一个或多个元素,并返回新的长度
-
pop()
- 删除并返回数组的最后一个元素
-
unshift()
- 向数组的开头添加一个或多个元素,并返回新的长度
-
shift()
- 删除并返回数组的第一个元素
-
slice()
-
用来截取数组,不会影响到原数组
-
参数:
- 截取的起始索引(包含起始位置)
- 截取的结束索引(不包含结束位置)
- 索引可以是负值,负值是从后往前计算(例如:-1就是倒数第一个)
- 第二个参数可以省略不写,它会直接截取到最后一个元素
- 也可以通过slice(0)来完成数组的浅复制
-
-
splice()
-
用来删除,替换,插入数组中的元素
-
参数:
- 删除的起始索引
- 删除的数量
- 要插入的元素
-
返回值:
- 返回被删除的元素
-
-
forEach()
-
用来遍历数组
-
用法:
-
需要一个回调函数作为参数,回调函数会反复调用多次,每次调用时,将遍历到的信息以参数的形式传递
-
参数:
- element 当前遍历到的元素
- index 当前元素的索引
- array 当前遍历的数组对象
let arr = ['孙悟空', '沙和尚', '猪八戒', '唐僧']; arr.forEach(function(element, index, array){ console.log('element =', element); console.log('index =', index); console.log('array =', array); });
-
-
-
concat()
- 用来连接两个或多个数组
-
join()
-
用来将数组中的所有的元素连接为一个字符串
-
参数:
- 需要一个连接符作为参数,默认为","
-
-
indexOf()
-
查询元素在数组的位置
-
参数:
- 要查询的元素
- 查询的起始位置
-
返回值
-
返回元素第一次的数组中出现的索引
如果没找到元素,则返回-1
arr = ['孙悟空', '猪八戒', '孙悟空', '沙和尚']; result = arr.indexOf('孙悟空'); //返回值0 result = arr.indexOf('唐僧'); //返回值-1 console.log(result); -
-
-
lastIndexOf()
-
查询元素在数组中的位置
-
参数
- 要查询的元素
- 查询的起始位置
-
返回值
返回元素最后一次出现的索引,找不到返回-1
-
-
join()
-
用来将数组中的所有元素链接为一个字符串
-
参数
- 指定一个链接符(字符串)作为参数,默认使用逗号。
arr = ['a', 'b', 'c', 'd']; result = arr.join('+'); //a+b+c+d result = arr.join(''); //abcd console.log(result);
-
-
reverse()
- 用来对数组进行反转,这是一个破坏性的方法,会改变原数组
arr = ['a', 'b', 'c', 'd']; arr.reverse(); console.log(result); //['d', 'c', 'b', 'a'] -
sort()
-
用来对数组进行排序的,也会影响到原数组
-
使用sort()对元素进行排序的时候,比较的是元素的字符编码而不是数字的大小
-
可以在sort()通过回调函数来指定排序规则
-
升序排列(从小到大)
function(a, b){ return a - b; } -
降序排列(从大到小)
function(a, b){ return b - a; }arr = [3,2,1,4,5,8,9,7,6,10]; arr.sort(); console.log(arr); // [1, 10, 2, 3, 4, 5, 6, 7, 8, 9]arr = [3,2,1,4,5,8,9,7,6,10]; arr.sort(function(a, b){ return a - b; }); console.log(arr); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-
-
19、函数(function)
19.1、arguments
- arguments是一个类数组对象(伪数组)
- 类数组对象和数组的操作方法基本一致,只是不能调用数组的方法
function fn() {
console.log(arguments);
}
fn();
- 在函数执行时,所有的实参都会储存在arguments对象中,通过arguments,即使不定义形参也可以使用实参。
// 创建一个函数,可以求任意个数字的和
function sum() {
// 创建一个变量,存储结果
let result = 0;
// 遍历arguments
for(let i=0; i<arguments.length; i++){
result += arguments[i];
}
return result;}
console.log(sum(123, 456)); //结果579
19.2、函数call和apply
-
根据函数的调用方式不同,this的值也不同:
- 以函数形式调用,this时window
- 以方法的形式调用,this时调用方法的对象
- 以构造函数调用。this是新建的对象
- 通过call和apply调用函数时,第一个参数是谁this就是谁
function fn(a, b) {
console.log(a, b);
}
let obj = {fn};
// obj.fn.call(window, 123, 456);
obj.fn.apply(window, [123, 456]);
-
函数对象的方法:
-
call
- 当我们调用call()方法时,实际上和直接调用函数的效果类似
- call可以用来指定函数的this,它的第一个参数传谁函数的this就是谁
- call()传递实参时,直接从第二个参数开始一一传递
-
apply()
- apply()的作用和call()一样
- apply需要将实参保存到一个类数组对象中同一传递
-
20、递归
-
递归就是函数自己调用自己
-
递归的核心就是对问题的分解,将一个大问题拆分一个一个的小问题
-
递归两个要点:
- 基线条件,递归停止条件
- 递归条件,如果分解问题
function jieCheng2(num) { // 1.基线条件,递归停止的条件 if(num === 1){ return 1; } // 2.递归条件,如何对问题进行拆分 // 6! = 5! * 6 // num! = (num - 1) * num return num * jieCheng2(num-1); } console.log(jieCheng2(10));
21、剩余参数
-
类似于...args被称为剩余参数,剩余参数会获取到所有没有形参对应的形参
-
...args表示剩余参数,它和arguments的却别
- 剩余参数就是一个数组,可以调用数组的方法
- args只会保存没有形参对应的参数
- 剩余参数必须是参数列表中的最后一个
let arr = [1, 2, 3, 4, 5, 6]; function fn(a, b, ...args) { console.log(args); }
22、bind
-
bind()也是一个函数的方法,调用方法时bind会返回一个新的函数对象,这个新的函数对象的功能和原来的功能是一样的,不同点在于新的函数的this是设置死的。
function fn2() { console.log(this); } let fn3 = fn2.bind(obj);
23、Math
- Math是一个用来进行数学运算的工具类
- 工具类不需要创建对象,可以直接使用
- 在Math中包含了一组数学运算相关的常量和方法
- 例子:
-
名称 说明 Math.PI 获取圆周率 Math.abs() 获取一个数的绝对值 Math.ceil() 对一个数向上取整 Math.floor() 对一个数向下取整 Math.round() 对一个数进行四舍五入取整 Math.random() 生成一个0-1之间的随机数 Math.pow(x, y) 求x的y次幂 Math.sqrt() 求一个数的平方根(开方) Math.max() 取多个值中的较大值 Math.min() 取多个值中的较小值
24、Date
-
Date是表示日期的对象,在JS中所有的时间相关的都使用Date来表示
-
创建对象:
-
创建一个表示当前日期的对象
let d = new Date(); -
创建一个指定日期的对象
let d = new Date('月/日/四位年 时:分:秒');
-
-
对象的方法:
-
方法 说明 返回值 d.getDay() 获取当前是周几 返回值 0-6 d.getDate() 获取当前是几日 d.getMonth() 获取当前是几月 返回值 0-11 d.getFullYear() 获取当前的年份 d.getTime() 获取当前日期的时间戳 Date.now() 获取当前的时间戳(代码执行这一刻)
25、包装类
- JS中提供了三个包装类String、Number、Boolean
- 包装类可以用来创建基本数据类型的对象,但是我们自己千万不要用
- 当我们调用字符串、数值、布尔值的方法或属性时,
- 浏览器会调用包装类临时将其转换为对象,然后调用对象的属性或方法
26、String
-
字符串的方法
-
字符串本质上就是一个字符数组
-
'abc' ---> ['a', 'b, 'c']
-
字符串的方法:
字符串的方法 说明 str.length 获取字符串的长度 str.charAt() 根据索引获取指定位置的字符 str.charCodeAt() 根据索引获取指定位置的字符编码 String.fromCharCode() 根据字符编码返回字符 str.indexOf() 查询子串在字符串中第一次出现的位置 str.lastIndexOf() 查询子串在字符串中最后一次出现的位置 str.slice() 截取字符串 str.trim() 去除前后空格 str.trimEnd() 去除结束的空格 str.trimStart() 去除开始的空格 str.startsWith() 检查字符串是否以指定内容开头 str.endsWith() 检查字符串是否以指定内容结尾 str.toUpperCase() 将字符串转换为大写 str.toLowerCase() 将字符串转换为小写 str.split() 将一个字符串拆分为一个数组
27、解构赋值
-
将数组中的元素赋值给变量
let [变量1, 变量2, ...变量n] = 数组;
let arr = [1, 2, 3, 4, 5, 6, 7]; let [a, b, ...c] = arr;数组中两个值调换位置
[arr[1], arr[2]] = [arr[2], arr[1]]; -
将数组中的元素拆分作为参数传递
fn( ...数组 );
function sum(a, b, c) { return a + b + c; } let arr2 = [567, 765, 345]; console.log(sum(...arr2)); -
对对象的解构
let {变量1,变量2} = 对象;
let obj = {name:'孙悟空', age:18, gender:'男'}; let {name,age,gender} = obj; console.log(name, age, gender);
28、箭头函数
--语法:
-
( [ 参数列表 ] ) => 返回值
-
参数 => 返回值
-
( [ 参数列表 ] ) => {
语句....
}
let sum = (a, b) => a+b;
console.log(sum(123, 456));
//原来的
let arr3 = [3,4,1,2,5];
arr3.sort(function (a, b){
return a - b;})
console.log(arr3);
//转换为箭头函数
let arr2 = [3,4,1,2,5];
arr2.sort((a,b)=>a-b);
console.log(arr2);
29、闭包
闭包就是能访问到外部函数中变量的内部函数
--用途:
闭包主要用来藏起来一些见不得人的东西
--闭包构成要件:
- 闭包必须有函数的嵌套
- 内部函数必须要访问外部函数变量
- 必须将内部函数作为返回值返回
--闭包的声明周期
- 当外部函数调用时,闭包便产生了
- 外部函数每调用一次就会产生一个闭包
- 当内部函数被垃圾回收时,闭包销毁
function outer(){
// 定义一个变量,记录函数执行的次数
let times = 0;
// 创建一个函数,函数每次调用时,都会打印函数被调用的次数
function inner() {
times++;
alert(times);
}
// 将inner设置为函数的返回值
return inner;
}
let fn = outer();
30、DOM(Document Obiect Model)
--文档对象类型
- Document(文档) -文档指整个网页
- Obiect(对象) -DOM将网页中的所有的东西都转换为了对象
- Model(模型) -模型用来体现节点之间的关系
--节点(Node)
-
网页中所有的部分都可以称其为一个节点,虽然都是节点,但是有着不同的类型
- 文档节点 document -表示整个网页
- 元素节点 element -各种标签都属于元素节点
- 属性节点 text -标签中的属性称为属性节点
- 文本节点 attribute -标签中的文字
31、文档加载
- 网页的加载是按照自上向下的顺序一行一行加载的,
- 如果将script标签写在前边,这样js执行时页面还没加载完毕,将会出现获取不到DOM对象的情况。
- 解决方式:
-
//1、将script标签放在body的最 <body> <script></script> </body> -
//2、可以将代码写在window.onload的回调函数 window.onload = function(){ ... }; -
//3、引入外部js文件时,在script标签上添加defer属性 <script defer src='....'></script>
32、DOM查询
- 查询指在网页中获取指定的节点
32.1、通过document对象查询
| document对象查询 | 说明 |
|---|---|
| document.getElementById() | 根据id获取一个元素节点对象 |
| document.getElementsByClassName() | 根据class属性值获取一组元素节点对象 |
| document.getElementsByTagName() | 根据标签名获取一组元素节点对象 |
| document.getElementsByTagName('*') | 获取页面中的所有元素 |
| document.getElementsByName() | 根据元素的name属性获取一组元素节点对象(主要用于表单项) |
| document.querySelector() | 根据选择器字符串获取符合条件的第一个元素 |
| document.querySelectorAll() | 根据选择器字符串获取符合条件的所有元素 |
| document.documentElement | 获取页面的根元素 (html) |
| document.body | 获取页面的body元素 |
| getBoundingClientRect() | 获取一个元素相对于视口的位置最干净的获取方式(含有left和top) |
32.2、通过element进行查询
| element进行查询 | 说明 |
|---|---|
| element.getElementsByTagName() | 根据标签名获取元素中的指定的后代元素 |
| element.childNodes | 获取当前元素的所有子节点 |
| element.children | 获取当前元素的所有子元素 |
| element.firstChild | 获取第一个子节点 |
| element.firstElementChild | 获取第一个子元素 |
| element.lastChild | 获取最后一个子节点 |
| element.lastElementChild | 获取最后一个子元素 |
| element.parentNode | 获取当前元素的父元素 |
| element.previousSibling | 获取前一个兄弟节点 |
| element.previousElementSibling | 获取前一个兄弟元素 |
| element.nextSibling | 获取后一个兄弟节点 |
| element.nextElementSibling | 获取后一个兄弟元素 |
32.3、元素的中的属性
-
如何读取元素的属性:
-
元素.属性名
-
例子:
- ele.name
- ele.value
- ele.id
- ele.className
-
-
如何设置:
-
元素.属性名 = 属性值
-
例子:
- ele.name = xx
- ele.value = xxx
- ele.id = xxx
- ele.className = xxx
-
32.4、其他属性:
- innerHTML 内部的HTML代码,带标签
- innerText 内部的文本内容,不带标签
32.5、 读取一个标签内部的文本:
-
<span>哈哈</span> //span.innerHTML //span.innerText //span.firstChild.nodeValue //span.textContent
33、DOM增加、删除、修改
33.1、创建元素
-
DOM创建 说明 document.createElement(标签名) 创建一个新的元素 document.createTextNode(文本内容) 创建一个新的文本节点
33.2、插入元素
-
DOM插入 说明 父节点.appendChild(子节点) 向父节点中插入一个子节点 父节点.replaceChild(新节点, 旧节点) 使用新节点替换旧节点 父节点.insertBefore(新节点, 旧节点) 将新节点插入到旧节点的前边 元素.insertAdjacentElement('位置', 元素); 向元素的指定位置插入子元素 元素.insertAdjacentHTML('位置', 'HTML代码'); 向元素的指定位置插入HTML代码 元素.insertAdjacentText('位置', '文本内容'); 向元素的指定位置插入文本内容 -
位置参数 (位置需要传递一个字符串作为参数:) 说明 beforebegin 开始标签前,成为当前元素的前一个兄弟元素 afterbegin 开始标签后,成为当前元素的第一个子元素 beforeend 结束标签前,成为当前元素的最后一个子元素 afterend 结束标签后,成为当前元素的后一个兄弟元素
33.3、复制节点
-
DOM复制节点 说明 节点.cloneNode() 对节点进行浅复制(只复制节点本身) 节点.cloneNode(true) 对节点进行深复制(复制节点本身及所有的后代节点)
33.4、删除元素
-
DOM删除 说明 子节点.parentNode.removeChild(子节点) 通过父元素删除子节点 子节点.remove() 通过函数删除子节点
34、通过JS去操作CSS:
34.1、操作内联样式
-
属性:style
-
读取样式:
- 元素.style.样式名
-
设置样式:
- 元素.style.样式名 = 样式值
-
注意:
-
通过style属性所读取和设置的样式都是内联样式
-
所以通过它所设置的样式通常会立即生效
-
如果样式名不符合标识符的规范,需要对样式名就行修改:
- 去掉-,-后的字母大写
- background-color ==> backgroundColor
- border-left-width ==> borderLeftWidth
-
34.2、获取当前的生效的样式
-
getComputedStyle()
-
参数:
- 要获取样式的元素
- 要获取的伪类(没有可以不写)
-
返回值:
- 一个对象,对象中包含了当前元素所有生效的样式
-
34.3、其他的样式相关的属性:
-
其他的样式 说明 clientWidth clientHeight 获取的是内容区和内边距的总大小 offsetWidth offsetHeight 获取的是内容区、内边距和边框的总大小 offsetParent 获取当前元素的定位父元素 离当前元素最近的开启了定位的祖先元素 如果所有的祖先都没有开启定位 则返回body offsetLeft offsetTop 当前元素距离其定位父元素的距离 scrollHeight scrollWidth 获取元素滚动区域的大小 -
注意:
- 以上属性都是只读属性,无法修改
- 以上属性所获取的值都是不带单位的值,可以直接参与运算
-
| scrollTop | scrollLeft | 获取(设置)垂直和水平滚动条滚动的距离 | | ----------------------- | ----------------------------------------- | | 判断垂直滚动条滚动到底 | scrollHeight - scrollTop === clientHeight | | 判断水平滚动条滚动到底 | scrollWidht - scrollLeft === clientWidth |
35、事件(Event)
- 事件就是用户和浏览器的交换瞬间,像鼠标点击,鼠标移动,按下按键...
- 我们可以通过为事件设置响应函数来完成和用户的交互
- 响应的方式:
-
//方式一: <button onclick="alert('....');">点我一下</button> -
//方式二: <script> let btn = document.getElementById('btn'); btn.onclick = function(){ ... ... }; </script> -
鼠标事件 说明 click 鼠标单击 dblclick 鼠标双击 mousedown 鼠标按下 mouseup 鼠标弹起 mouseout 鼠标移出 mouseover 鼠标移入(元素的子元素移入也会触发事件 (子元素会触发第二次)) mousemove 鼠标移动 mouseenter 鼠标悬停(元素的子元素移入不会触发事件 (子元素不会触发)) mouseleave 鼠标取消悬停 contextmenu 表示鼠标右键菜单的事件 点击右键弹出菜单,是contextmenu事件的默认行为 可以通过取消它的默认行为,来禁用菜单 wheel 表示鼠标滚轮的事件 event.deltaX 滚轮的水平滚动方向 event.deltaY 滚轮的垂直滚动方向 -
键盘事件 说明 keydown 某个键盘按键被按下 keyu 某个键盘按键被松开 keypress 某个键盘按键被按下并松开 -
其他事件 说明 window.onload 页面加载事件 默认触发的 执行一次 submit 提交按钮触发的事件,一般用于表单form blur 失去焦点(常用于input) focus 获取焦点(常用于input) change 修改事件 scroll 滚动栏滚动 select 选择事件 reset 重置事件 -
键盘事件2 用来获取当前是哪个按键被按下 event.ctrlKey 用来检查ctrl是否按下 如果按下了 返回true,否则返回false event.shiftKey 用来检查shift是否按下 event.altKey 用来检查alt是否按下
35.1、事件对象:
-
当事件的回调函数被调用时,浏览器每次都会传递一个对象作为参数,这个对象就是事件对象。
-
事件对象中存储了事件相关的一切信息:事件触发时,
- 哪个鼠标按键被按下、
- 哪个键盘上的按键被按下、
- 鼠标滚轮滚动的方向..
-
要获取事件对象,只需在事件的回调函数中定义一个形参即可
-
areaDiv.onmousemove = function (event) { // 获取鼠标的x轴和y轴 let x = event.clientX; let y = event.clientY; // 在showMsg显示坐标 showMsg.innerHTML = 'x = '+x+' , y = '+y; };
35.2、事件的冒泡(bubble)
-
冒泡指事件的向上传导,子元素上事件触发时,会同时导致其祖先元素上的同类事件也被触发
-
冒泡的存在简化了代码的编写
-
但是有时我们不希望冒泡的存在,可以通过事件对象来取消冒泡:
-
通过cancelBubble属性来取消冒泡
event.cancelBubble = true; -
通过stopPropagation()方法来取消冒泡
event.stopPropagation();
-
35.3、事件的传播
-
事件的传播分成了三个阶段:
-
事件的捕获
- 指事件从最外层元素开始向内部元素进行事件的捕获
- 默认情况下,捕获阶段不会触发事件
- 如果希望在捕获时触发事件,可以将addEventListener()的第三个参数设置为true
-
目标元素(触发事件的元素)
- 捕获到达目标元素停止
-
事件的冒泡
- 从目标元素开始向外层元素进行事件的冒泡
- 默认情况下,冒泡时事件会被触发
-
35.4、 addEventListener()
-
为元素添加事件的响应函数
-
参数:
- 要绑定的事件的字符串(不要on)
- 事件的回调函数
- 是否在捕获阶段触发事件,默认为false
35.5、removeEventListener()
- 可以用来移除一个事件的响应函数
- 移除时的参数必须和设置时的一摸一样
-
function clickHandler(event) { alert(1); } btn01.addEventListener('click',clickHandler); btn01.removeEventListener('click',clickHandler);
36、修改元素的class
- 当修改的样式过多时,可以通过修改元素的class属性来影响元素的样式
36.1、classLIst
- classList可以用来返回当前元素的所有的类,其中有很多方法方便我们操作元素。
- 它的类型是DOMTokenList,提供了一下方法:
-
方法 功能 add() 用来向元素添加一个或多个类 remove() 用来移除元素中的一个类 replace() 用来使用一个新的class替换原来的class toggle() 切换一个元素的class(类似开关) 如果元素拥有该类,则删除 如果元素没有该类,则添加 contains() 检查一个元素是否含有某个class 如果包含,返回true 如果不包含,返回false -
let btn01 = document.getElementById('btn01'); let box1 = document.getElementById('box1'); btn01.addEventListener('click', function () { box1.classList.add('b2'); }
37、定时器
- 定时器分为两种,延时调用和定时调用
37.1、创建一个定时器
-
setTimeout()(延时调用)
-
它可以用来在指定时间后调用函数
-
参数
-
回调函数,要调用的函数
-
时间(毫秒)
setTimeout(function () { alert('hello'); }, 3000);
-
-
小技巧:setTimeout可以通过嵌套实现循环延时调用
setTimeout(function set() { alert('hello'); setTimeout(set, 3000); }, 3000);
-
-
setInterval()(定时调用)
-
参数:
- 回调函数,要调用的函数
- 时间(毫秒)
-
返回值
- 返回一个定时器id
let timer = setInterval(function () { num++; h1.innerHTML = num; clearInterval(timer); }, 1000)
-
37.2.关闭定时器
-
clearTimeout()
- 关闭定时器(延时调用)
-
setInterval()(定时调用)
-
参数:
- 回调函数,要调用的函数
- 时间(毫秒)
-
返回值
- 返回一个定时器id
let timer = setInterval(function () { num++; h1.innerHTML = num; clearInterval(timer); }, 1000)
-
-
clearInterval()
-
用来关闭定时器
-
参数
- 定时器的id
-
clearInterval(timer);
-
38、BOM(浏览器对象模型)
-
BOM中为我们提供了一组对象,用来完成对浏览器的各种操作
-
BOM对象:
-
Window
- 代表的是浏览器窗口
-
History
- 代表的是浏览器的历史记录
-
Location
- 代表的是浏览器的地址栏
-
Navigator
- 代表浏览器的信息
-
Screen
- 代表的是设备的屏幕信息
-
-
BOM对象都是window对象的属性,所以可以直接访问
38.1、History
| history.length | 当前访问的页面数量 |
|---|---|
| history.forward() | 切换到前边访问的网址 |
| history.back() | 相当于浏览器的回退按钮 |
| history.go() | 跳转到指定的历史记录 |
alert(history.length);
history.forward();
history.back();
history.go(2);
history.go(-1);
38.2、Location
-
Location表示浏览器地址栏信息
-
如果直接读取Location,则可以获取到地址栏的信息
-
如果修改Location的值,则浏览器会自动跳转到新的地址
- 通过这种方式跳转页面,会留下历史记录,可以通过回退按钮回退
| assign() | 用来跳转地址,和直接修改Location是一样的 |
|---|---|
| replace() | 用来跳转地址,它不会产生历史记录,无法通过回退按钮回退 |
| reload() | 用来重新加载网页,相当于网页的刷新按钮 |
location.assign('https://www.jd.com'); location.replace('https://www.jd.com');
location.reload(true);
38.3、Navigator
- 代表浏览器信息,通过Navigator来识别出不同的浏览器
- userAgent 返回的是一个字符串,用来表示浏览器信息
IE修改了关键字,所以Navigator识别不出来,需要通过ActiveXobject
if('ActiveXObject' in window){
alert('你是IE!');
}else if(ua.indexOf('Firefox') !== -1){
alert('你是火狐~~');
}else if(ua.indexOf('Chrome') !== -1){
alert('你是Chrome');
}
39、正则表达式(对象)
- 正则表达式就是用来定义一个字符串的规则,计算机可以根据正则表达式来判断一个字符产是否符合规则,也可以用来将符合内容的字符串从一段内容中提取出来。
39.1、创建正则表达式
-
第一种方式:new RegExp('正则表达式','匹配模式');
-
let re = new RegExp('a');
-
-
第二种方式:/ 正则表达式 / 匹配模式
-
let re2 = /a/;
-
39.2检查正则表达式
- test() 用来检查一个字符串是否符合正则表达式
-
let re = /a/; alert(re.test('abc')); -
正则表达式 描述 表示或 [ ] 里面的内容都表示或 [a-z] 表示任意的小写字母 [A-Z] 表示任意大写字母 [a-zA-Z] 任意字母 [0-9] 任意数字 [^n ] 表示除了n ^n 字符串以n开头 n$ 字符串以n结尾 . 表示任意字符 \ . 表示. \w 任意单词字符,相当于[a-zA-Z0-9_] \W 除了单词字符,相当于[(^)a-zA-Z0-9_] (不带括号) \d 任意数字,相当于[0-9] \D 除了数字,相当于[(^)0-9] (不带括号) \s 空格 \S 除了空格 -
let phoneReg = /1[3-9][0-9]{9}/; str = 'hello13716578901dasdasdasd'; alert(phoneReg.test(str)); -
量词 描述 {n} 正好出现n次 {n,m} 出现n到m次 {n,} 至少出现n次 + 至少一次,等价于{1,} ? 0-1次,等价于{0,1} * 0-多次,等价于{0,} (有没有都行) -
re = /ab{1,3}c/; re = /ab{1,}c/; re = /(ab){2}c/; -
匹配模式 描述 i 忽略大小写 g 全局匹配
39.3、字符串中和正则相关的方法
-
search()
-
用来根据正则表达式搜索字符串是否含有指定的内容
-
返回值:
- 返回字符串第一次出现位置的索引,如果没有找到则返回-1
-
let result = str.search('13716890876'); result = str.search(/1[3-9][0-9]{9}/);
-
-
split()
- 根据正则表达式来将一个字符串拆分成一个数组
-
str = '孙悟空abc猪八戒Adc沙和尚'; result = str.split(/a[bd]c/i);
-
replace()
-
使用一个新的内容,替换字符串中指定内容
-
参数:
- 正则表达式
- 新的内容
-
使用relpace()进行替换时,默认只会替换第一个符合条件的内容,如果希望替换所有符合条件的内容,可以使用匹配模式 g
-
str = '你真是太他妈的好看了'; result = str.replace(/他妈的/g,'***'); console.log(result); //你真是太***好看了
-
-
match()
-
将字符串中符合正则表达式的内容提取出来
-
参数:
- 正则表达式
-
返回值:
- 将符合条件的内容保存到一个数组中返回
-
str = 'asdasfl13716890876jsasdfa13654678901skdfjalsdfasdf'; result = str.match(/1[3-9][0-9]{9}/g); alert(result); //13716890876,1365467891
-
1、javascript高级
1.1、面向对象
面向对象语言的三大特征
- 封装
- 继承
- 多态
在js(es5)当中,我们可以将构造函数理解为(类)
在es6当中出现一个新的关键字 class类
不过它不是真正的类,底层依旧是通过封装构造函数来实现,其本质就是一个语法糖。
1.2、继承
研究继承核心的问题就是让子类能够继承父类已有属性和方法
-
借助构造函数继承
- 在子类当中去调用父类的构造函数,当普通函数调用
- 需求:让子类继承属性
-
function Dog(name,color,age) { this.name = name; this.color = color; this.age = age; } function Corky(name,color,age) { //当前作用域内 this是 Corky的实例化对象 Dog.call(this,name,color,age); } var d1 = new Dog('wangcai','black',3); var c1 = new Corky('dapi','black',3);
-
原型链继承
- 让父类的实例作为子类的原型,将子类的原型构造器补充完整
- 需求:让子类继承方法)
-
Dog.prototype.run = function () { console.log('我可以跑') } Corky.prototype = new Dog(); //我们需要手动的将Corky原型上的构造器属性 重新指向Corky Corky.prototype.constructor = Corky;
-
混合继承
- 原型继承方法,借用构造函数继承属性一起使用
1.3、事件循环机制
1.2.1、多进程和多线程
-
进程:程序的一次执行, 它占有一片独有的内存空间
-
线程: CPU的基本调度单位, 是程序执行的一个完整流程
-
进程与线程
- 一个进程中一般至少有一个运行的线程: 主线程
- 一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
- 一个进程内的数据可以供其中的多个线程直接共享
- 多个进程之间的数据是不能直接共享的
-
浏览器运行是单进程还是多进程?
-
有的是单进程
- firefox(火狐)
- 老版IE
-
有的是多进程
- chrome
- 新版IE
-
-
如何查看浏览器是否是多进程运行的呢?
- 任务管理器==>进程
-
浏览器运行是单线程还是多线程?
- 都是多线程运行的
1.2.2、js是单线程的
-
如何证明js执行是单线程的?
- setTimeout()的回调函数是在主线程执行的
- 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行
-
setTimeout(function () { console.log('今天中午吃点啥') },3000); var a = 0; for (var i = 0; i < 50000; i++) { for (var j = 0; j < 50000; j++) { a++; } } console.log(a);
-
为什么js要用单线程模式, 而不用多线程模式?
- JavaScript的单线程,与它的用途有关。
- 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
- 这决定了它只能是单线程,否则会带来很复杂的同步问题
-
代码的分类:
- 初始化代码(同步代码)
- 回调代码(异步代码)
-
js引擎执行代码的基本流程
-
先执行初始化代码: 包含一些特别的代码
- 设置定时器
- 绑定监听
- 发送ajax请求
-
后面在某个时刻才会执行回调代码
-
-
同步 同步执行完成才会去执行异步
-
异步 只要是异步的任务都会有自己的管理模块进行托管
-
回调
- 事件
- 定时器
- ajax
- 生命周期回调函数
1.4、事件循环模型
1.4.1、所有代码分类
-
初始化执行代码(同步代码)
- 包含绑定dom事件监听
- 设置定时器
- 发送ajax请求的代码
-
回调执行代码(异步代码):
- 处理回调逻辑
1.4.2、js引擎执行代码的基本流程
初始化代码===>回调代码
1.4.3、模型的2个重要组成部分:
- 事件管理模块
- 回调队列
1.4.4、模型的运转流程
- 执行初始化代码, 将事件回调函数交给对应模块管理
- 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中
- 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行
1.5、Web Workers模拟多线程
1.4.1 什么是Web Worker
- H5规范提供了js分线程的实现, 取名为: Web Worker
1.4.2、相关API
- Worker: 构造函数, 加载分线程执行的js文件
- Worker.prototype.onmessage: 用于接收另一个线程的回调函数
- Worker.prototype.postMessage: 向另一个线程发送消息
每个线程可以向不同线程发送消息 也可以接收不同线程传来的消息
主线程操作
-
发送消息:
- worker.postMessage(消息可以是任何数据)
-
接受消息:
- worker.onmessage = function(e){ console.log(e.data) }
- // 接收到的消息或者数据在事件对象的data属性当中
子线程操作
-
发送消息:
- this.postMessage(消息可以是任何数据)
-
接受消息:
- this.onmessage = function(e){ console.log(e.data) }
- // 接收到的消息或者数据在事件对象的data属性当中
1.4.3、不足
- worker内代码不能操作DOM(更新UI)
- 不能跨域加载JS
- 不是每个浏览器都支持这个新特性
1.6、闭包
1.6.1、如何产生闭包(条件)?
- 函数嵌套
- 内部函数引用外部函数的局部变量
- 使用(调用)外部函数
注意:并且内部函数也要调用或者引用(针对谷歌)(因为部分浏览器会对内部函数做优化,内部函数不使用或者不引用,相当于没有)
1.6.2、闭包到底是什么?
- 理解一: 闭包是嵌套的内部函数(绝大部分人)
- 理解二: 包含被引用变量(外部函数)的对象(极少数人)
- 理解三: 所谓的闭包是一个引用关系,该引用关系存在于内部函数中,引用的是外部函数的变量的对象(深入理解)
1.6.3、常见的闭包
- 将函数作为另一个函数的返回值
- 将函数作为实参传递给另一个函数调用
- 使用闭包实现私有方法操作独立的私有属性
1.6.4、闭包的作用
- 延长外部函数变量对象的生命周期
- 让函数外部可以操作(读写)到函数内部的数据(变量/函数),通过闭包间接的操作
- 注意: 浏览器为了性能后期将外部函数中不被内部函数使用的变量清除了
1.6.5、闭包的生命周期
- 产生: 在嵌套内部函数定义完时就产生了(不是在调用),外部函数调用的时候
- 死亡: 在嵌套的内部函数成为垃圾对象时
1.6.6、自定义模块(模块化)
- 具有特定功能的js文件
- 将所有的数据和功能都封装在一个函数内部(私有的)
- 只向外暴露一个包含n个方法的对象或函数
- 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能使用自调用和不使用自调用函数区别
1.6.7、闭包的缺点和解决(内存泄漏和内存溢出)
- 内存泄漏 : 内存无法释放;
- 内存溢出 : 内存被撑爆;
- f = null; 解决方式;让闭包机制清除,必须删除外部函数调用的时候生成的(定义的那个对应内部函数);
2、jQuery
2.1、引入jQuery
<script src="js/jquery-1.10.1.js"></script>
<!--我们的代码要在jquery的代码之后引入-->
2.2、jQuery核心函数
-
简称: jQuery函数($/jQuery)
-
jQuery库向外直接暴露的就是$/jQuery
-
引入jQuery库后, 直接使用$即可
- 当函数用: $(xxx)
- 当对象用: $.xxx()
2.1.1、作为一般函数调用:$(param)
-
参数为函数 : 当DOM加载完成后,执行此回调函数
-
$(function () {}) 等同于 window.onload
-
-
参数为选择器字符串: 查找所有匹配的标签, 并将它们封装成jQuery对象
-
$('#btn') === document.getElementById
-
-
参数为DOM对象: 将dom对象封装成jQuery对象
-
$(this)
-
-
参数为html标签字符串 (用得少): 创建标签对象并封装成jQuery对象
-
$('<input type="text" name="msg3"/><br/>').appendTo('div')
-
2.1.2、作为对象使用: $.xxx()
- $.each() 隐式遍历数组
- $.trim() 去除两端的空格
2.1.3、jQuery对象
jQuery对象是一个包含所有匹配的任意多个dom元素的伪数组对象
执行$()返回的就是jQuery对象
- size()/length 包含的DOM元素个数
- [index]/get(index) 得到对应位置的DOM元素
- each() 遍历包含的所有DOM元素
- index() 得到在所在兄弟元素中的下标
2.3、jQuery选择器
| 基本选择器 | 作用 |
|---|---|
| $('xx') | 元素选择器 |
| $('.xx') | 属性选择器 |
| $('#xx') | id选择器 |
| $('xx,yy,zz') | 取多个选择器的并集(组合选择器) |
| $('xxyyzz') | 取多个选择器的交集(相交选择器) |
| 层次选择器 | 作用 |
|---|---|
| $('xx yy') | 后代选择器 |
| $('xx > yy') | 子级选择器 |
| $('.xx + yy') | class为xx的下一个yy |
| $('.xx ~ yy') | class为xx的后面的所有yy元素 |
| 过滤选择器 | 作用(在原有匹配元素中筛选出其中一些) |
|---|---|
| :first | 第一个元素 |
| :last | 最后一个元素 |
| :eq(index) | 是按照索引来查找元素的 从零开始 |
| :lt | 小于给定索引值(不包含边界) |
| :gt | 大于给定索引值 (不包含边界) |
| :contains(B) | 选择内容为B的元素 |
| :hidden | 选择隐藏的元素 |
| xx[yy] | 选择有yy属性的xx元素 |
| xx[yy = zz] | 选择yy属性等于zz的xx元素 |
| xx[yy]+[yy!=zz] | 选择所有xx元素中有yy属性的,且yy属性不等于zz(省略+号) |
| 表单选择器 | 作用 |
|---|---|
| :input | 表单元素选择器,包含所有input 下拉框 按钮 文本域 |
| :text | 表单文本框选择器 |
| :checkbox | 复选框选择器 |
| :radio | 单选框选择器 |
| :checked | 选中结果选择器 |
2.4、jQuery工具方法
-
$.each() 遍历数组或对象中的数据
-
$.each(person,function (k,v) { console.log(k,v)}); -
$.each(arr,function (index,item) { console.log(index,item) })
-
-
$.trim() 去除字符串两边的空格
-
$.type(obj) 得到数据的类型
-
$.isArray(obj) 判断是否是数组
-
$.isFunction(obj) 判断是否是函数
-
$.parseJSON(json) 解析json字符串转换为js对象/数组
2.5、jQuery属性
- 单参数的方法 传值为写 不传值为读
- 双参数的方法 传两个参数为写 传一个参数为读
- 读写合一 读只读第一个 写是写所有
-
attr(name) / attr(name, value) 读写非布尔值的标签属性
-
//给所有的div设置name属性(value为atguigu) $('div').attr('name','atguigu');
-
-
prop(name) / prop(name, value) 读写布尔值的标签属性
-
//点击'全选'按钮实现全选 $('button').click(function () { $checkNodes.prop('checked',true) })
-
-
removeAttr(name) / removeProp(name) 删除属性
-
//移除所有div的title属性 $('div').removeAttr('title');
-
-
addClass(classValue) 添加class
-
//给所有的div添加abc类名 $('div').addClass('abc');
-
-
removeClass(classValue) 移除指定class
-
//移除所有div的guiguClass的class $('div').removeClass('guiguClass');
-
-
val() / val(value) 读写标签的value
-
//将输入框的值设置为atguigu $(':text').val('atguigu')
-
-
html() / html(htmlString) 读写标签体文本
-
//设置第一个li的标签体为"<h1>mm</h1>" $('li:first').html('<h1>mm</h1>');
-
2.6、css模块
2.6.1、style样式
-
css(styleName) 根据样式名得到对应的值
-
css(styleName, value) 设置一个样式
-
$('p:last').css('background','blue');
-
-
css({多个样式对}) 设置多个样式
-
$('p:last').css({ 'color':'#ff0011', 'background':'blue', 'width':'300px', 'height':'30px' })
-
2.6.2、位置坐标
- offset() 读/写当前元素坐标(原点是页面左上角)
- position() 读当前元素坐标(原点是父元素左上角)
- scrollTop() / scrollLeft() 读/写元素/页面的滚动条坐标
2.6.3、尺寸
- width() / height() width/height
- innerWidth() / innerHeight() width + padding
- outerWidth() / outerHeight() width + padding + border
2.7、筛选模块
2.7.1、过滤
在jQuery对象内部的元素中找出部分匹配的元素, 并封装成新的jQuery对象返回
| 方法 | 描述 |
|---|---|
| first() | 第一个元素 |
| last() | 最后一个元素 |
| eq(index) | 根据索引值查找 |
| filter(selector) | 对当前元素提要求,筛选当前元素 |
| not(selector) | 对当前元素提要求, 并取反 |
| has(selector) | 对子孙元素提要求(是否包含) |
2.7.2、查找
查找jQuery对象内部的元素的子孙/兄弟/父母元素, 并封装成新的jQuery对象返回
| 方法 | 描述 |
|---|---|
| children( ) | 子标签中找 |
| find( ) | 后代标签中找 |
| preAll( ) | 前面所有的兄弟标签 |
| nextAll( ) | 后面所有的兄弟标签 |
| siblings( ) | 前后所有的兄弟标签(不包含自己) |
| parent( ) | 父元素 |
2.7.3、添加/替换元素
| 方法 | 描述 |
|---|---|
| append(content) | 向当前匹配的所有元素内部的最后插入指定内容 |
| prepend(content) | 向当前匹配的所有元素内部的最前面插入指定内容 |
| before(content) | 将指定内容插入到当前所有匹配元素的前面 |
| after(content) | 将指定内容插入到当前所有匹配元素的后面替换节点 |
| replaceWith(content) | 用指定内容替换所有匹配的标签删除节点 |
| empty() | 删除所有匹配元素的子元素(是掏空节点) |
| remove() | 删除所有匹配的元素(是删除节点) |
2.8、事件绑定
eventName(function(){}) 绑定对应事件名的监听
//方式一
$('#div').click(function(){});
//方式二
$('.out').mouseenter(function () {
console.log('mouseenter移入')
}).mouseleave(function () {
console.log('mouseleave移出')
})
优缺点:编码方便, 但只能加一个监听, 且有的事件监听不支持
on(eventName, funcion(){}) 通用的绑定事件监听
例如:$('#div').on('click', function(){})
优缺点:编码不方便, 可以添加多个监听, 且更通用,滑动事件:mouseenter,mouseleave,mouseover,mouseout
hover 底层是使用mouseenter和mouseleave 实现的,如果值传递一个回调函数 则移入移出都执行这一个回调函数
$('.out').hover(function () {
console.log('移入');
},function(){
console.log('移出');
});
2.9、解除事件绑定
off() 解除事件监听的方法
- 如果不传参数 默认清除所有事件
- 允许传递参数,参数的类型为字符串,内容为事件名称,可以单独解绑一个或多个事件,多个事件名称用空格隔开
//解除全部
$('.out').off()
//解除指定某个
$('.out').off('click mouseleave');
2.10、事件委托
- 将多个子元素(li)的事件监听委托给父辈元素(ul)处理
- 监听回调是加在了父辈元素上
- 当操作任何一个子元素(li)时, 事件会冒泡到父辈元素(ul)
- 父辈元素不会直接处理事件, 而是根据event.target得到发生事件的子元素(li), 通过这个子元素调用事件回调函数
2.10.1设置事件委托
$(parentSelector).delegate(childrenSelector, eventName, callback)
在jquery的事件委托绑定中,jquery的底层帮我们修正了this的指向 直接指向当前触发这个事件的被委托的子元素
方式一:
- 由父元素调用,传递三个参数
- 参数1 委托的子元素
- 参数2:事件名称
- 参数3:回调函数
$('ul').delegate('li','click',function () {
this.style.background = 'pink'
})
方式二:
- 由父元素调用,传递三个参数
- 参数1:事件名称
- 参数2:委托的子元素
- 参数3:回调函数
$('ul').on('click','li',function () {
this.style.background = 'skyblue'
});
2.10.2、移除事件委托
$(parentSelector).undelegate(eventName)
- 解绑的操作 也是有父元素或祖先元素调用
$('#btn2').click(function () {
$('ul').undelegate()
});
2.11、jQuery扩展插件
-
扩展jQuery的工具方法
$.extend(object) -
扩展jQuery对象的方法
$.fn.extend(object)
2.12、onload和ready的却别
window.onload
- 包括页面的图片加载完后才会回调(晚)
- 只能有一个监听回调
$(document).ready()
- 等同于: $(function(){})
- 页面加载完就回调(早)
- 可以有多个监听回调
3、Bootstrap
简洁、直观、强悍的前端开发框架,让web开发更迅速、简单。
4、BFC
在解释 BFC 是什么之前,需要先介绍 Box、Formatting Context的概念
4.1、Box:CSS布局的基本单位
- Box 是 CSS 布局的对象和基本单位, 直观点来说,就是一个页面是由很多个 Box 组成的。
- 元素的类型和 display 属性,决定了这个 Box 的类型。
- 不同类型的 Box,会参与不同的 Formatting Context(一个决定如何渲染文档的容器)
- 因此Box内的元素会以不同的方式渲染。让我们看看有哪些盒子:
block-level box:
- display 属性为 block, list-item, table 的元素,会生成 block-level box。
- 并且参与 block fomatting context;
inline-level box:
- display 属性为 inline, inline-block, inline-table 的元素,会生成 inline-level box。
- 并且参与 inline formatting context
Formatting context
- Formatting context 是 W3C CSS1.1 规范中的一个概念。
- 它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。
- 最常见的 Formatting context 有 Block fomatting context (简称BFC),Inline formatting context (简称IFC)
4.2、BFC是什么
- BFC(Block formatting context)直译为"块级格式化上下文"。
- 它是一个独立的渲染区域,只有Block-level box参与,
- 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干
- 当开启了元素的BFC以后,父元素可以包含浮动的子元素。
4.3、BFC布局规则
- 内部的Box会在垂直方向,一个接一个地放置。
- 内部的Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
- BFC的区域不会与float box重叠。
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
4.4、BFC什么时候出现(哪些元素会生成BFC?)
- 根元素
- float属性不为none
- position为absolute或fixed
- overflow不为visible