JsvsScript基础整理
JsvsScript介绍
JavaScript是一门编译语言,也称为”动态”或”解释执行”语言。和传统的便于语言不通,他不是提前编译的,编译结果也不能再分布式系统中进行转移,任何js代码片段再执行前都要进行编译,通常会马上执行它。
进一步说,JavaScript 是一种基于原型、多范式、单线程的动态语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。
JsvsScript特点
是一门解释型语言:是指不需要被编译为机器码在执行,而是直接执行。
是一门动态语言,所谓的动态语言可以暂时理解为在语言中的一切内容都是不确定的。
是一门动态语言,所谓的动态语言可以暂时理解为在语言中的一切内容都是不确定的。
是一门面向对象的语言。
严格区分大小写。
JsvsScript基础
如何使用js
书写位置:
- 内部Js写在/body上方
1
2
3
4<script>
alert("Hello,World!");
</script> - 外部写在script里面
<script src="main.js"></script>
输入输出语法
prompt()输入
let uname=prompt('请输入姓名')
- document.write(‘向页面输出此内容’)
- alert(‘页面弹出警告对话框’)
- 控制台输出:
- console.log(‘控制台打印输出’)
- console.log(uname,age)
- console.info(“输出一条信息”);
- console.warn(“输出一条警告”);
- console.error(“输出一条错误”);
注解
// 单行注解
/* 多行注解 */
变量常量
变量声明var let
- 变量本质:是程序在内存中申请的一块用来存放数据的小空间
- var
是js最早的变量声明方式,有函数作用域,没有块级作用域
var声明过的变量可以重复声明(不合理但可以)
var可以先使用再声明(不合理但可以)
声明变量:var name=’xixi’
多个变量逗号隔开let age=22,uname=’西西’ - let
声明块级作用域,更安全,尽量使用
常量声明const
不能重新赋值,也有块级作用域
常量声明时候必须初始化
在JavaScript中,块级作用域是指变量在特定代码块中(如一个函数、循环或条件语句)声明后,其作用范围仅限于该代码块内
变量命名规则
- 不能用关键字
- 严格区分大小写:Name和name不一样
- 只能用下划线。字母,数字,$组成
- 数字不能开头
- 遵循小驼峰命名法,如userAge
数据类型
typeof运算符
typeof运算符检查数据是什么类型
console.log(typeof 变量)
原始数据类型
- String 字符串:表示文本数据,可以用单引号、双引号或反引号包围console.log(‘string’)
- Number 数字:表示整数和浮点数
输出单纯输出变量就可以,不要加引号 console.log(变量名) - Boolean 布尔值:表示true或false
- Null 空值:表示空或无值
- NaN 代表一个错误计算,它是一个不正确的或者一个未定义的数学操纵得到的结果
console.log(‘水果’-5) //NaN
console.log(NaN-5) //NaN - Undefined 未定义:表示变量未赋值
- Symbol 符号:表示唯一的标识符,常用于对象属性
之外的类型都称为Object
undefined值实际上是由null值衍生出来的,所以如果比较undefined和null是否相等,会返回true。
从语义上看null表示的是一个空的对象,所以使用typeof检查null会返回一个Object
引用数据类型
- Object 对象:键值对的集合,用于存储和管理数据
let person = {
name: ‘Bob’,
age: 30,
isEmployee: true
}; - Array 数组:有序集合,可以包含多种类型的数据
let fruits = [‘apple’, ‘banana’, ‘cherry’]; - Function 函数:可调用的代码块
字符串拼接
拼接字符串和变量:
document.write(‘我想买’+num+’个西瓜’)
模板字符串:拼接内容用${}包裹住变量
document.write(我想买${num}个西瓜
)注意用反引号
类型转换
隐式转换
隐式转换:某些运算被执行时,系统内部自动将数据类型进行转换
+号两边只要有一个是字符串,都会把另外一个转成字符串
除了+以外的算数运算发如-*/也都可以
使用:+号作为正号解析可以转换成数字型
任何数据和字符串相加的结果都是字符串
console.log(1+1) //2
console.log(1+’good’) //1good
console.log(2+’2’) //22
console.log(2-2) //0
console.log(2-‘2’) //0
console.log(11 +’11’) //22
console.log(1 * ‘1’) //1
console.log(+’123’) //123 转换为数字型(隐式转换)
console.log(+123) //123
console.log(typeof ‘123’) //string
console.log(typeof +’123’) //number
显示转换
自己写代码告诉系统该转换成什么类型
转换为数字型 Number(数据)
如:转化为int型,只要整数,一刀切
console.log(parseInt(‘12px’)) //12
console.log(parseInt(‘12.34px’)) //12
console.log(parseInt(‘12.67px’)) //12
转换为布尔值Boolean(数据)
转换为String类型
转换为字符串有三种方式:toString()、String()、 拼串(为任意的数据类型+””)
比如
1 | var a = 123; |
运算符
算数运算符
let sum = 5 + 3; // 8
let difference = 5 - 3; // 2
let product = 5 * 3; // 15
let quotient = 5 / 3; // 1.666…
let remainder = 5 % 3; // 2
let power = 5 ** 3; // 125
比较运算符
==两边的值是否相等
===两边的值和类型是否都相等(推荐使用)
let isEqual = 5 == ‘5’; // true
let isStrictEqual = 5 === ‘5’; // false
逻辑运算符
与&& 或|| 非!
let isAdult = age >= 18 && age < 65;
let isChildOrSenior = age < 18 || age >= 65;
let isNotAdult = !isAdult;
赋值运算符
将等号右边的值赋予给左边,左边必须是一个容器
赋值:= 加法赋值:+= 减法赋值:-= 乘法赋值:*=
除法赋值:/= 取余赋值:%= 幂赋值:**=
自增运算符(同自减运算符)
前置自增 先增加再运算 let i=1 console.log(++i +1) //i=2 结果3
后置自增 先运算再增加 let i=1 console.log(i++ +1) //结果2 i=2
三元运算符
条件 ? 满足条件执行的代码:不满足条件执行的代码
如:判断输出是否小于10,如果小于0前面补0
num<10? 0+num : num
运算符优先级
运算符优先级由上到下依次减小,对于同级运算符,采用从左向右依次执行的方法。
语句
表达式:能有一个返回的结果
语句:一段可以执行的代码(行为)
分支结构(选择执行)
if用于区间判断,switch用于等值判断
if语句
根据条件判断真假,来选择性的执行代码
if(条件){
满足条件执行的代码
}
条件为真则执行{ }里的代码
1 | if(expression1) |
三元运算符
条件 ? 表达式1 : 表达式2
条件满足执行1不满足执行2
switch语句
1 | switch(表达式){ |
1 | var today = 1; |
循环结构(重复执行)
while
1 | while(循环条件){ |
1 | var i = 1; |
do…while
do…while循环会至少执行一次
1 | do{ |
1 | var i = 1; |
for
for(初始化表达式 ; 循环条件表达式 ; 更新表达式){
语句…
}
1 | for (var i = 1; i <= 10; i++) { |
break、continue和label
break:结束最近的一次循环,可以在循环和switch语句中使用。
continue:结束本次循环,执行下一次循环,只能在循环中使用。
如果想要跳出多层循环或者跳到指定位置,可以为循环语句创建一个label,来标识当前的循环,如下例子:
1 | outer: for (var i = 0; i < 10; i++) { |
数组
数组:(Array)是一种数据类型,属于引用数据类型作用:在单个变量名下存储多个数据
注意数组是按序保存的,可以存储任意类型数据
创建数组
1 | var arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]; |
遍历数组
- for循环
数组长度:数组名.length,长度比索引多一个1
2
3
4let arr = [数据1,数据2,...,数据n]
for(let i = 0 ; i <= arr. length-1 ;i++){
i console.log(names[i])
} - forEach遍历数组
1
2
3
4
5
6
7var arr = ["西瓜", "苹果", "鸭梨"];
arr.forEach(function (value, index, obj) {
console.log(value + index + obj);
});
西瓜 0 西瓜,苹果,鸭梨
苹果 1 西瓜,苹果,鸭梨
鸭梨 2 西瓜,苹果,鸭梨
数组方法
push()方法
push()方法:可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
1 | var arr = ["1", "2", "3"]; |
pop()方法
pop()方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回
1 | var arr = ["1", "2", "3"]; |
unshift()方法
unshift()方法向数组开头添加一个或多个元素,并返回新的数组长度
shift()方法
shift()方法可以删除数组的第一个元素,并将被删除的元素作为返回值返回
forEach()方法
forEach()方法用来遍历数组
1 | var arr = ["西瓜", "苹果", "鸭梨"]; |
slice()方法
slice()方法用来从数组提取指定元素,该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回
索引从0开始
第一个参数:截取开始的位置的索引,包含开始索引
第二个参数:截取结束的位置的索引,不包含结束索引,第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
注意:索引可以传递一个负值,如果传递一个负值,则从后往前计算,-1代表倒数第一个,-2代表倒数第二个。
1 | var arr = ["西瓜", "苹果", "鸭梨","草莓","哈密瓜"]; |
splice()方法
splice()方法用于删除数组中的指定元素,该方法会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回
第一个参数:表示开始位置的索引
第二个参数:表示要删除的元素数量
第三个参数及以后参数:可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
concat()方法
concat()方法连接两个或多个数组,并将新的数组返回,该方法不会对原数组产生影响
1 | var arr = ["孙悟空", "猪八戒", "沙和尚"]; |
join()方法
join()方法将数组转换为一个字符串,该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回,在join()中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符,如果不指定连接符,则默认使用,作为连接符
1 | var arr = ["孙悟空", "猪八戒", "沙和尚"]; |
reverse()方法
reverse()方法用来反转数组(前边的去后边,后边的去前边),该方法会直接修改原数组
1 | var arr = ["孙悟空", "猪八戒", "沙和尚"]; |
对象
对象是js里的一种数据类型(引用类型),也是用于存储数据的
对象本质是无序的数据集合
好处:可以用来详细的描述某个事物,是用键值对的形式存储,语义更明了
声明对象使用大括号;声明数组使用中括号
属性和方法
属性:对事物的描述信息,如姓名一般是名词
方法:事物的行为,如跑步一般是动词
方法是成对出现的,包括方法名和匿名函数
方法名和匿名函数之间使用英文∶分隔
多个方法之间使用英文,分隔,方法是无序的
方法是依附在对象中的函数(对象外是函数,对象内是方法)
对象方法也可以传参
创建对象两种方法
1 | let dog = { |
1 | let dog = new Object(); |
访问属性两种方法
调用对象的sing方法
dog.sing( )
- 对于多词属性比如中横线分割的属性dog-ben,点操作就不能用了
我们可以采取:对象 [ ‘ 属性 ‘ ] 方式,单引号和双引号都可,也可以用于其他正常属性1
2
3
4
5
6
7let obj={
dog-ben = "笨笨"
age = 6
}
获取属性
obj [ ' dog-ben ' ]
obj [ ' age ' ] - 点语法:使用.操作符访问属性
console.log(person.name); // 输出: Alice - 方括号语法:使用[]符操作访问属性,通常用于动态属性名或属性名包含特殊字符
console.log(person[‘age’]); // 输出: 25
const key = ‘name’;
console.log(person[key]); // 输出: Alice
增删改属性
- 添加属性:直接为对象指定新的属性名和值
person.job = ‘Engineer’; - 修改属性:通过属性名直接赋值即可
person.age = 26; - 删除属性:使用delete操作可以符删除对象的属性
delete person.age;
遍历对象中的属性
枚举遍历对象中的属性,可以使用for … in语句循环,对象中有几个属性,循环体就会执行几次
1 | for (var 变量 in 对象) { |
1 | var person = { |
构造函数
构造函数:构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写,构造函数和普通函数的还有一个区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
那构造函数是怎么执行创建对象的过程呢:
调用构造函数,它会立刻创建一个新的对象
将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
逐行执行函数中的代码
将新建的对象作为返回值返回
1 | function Person(name, age) { |
构造函数是一种用于创建特定类型对象的函数,通常以大写字母开头,通过new关键字创建实例。
类 class
ES6 引入了class关键字,使得定义对象和继承更加简洁。
1 | class Person { |
对象数组
例:
1 | let students =[ |
- 遍历对象数组:把对象数组中的所有家乡打印出来
利用for in 遍历对象
for (let 变量 in 对象名) {
console.log(变量) //属性名
console.log( 对象 [ 变量 ] ) //属性值
}
for (let key in 对象名) {
console.log(变量) //属性名
console.log( 对象名 [key] ) //属性值
//注意 key是每一个属性名,类型是字符串,所以必须用 [ ] 语法
}
对象操作
for…in循环:遍历对象的可枚举属性
1 | const person = { |
合并对象 Object.assign():
Object.assign():将一个或多个源对象的属性复制到目标对象
1 | const target = { a: 1, b: 2 }; |
展开运算符:
展开运算符:ES6提供了一种更简洁的对象合并方式
1 | const result = { ...target, ...source }; |
对象解构
ES6提供了一种简洁的方法从对象中提取属性值并给变量
1 | const person = { name: 'Alice', age: 25 }; |
函数
函数:可以被重复使用的代码
函数可以把具有相同或相似逻辑的代码
“包裹”起来,这么做的优势是有利于代码复用
定义函数:
无参函数
function 函数名( ) {
代码块
}
调用函数:函数名( )
什么时候调用函数,什么时候执行
有参函数
function 函数名( 参数1,参数2) {
return 参数1+参数2
}
调用函数:函数名(参数1,参数2 )
调用函数输出结果是参数之和参数1+参数2
匿名函数
没有名字的函数
function(){
代码块
}
- 函数表达式(函数表达式不能再函数声明之前调用)
let fn = function(){
代码块
}
调用fn( ) - 立即执行函数
不需要调用立即执行(本质上已经调用过了)
避免全局变量之间的污染,多个立即执行函数要用分号隔开,不然会报错1
2
3
4//立即执行函数写法一 注意分号!
(function ( ) { } ) ( ) ;
//立即执行函数写法二
( function( ) { } )
箭头函数
ES6 引入简洁的函数语法。箭头函数不会创建自己的this,而是从作用父域中继承this
1 | const add = (a, b) => a + b; |
- 无参数父母参数:当函数没有参数或有多个参数时,需要用逗号
1
2const sayHi = () => console.log('Hi!');
const multiply = (a, b) => a * b; - 多行函数体:如果函数体包含多行代码,需要用大括号,并使用return语句返回值。
1
2
3
4const sum = (a, b) => {
const result = a + b;
return result;
}; - 如果函数只有一个形参,小括号可以省略
- 只有一行代码的时候,可以省略大括号和return
- 可也以返回一个对象
嵌套函数
在函数中声明的函数就是嵌套函数,嵌套函数只能在当前函数中可以访问,在当前函数外无法访问
1 | function fu() { |
创建多个对象(重点)
1 | // 使用工厂模式创建对象 |
构造函数
构造函数:构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写,构造函数和普通函数的还有一个区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
那构造函数是怎么执行创建对象的过程呢:
调用构造函数,它会立刻创建一个新的对象
将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
逐行执行函数中的代码
将新建的对象作为返回值返回
1 | function Person(name, age) { |
构造函数是一种用于创建特定类型对象的函数,通常以大写字母开头,通过new关键字创建实例。
形参与实参
形参是在函数定义时声明的参数,它们是函数的占位符,用于接收调用时确定的实际值
实参是函数调用时传递给函数的具体值,它们对应于函数定义中的形参
1 | function greet(name, greeting) { |
name和greeting是形参。它们定义了greet函数所需的输入
‘Alice’和’Hello’是实参,它们在函数调用时传递给greet函数的形参name和greeting
- 在开发中保持实参与形参一致
形参多于实参:那么多出来的形参值为undefined
实参多于形参:多出来的实参会被忽略 - 可以给形参设置默认值
注意:这个默认值只会在缺少实参传递或者实参是undefind才会被执行
剩余参数…
用…收集不确定数量的实际参量作为一个备份,从而处理多余的参数
1 | function showItems(...items) { |
展开运算符…
剩余参数只能用在函数里,展开运算符哪里都可以用
1 | const arr = [1,5,3,8,2] |
不会修改原数组。如果求数组最大值,用…arr很方便
动态参数
arguments 动态参数,只存在函数体里面,是伪数组
function getSum() {
// arguments 动态参载只存在函数里
console.log( arguments)
}
getSum(2,3)
比如不管用户传入多少实参,都要计算出来
返回值
通过return关键字把处理后的结果返回给调用者
return会立即结束当前函数,后面代码不会再被执行
return关键字和被返回的表达式之间不允许使用换行符
函数可以没有return,此时函数默认返回值为undefined
全局作用域
作用域所有代码执行的环境(整个script标签内部)
局部作用域作用与函数内的代码环境,分函数作用域或块级作用域
闭包
- 闭包是指一个函数能够访问其词法作用域中的变量,即使这个函数是在其词法作用域之外执行的。换句话来说,闭包可以记住并访问定义它的作用域中的变量
- 闭包是指在函数内部定义的函数可以访问其外部函数的变量和参数,即使外部函数已经返回
- 简单理解:闭包=内层函数+外层函数的变量
(函数套函数,里层函数用到外层函数的变量) - 闭包如何工作
当一个函数在其内部定义了另一个函数时,内部函数可以访问外部函数的变量和参数,即使外部函数已经执行完毕。这个机制使得内部函数有了一个闭包,闭包保存了外部函数的作用域链。 - 应用:用闭包函数创建避免全局污染;实现数据的私有
例:
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 输出: 1
console.log(counter()); // 输出: 2 - 闭包的性能及注意事项
- 内存消耗:闭包可能会导致内存占用增加,因为它们保留了对外部作用域的引用,可能会导致内存浪费。在创建大量闭包时需要小心
- 避免不必要的闭包:如果不需要访问外部变量,尽量避免创建闭包,以免增加复杂性和内存使用
- 调试困难:由于闭包涉及高层作用域链,调试和跟踪变量的生命周期可能会比较困难
递归
函数在其内部调用自身
function factorial(n) {
if (n === 0) {
return 1;
}
return n * factorial(n - 1);
}
console.log(factorial(5)); // 输出: 120
其他
单一职责原则:每个功能只做一件事,以便于测试和维护。
剩余参数是真数组,动态参数伪数组。真数组可以用push等方法,开发中提倡用剩余参数
避免全局变量:尽量使用局部变量来避免变量冲突和意外的全局状态更改。
纯函数:首先编写纯函数,即不依赖外部状态并且不修改外部状态的函数,然后进行测试和调试。
错误处理:使用try…catch块捕获和处理函数中的异常
函数参数解构:当传递的对象有多个属性时,可以使用参数解构提高代码的可执行性。
function displayUser({ name, age }) {
console.log(Name: ${name}, Age: ${age}
);
}
const user = { name: ‘Dave’, age: 25 };
displayUser(user);
函数中的this关键字
this关键字:指向函数执行时的上下文对象。不同的调用方式下,this的值可能不同。
- this的绑定和定义的位置(编写的位置)没有关系
- this的绑定和调用方式以及调用的位置有关系
- this是在运行时被绑定的;
this的绑定规则
作为普通函数调用:this指向全局对象(在浏览器中为window对象)
函数没有被绑定到某个对象上进行调用1
2
3
4
5
6
7
8
9
10//1.普通的函数被独立的调用
function foo(){
console.log("foo:",this)}
foo()
//2.函数定义在对象中,但是独立调用
var obj={name: "why",bar:function(){
console.log("bar:", this)
}}
var baz = obj.bar
baz()作为对象方法调用:this指向调用该方法的对象
构造函数调用:this指向新创建的对象
箭头函数调用:this指向定义箭头函数时的上下文对象
1
2
3
4
5
6
7const person = {
name: 'Charlie',
greet: function() {
console.log(`Hello, I'm ${this.name}`);
}
};
person.greet(); // 输出: Hello, I'm Charlie箭头函数中的this:不会创建自己的this,而是从父级上下文中继承
1
2
3
4
5
6
7
8
9const person = {
name: 'Charlie',
greet: function() {
setTimeout(() => {
console.log(`Hello, I'm ${this.name}`);
}, 1000);
}
};
person.greet(); // 输出: Hello, I'm Charliebind、call、apply调用:this指向指定对象
常用函数
随机函数Math.random()
Math.random()随机数函数。返回一个[0,1)的随机小数
生成0-10的随机数Math.floor(Math.random()(10+1))
5-10的随机数Math.floor(Math.random()(5+1)+5)
n-m的随机数Math.floor(Math.random()*(m-n+1)+n)
函数剩余参数arguments
动态参数:
arguments是函数内部内置的为驻足变量,它包含了调用函数时传入的所有实参
arguments是一个伪数组,只存在函数中,可以通过for循环依次得到传递过来的实参
如:function sum(){
les s=0
for(let i=0; i<arguments.length; i++){a+=arguments[i]}
console.log(s)
}
sum(1,5)
sum(2,4,9)
函数剩余参数
…是语法符号,置于最末函数形参之前,用于获取多余的实参,得到的是真数组
如:function sum(a,…arr){
console.log(arr)//[5][4,9]
for(let i=0; i<arguments.length; i++){a+=arguments[i]}
console.log(arr)
}
sum(1,5)
sum(2,4,9)
如:function sum(…arr){
console.log(arr)//[1,5][2,4,9]
for(let i=0; i<arguments.length; i++){a+=arguments[i]}
console.log(arr)
}
sum(1,5)
sum(2,4,9)
toString方法
oString()函数用于将当前对象以字符串的形式返回
1 | // 使用构造函数来创建对象 |
hasOwnProperty()检查对象是否含有该属性
1 | // 创造一个构造函数 |
展开运算符…
const arr=[1,2,3,4,5,6,7]
console.log(…arr) // 1 2 3 4 5 6 7
场景:求数组最大值,最小值,合并数组
console.log(Math.max(…arr)) //7
作用域
变量或值在代码中可用性的范围
局部作用域
函数作用域
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问
1.函数内部声明的变量,在函数外部无法被访问
2.函数的参数也是函数内部的局部变量
3.不同函数内部声明的变量无法互相访问
4.函数执行完毕后,函数内部的变量实际被清空了
块作用域
在JavaScript中使用{}包裹的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问。
1.let声明的变量会产生块作用域,var 不会产生块作用域
2.const声明的常量也会产生块作用域
3.不同代码块之间的变量无法互相访问
4.推荐使用let 或 const
let声明的外面无法访问,var声明的外面可以访问。var没有块作用域。
全局作用域
script标签和.js 文件的【最外层】就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。全局作用域中声明的变量,任何其它作用域都可以被访问
1.为window对象动态添加的属性默认也是全局的,不推荐!
2.函数中未使用任何关键字声明的变量为全局变量,不推荐!
3.尽可能少的声明全局变量,防止全局变量被污染
作用域链
作用域链本质上是底层的变量查找机制。
在函数被执行时,会优先查找当前函数作用域中查找变量
如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
1.嵌套关系的作用域串联起来形成了作用域链
2.相同作用域链中按着从小到大的规则查找变量
3.子作用域能够访问父作用域,父级作用域无法访问子级作用域
垃圾回收机制
- 内存的生命周期
内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
内存使用:即读写内存,也就是使用变量、函数等
内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存 - 全局变量一般不会回收(关闭页面回收)
一般情况下局部变量的值,不用了,会被自动回收掉 - 内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏
- 堆栈空间分配区别:
1.栈(操作系统)︰由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面。
2.堆(操作系统)︰一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆 - JS垃圾回收机制-算法说明
- 引用计数法:内存不再使用,就该回收。看一个对象是否有指向它的引用
算法:
1.跟踪记录被引用的次数
2.如果被引用了一次,那么就记录次数1,多次引用会累加++3. 如果减少一个引用就减1 –
4.如果引用次数是0,则释放内存 - 标记清除法(现在常用)
核心:
1.标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
2.就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。
3.那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
- 引用计数法:内存不再使用,就该回收。看一个对象是否有指向它的引用
APIs DOM
APIs作用:通过js去操作html和浏览器
DOM文档对象模型,操作网页内容(标签)
DOM树:将Html文件以树结构直观地表现出来,直观体现标签和标签之间的关系
DOM对象:把一个html标签用js取过来那么它会变成一个对象Object类型,拥有属性方法内容等,此时叫dom对象
document:的DOM里面最大的一个对象,整个页面最大的对象
获取、操作DOM对象
使用css选择器获取
1.匹配的第一个元素
1 | document.querySelector('css选择器') 必须加单引号,是字符串 |
2.匹配的所有的(多个)元素
document.querySelectorAll(‘css选择器’)
返回值:对象集合(是伪数组)
不能直接修改,只能通过数组遍历的方法依次给里面的元素修改
document.querySelector(‘ul li’) 选择所有的li
3.根据ID获取第一个元素
1 | document.getElementById('ID') |
操作元素内容
修改标签元素里面的内容
- 对象.innerText
将文本内容添加/更新到任意标签位置,显示纯文本,不解析标签
obj.innerText=’我是新修改的内容’ - 对象.innerHtml
将文本内容添加/更新到任意标签位置,显示纯文本,会解析标签,多标签建议使用模板字符obj.innerText='<strong>我是新修改的内容</strong>'
操作元素属性
1.操作元素常用属性
对象.属性=’值’ 常见属性:href,title,src
2.操作元素样式属性,如位置,大小
对象.style.属性=’值’ 用于量少的时候
元素.className=’新类名’ 用于修改大量样式
元素.className.add(‘类名’) 追加一个类
元素.className.remove(‘类名’) 删除一个类
元素.className.toggle(‘类名’) 切换一个类
3.操作表单元素属性,如文本框,按钮
对象.属性名=’值’
表单.value=’用户名’
表单.type=’password’
如果表单中的属性添加就有效果,移除没有效果,那么一律用布尔值表示,true代表添加了,flase代表移除了(如disable,checked,selected)
const ipt=document.querySelecter(‘input’)
input.checked=’true’
231,214,232性 data-自定义属性
对象.adtaset.属性名=’值’
间歇函数(定时器)
定时器函数重复执行代码,定时器函数可以开启和关闭定时器
- 开启定时器
setInterval(函数名!,间隔时间) Interval间歇,设置间歇函数 - 每隔一段时间调用这个函数,时间单位是毫秒
如:写一个间歇函数,每隔1s打印一次,函数这里是匿名函数再如:1
2
3setInterval(function(){
console.log('夏天和西瓜更配哦')
},1000)1
2function fn(){ console.log('夏天和西瓜更配哦')}
setInterval(fn,1000) - **注意!这里是fn不是fn(),fn()**会立刻调用函数,不会每隔一秒再入调用。间隔函数会自动根据函数名去调用函数,所以要写函数名而不是写函数名加()调用函数
- 返回值:定时器的id,是一个数字
let n=setInterval(fn,1000)
这个n是一个数字,关计时器用 - 关闭定时器
let 变量名=setInterval(函数,间隔时间)
clearInterval(变量名)
变量名是定时器返回的ID号 - 关闭定时器后再打开
n=setInterval(fn,1000) n是刚才关闭定时器的变量名
事件监听
事件:在编程时系统内发生的动作或发生的事情,如点击按钮
事件监听;让成语检测是否有事件发生,一旦有时间触发,就立即调用一个函数做出响应,也称为绑定时间或者注册事件
对象元素.addEventListener(‘事件类型’,要执行的函数)
三要素:
- 事件源:获取被事件触发的元素
- 事件类型:用什么方式触发的,如鼠标点击,鼠标经过
- 事件调用的函数:这个元素要做什么事儿
- 执行函数是被触发之后执行,每次触发都会执行一次
1
2const btn=document.querySelecter('.btn')
btn.addEverntListener('click',function(){alter('我被点击啦')})
事件类型
鼠标事件:鼠标触发
click 鼠标点击
mouseenter 鼠标经过
mouseleave 鼠标离开
焦点事件:表单获得光标
focus 获得焦点
blur 失去焦点
键盘事件:键盘触发
Keydown 键盘按下触发
Keyup 键盘抬起触发
文本事件:表单输入触发
input 用户输入事件
事件对象
事件对象:也是对象,存储事件触发时的相关信息,如鼠标信息,键盘信息
获取事件对象
在事件绑定的回调函数的第一个参数就是事件对象,一般命名为event,ev,e,如下面的e
元素.addEventListener(‘click’,function(e){})
注意,事件对象只有这个事件被触发之后才会拥有,事件不触发没有这个事件对象
事件对象常用属性
type:获取当前的事件类型
clientX/clientY:获取光标,相当于浏览器可见窗口左上角的位置
offsetX/offsetY:获取光标,相当于当前DOM元素左上角的位置
key:用户按下的键盘建的值
如:
input.addEventListener(‘keyup’,function(e){
if(e.key===’Enter’){
console.log(‘回车键被按下啦’)
}
})
环境对象
能够分析判断函数运行在不同环境中this所指代的对象,可以让我们的代码更简洁
每个函数里面都有this环境对象,普通函数里面this指向的是window
在事件函数里面,this指向的是调用者,如给点击按钮绑定事件,this指向的是按钮button
回调函数
如果将函数A作为参数传递给函数B时,我们称函数A为回调函数
即当一个函数当作参数来传递给另外一个函数的时候,这个函数就是回调函数
回调函数的很值还是函数,只不过把它当作参数使用了
使用匿名函数作为回调函数比较常见
如
function fn(){console.log(‘我是fn函数我被传递给了setInterval,我fn是回调函数’)}
setInterval(fn,1000)
此时A就是fn,B就是setInterval
事件流
事件捕获和事件冒泡
事件流指的是事件完整执行过程中的流动路径,包括捕获阶段和冒泡阶段
捕获阶段:从document到div,从父到子
冒泡阶段:从div到document,从子到父
实际开发中以事件冒泡为主
事件捕获需要写对应代码才能看到效果
DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
低山和参数默认fales代表冒泡阶段触发,写true代表捕获阶段触发
事件冒泡:当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发,这一过程叫事件冒泡
可以理解为:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
事件冒泡是默认存在的
阻止冒泡和捕获
想要把事件限制在当前元素内,就需要阻止事件冒泡,前提:拿到事件对象
语法:事件对象.stopPropagation() Propagation:流动(事件捕获也能组织,不过用不到,此方法可以阻断事件流动传播)
解绑事件
on事件方式:用null覆盖
如:btn.onclick=function(){alter(‘我被点啦’)}
解绑:btn.onclick=null;
场景:一个按钮只想让它被点击一次,点击有限次之后不能再点击
addEventListener事件方式:必须用removeEventListener(事件类型,事件处理函数,[获取捕获或者冒泡阶段]) []打参数可以省略
如:function fn(){alter(‘我被点啦’)}
绑定事件:btn.addEventListener(‘click’,fn)
解绑事件:btn.removeEventListener(‘click’,fn)
匿名函数无法解绑
鼠标经过事件区别
鼠标移入和鼠标移开:
mouseover和mouseout有冒泡效果
mouseenter和mouselesve没有冒泡效果(推荐)
事件委托
理解:如果给ul里面的多个li绑定点击会弹窗的事件,以前可以用for循环实现,但是很笨;使用事件委托这个技巧一次就能实现
事件委托利用事件流事件冒泡的特性解决一些开发需求的知识技巧,父元素注册事件,当触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件,不再需要给每个子元素都绑定
优点:减少绑定次数,提高程序性能
ul.addEventListener(‘click’,function(){})点击ul里面的li执行父级点击事件
实现:事件对象.target.tagName 可以获取真正触发事件的元素
如:const ul=document.querySelector(‘ul’)//先获取父元素
ul.addEventListener(‘click’,function(){})//再绑定事件
阻止默认行窃
比如阻止链接的跳转,表单域跳转
语法:在函数里面加e.preventDefault()
注意和阻止冒泡不一样
页面加载事件,页面滚动使事件,页面尺寸事件,元素尺寸位置
页面加载事件
理解:加载外部资源(图片,外联cssjs等)加载完毕时触发的事件
有时需要等页面资源全部处理完再做时间事情,正常js写在/body上方,但是老代码喜欢写在head中,此时直接找dom元素找不到
事件名:load 给整个页面资源添加
如:等待页面所有资源加载完毕,就回去执行回调函数
window.addEventListener(‘load’,function(){})
事件名:DOMContentLoaded 给doocument元素加
当初始的Html文档被完全加载解析完成后DOMContentLoaded事件被触发,无需等待样式表,图像等完全加载
document.addEventListener(‘DOMContentLoaded’,function(){})
页面滚动使事件
滚动条在滚动的时候持续触发的事件
理解:有些网站在滚动条滚动到不同区域的时候会触发导航栏的变化
事件名:scroll
如:监听整个页面滚动(页面滚动就触发):
window.addEventListener(‘scroll’,function(){})
获取位置的两个属性:
记录滚动被卷曲的部分,可以读可以写
scrollLeft滚动的左侧
scrollTop滚动的上侧
页面被滚动卷去的尺寸:
window.addEventListener(‘scroll’,function(){
//注意这行代码要写在函数里面,得到的n是数字型不带单位
let n=document.documentElement.scrollTop
配合修改样式:
if(n>100){}
})
页面尺寸事件
会在窗口尺寸改变的时候触发事件
事件名:resize
window.addEventListener(‘resize’,function(){})
获取元素可见部分的宽高(不包括border,margin,滚动条等;包括padding)
clientWideh可见高度
clientHeight可见宽度
如检测屏幕宽度
window.addEventListener(‘resize’,function(){
let w=docuemnt.documentElement.clientWidtn
console.log(w)
})
元素尺寸位置
如果自己算可能有误差,用js可以得到元素在页面中的位置,省去计算
获取元素宽高:包含padding,border
获取可视宽高,是数值方便计算,如果盒子是隐藏的那么获取结果是0
offsetWidth
offsetHeight
获取位置:元素距离自己定位父级元素的左、上的距离
没有定位那么以页面左上角为准
下面这两个是只读属性
offsetLeft
offsetTop
日期对象
日期对象和方法
表示时间的对象
当前的系统时间
const date = new Date()
指定时间
const date1 = new Date(‘2024-8-15 14:20:30’)
前面返回的数据不能直接使用,开发中要使用方法转换为常用的格式
getFullYear() 获取年份 结果是4位年份
getMonth() 获取月份 取值0-11,注意打印要+1
getDate() 获取月份中的天,不是从0开始
getDay() 获取星期 取值0-6
getHours() 获取小时 取值0-23
getMinutes() 获取分钟 取值0-59
getSecond() 获取秒 取值0-59
使用如:
console.log(date.getFullYear())
console.log(date.getFullYear()+3)
在页面显示当前时间:
const pre = document.getElementById(‘myDate’)
function getMyDate() {
const date = new Date()
return 今天是:${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate() + 1}日 ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}
let h = date.getHours()
h = h < 10 ? ‘0’ + h : h
}
setInterval(function () {
pre.innerHTML = getMyDate()
}, 1000)
时间戳
时间戳:1970年01月01日00:00:00秒至今的毫秒数
理解:比如用于计算倒计时的效果要借助时间戳计算
算法:将来的时间戳-当前的时间戳=剩余的时间毫秒数,剩余的时间毫秒数转换为剩余时间的年月日时分秒就是倒计时时间
获取时间戳方法1:getTime()
const date =new Date()
console.log(date.getTime())
获取时间戳方法2:+new Date()
console.log(+new Date())
console.log(+new Date(‘2024-8-15 15:02:35’))
获取时间戳方法3:Date.now()
console.log(Date.now())
注意:第三种只能得到当前的时间戳,前两种可返回指定时间的时间戳
节点操作
DOM节点
DDOM树里面的每一个内容都称为节点
元素节点(重点):所有的标签,html,body,div
属性节点:所有的属性,如href
文本节点:所有的文本
其他,如注释节点
查找节点
查找节点:根据节点关系查找目标节点
理解:如有一个广告,广告里面有关闭的x,点击关闭的x关闭的是广告,还要获取广告的div。但是点击关闭的x,直接关闭它的父亲,就不用再获取广告div了
节点关系:针对的找亲戚返回的都是对象
父节点查找:
子元素.parentNode
返回最近一级的父节点对象(亲父亲,干爹不算),没找到返回null
还可以套娃 子元素.parentNode.parentNode
子节点查找
父元素.children
获取所有子节点,包括文本节点,注释节点等
返回的是一个伪数组
console.log(ul.children)
兄弟节点查找
下一个兄弟节点nextElementSibling
上一个兄弟节点previousElementSibling
const li2 = document.querySelector(‘ul li:nth-child(2)’)
console.log(li2.previousElementSibling)
增加节点
理解:如留言板,发布一条新增一条,再页面中增加元素时用
操作:创建一个新的节点,把新的节点放入到指定的元素内部
创建一个新的元素节点
document.createElement(‘标签名’)
如创建一个新的div元素
const div=document.createElement(‘div’)
插入到父元素中的某个子元素里面
如插入到这个父元素的最后一个子元素
父元素.appendChild(要插入的元素)
document.body.appendChild(div)
如插入到这个父元素中的某个子元素的前面
父元素.insertBefore(要插入的元素,在哪个元素前面)
克隆节点
复制一个原有的节点,把复制的节点放入到指定的元素内部
克隆节点
元素.cloneNode(布尔值)
true,克隆时会包含后代节点一起克隆
false(默认),克隆不会包含后代节点
删除节点
一个节点在页面中已不需要,要通过父元素删除,如果不存在父子关系那么删除失败
父元素.removeChild(子元素)
移动端事件和js插件swiper
触屏事件touch,响应触控笔或用户手指
常见触屏事件:
touchstart 触摸到一个DOM元素时触发
touchmove 在一个DOM元素上滑动时触发
touchend 从一个DOM元素上移时触发
js插件
swiper插件
多个swiper插件同时使用时,注意区分类名
BOM
BOM 浏览器对象模型,提供了与浏览器窗口进行交互的对象集合,核心对象是window。BOM没有统一的标准,兼容性较差,它包含了document、location、navigator、screen、history等对象
Window对象
Window对象是BOM的核心,代表当前浏览器窗口或标签页。
包含了许多方法和属性,如:
alert(), prompt(), confirm():用于弹出对话框。
setTimeout(), setInterval():用于定时执行代码。
location:包含当前页面的URL信息,并允许你导航到其他页面。
history:提供对浏览器历史记录的操作。
Navigator对象
提供有关浏览器的信息
常用属性有:
navigator.userAgent: 返回用户代理字符串,可用于识别用户的浏览器和操作系统
Screen对象
包含关于用户屏幕的信息。
常用属性有:
screen.width, screen.height: 屏幕分辨率。
Location对象
用于获取当前页面的URL地址,也可以用来加载新的页面。
方法包括:
location.reload(): 刷新当前页面。
属性包括:
location.href: 当前页面的完整URL。
js页面跳转,如(实际配合定时器使用):
location.href=’http://www.baidu.com‘
search属性获取地址中携带的参数,符号?后面的部分
console.log(location.search)
History对象
提供了对浏览器会话历史的访问。
方法包括:
history.back(): 后退一页。
history.forward(): 前进一页。
history.go(n): 移动n个历史记录条目
定时器
延时函数 (定时事件)
定时器用于在指定的时间后执行代码
setTimeout仅仅执行一次,需要等待,就算等待的毫秒数是0也要先执行后面的代码
setTimeout(回调函数,等待的毫秒数)
清除延时函数:
let timer = setTimout(回调函数,等待的毫秒数)
clearTimeout(timeoutId)
每一次调用延时器都会产生一个新的延时器
如:单击按钮,等待 3 秒,然后控制台会输出Hello
1 |
|
间歇函数
间歇函数每隔一段时间就执行一次,除非手动清除,延时函数只会执行一次
setInterval(回调函数,间隔的毫秒数):每隔指定时间重复执行函数
clearInterval(intervalId)清除定时器
其他
js执行机制
js语言是单线程,就是说同一个时间只能做一件事。
js的同步任务都在主线程上执行,形成一个执行栈
js的异步任务通过回调函数实现,在任务队列中等待
异步任务包括:普通事件(click等),资源加载(load等),定时器(间歇函数延时函数等)
数组map和join方法渲染表格
map()
字符串拼接:mao()和join()数组方法实现
map()遍历数组处理数据,并且返回新的数组
map有返回值,forEach没有返回值
如 const arr[‘西瓜’,’苹果’,’水蜜桃’,’香蕉’]
const newArr=arr.map(function(ele,index){
console.log(ele) //数组元素
console.log(index)//数组索引号
return ele+’味’
})
console.log(newArr)//[‘西瓜味’,’苹果味’,’水蜜桃味’,’香蕉味’]
join()
join()方法把数组中的所有元素转换成一个字符串
console.log(arr.join(‘’))//西瓜苹果水蜜桃香蕉
console.log(arr.join(‘|’))//西瓜|苹果|水蜜桃|香蕉
join()括号里指定分隔符,空字符串(‘’)表示元素之间都没有任何字符
渲染表格应用
理解:要在表格中根据数据新增一行数据
步骤:封装一个函数,函数里使用map()遍历数组(数据对象),更换tr里面的数据,然后返回有数据的tr数组
join()方法把有数组的数组转换成字符串
字符串通过innerHTML赋值给tbody
tbdoy.innerHTML=trArr.join(‘’)
正则表达式
是否包含某内容test()
定义规则// const reg=/里面写内容/
test()方法:查询是否有符合规则(和//里面的内容一样的内容)
语法:reg.test(被检测的字符串)
返回值:匹配true 不匹配false
如:const reg=/太阳/
console.log(reg.text(‘太阳当空照,花儿对我笑’)) //true
是否包含某内容exec()
在一个指定字符串中执行一个搜索匹配,成功返回一个数组,反正null
如:const reg=/西瓜/
console.log(reg.exec(‘夏天不能没有西瓜’))
返回[‘西瓜’,index:6,input’夏天不能没有西瓜’]
返回的数组中,index表示reg出现在6位(0123456)
元字符
边界符:表示位置,开头和结尾,必须用什么开头,用什么结尾
^表示匹配行首的文本(以谁开始)
$表示匹配行尾的文本(以谁结束)
console.log(/^夏/.test(‘夏天不能没有西瓜’)) //true
console.log(/^天/.test(‘夏天不能没有西瓜’))//false
console.log(/瓜$/.test(‘夏天不能没有西瓜’)) //true
console.log(/天$/.test(‘夏天不能没有西瓜’))//false
如果^和$放在一起叫精确匹配
console.log(/^西$/.test(‘西’)) //true
console.log(/西$/.test(‘西西’))//false
量词:设定某个模式的出现次数
重复0次或更多次
+重复1次或更多次
?重复0次或1次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n次到m次
注意逗号左右两侧千万不能有空格
console.log(/西$/.test(‘’))//true
console.log(/西*$/.test(‘西西’))//true
console.log(/西*$/.test(‘西’))//true
console.log(/西{2}$/.test(‘西西’))//true
如果^和$放在一起叫精确匹配,出现别的就是false
console.log(/西*$/.test(‘西西嘻’))//false
console.log(/西*$/.test(‘西嘻’))//false
字符类:
[]匹配字符集合,如[abd]那么/^[abd]$/.test(‘包含abc任意一个字符都true’)
[a-zA-Z0-9]
^[1-9][0-9]{4,}& 表示从10000开始,[0-9]{4}表示0000-9999
[^]取反号[^a-z]表示匹配除了小写字母以外的字符
.表示匹配除了换行符之外的任何单个字符
预定义类
\d表示0~9