JavaScript基础
一、js简介
1. JavaScript是什么
JavaScript是什么:一种运行在客户端(浏览器)的编程语言,
- 页面特效(监听用户的一些行为让网页作出对应的反馈)
- 表单验证(针对表单数据合法进行判断)
- 数据交互(获取后台数据,渲染到前端)
- 服务端编程(node.js)
JavaScript组成:
-
ECMAScript:
规定了js基础语法核心,比如变量、分支语句、循环语句、对象等
-
Web APIs:
- DOM:操作文档,比如对页面元素进行移动、大小、添加删除操作等
- BOM:操作浏览器,比如页面弹窗、检测窗口宽度、存储数据到浏览器等
2. 书写位置
-
内部JavaScript:直接写在html文件里面,在上面
alert('你好');
-
外部JavaScript:代码写在.js文件中
<!-- 通过src引入外部.js文件 --> <script src="index.js"></script>
-
内联JavaScript:代码写在标签内部(vue框架用的较多)
<button onclick="alert('你好')">按钮</button>
3. 注释和结束符
注释:单行注释// 多行注释/**/
结束符:可加可不加“;”
4. 输入输出语法
输出语法:
-
向body内输出内容
${}
语法是一种用于嵌入 JavaScript 表达式的方法,它可以在该位置动态计算表达式的值。document.write('abc'); <!-- 1级标签输出 --> document.write(`<h1>abc</h1>`);
-
页面弹出警告对话框
alert('你好');
-
控制台输出语法
console.log('控制台打印'); console.log(str.trim()); //去除左右两侧的空格
输入语法:
-
显示一个对话框,对话框里含一条文字信息,用来提示用户输入文字
prompt('请输入年龄');
字面量:计算机中描述事/物
二、变量
变量:存储数据容器
- 栈:存放简单数据类型
- 堆:存放引用数据类型
变量的本质
内存:计算机存储数据的地方,相当于一个空间
变量本质:程序在内存中申请一块用于存放数据的小空间
1. 变量声明和赋值
声明关键字 变量名 [= 值]
-
let关键字
let age = 18; //变量初始化 let name = 'zhang';
-
var关键字
var name = 1; var name = 2; //以最后一个为准
var定义的变量是函数级作用域或全局作用域的变量。如果变量在函数内声明,则它是局部变量,如果在函数外声明,则是全局变量。var定义的变量会发生变量提升,即在定义语句之前也可以使用这个变量名。而let定义的变量是块级作用域的变量。块级作用域是指在花括号({})内定义的任何内容。如果在函数内使用let定义变量,则它只能在该函数内部使用,并且不能在函数外使用。在同一个作用域内不能定义具有相同名称的let变量。少用var
2. 常量
常量:用const声明的变量,不可以改变,声明时候必须赋值
const a = 1;
变量声明:变量声明const优先
-
const声明对象可以修改里面属性,对象是引用类型,里面存放地址,只要地址不变,就不会报错
const arr = [] arr = [1, 2, 3] //不能用const const obj = {} //不能用const obj { name : 'a' }
三、数据类型
1. 基本数据类型
-
number 数字型
- 算数运算符:+、-、*、/、%
- 计算错误时会返回NaN,NaN和任何数字在一起都返回NaN。NaN != NaN
-
string 字符串型
-
用’ '、" "、``包裹的数据。外双内单或者外单内双
console.log('abc叫做"abc"'); //abc叫做"abc"
-
-
"+"拼接
console.log(1 + 1); console.log('a' + 'b'); console.log('a' + 1); let age = 19; console.log('数字是' + age);
-
模板字符串,反引号``
document.write(`数字是 ${age} `);
-
boolean 布尔型,true | false
- 空字符串’‘、0、false、null、undefined、NaN转换为Boolean型均为假(Boolean(’') == false),其他为真
-
undefined 未定义型,只声明未定义默认为该类型
-
null 空类型,赋值了但内容为空,尚未创建的对象
console.log(undefined + 1); //NaN console.log(null + 1); //1
-
检查数据类型
let num = 10; console.log(typeof num); //number let str = 'abc'; console.log(typeof str); //string let str1 = '10'; console.log(typeof str1); //string let flag = true; console.log(typeof flag); //boolean let un; console.log(typeof un); //undefined let obj = null; console.log(typeof obj); //object
使用表单、prompt获取过来的数据默认为字符串类型
-
隐式转换和显示转换
console.log(1 + 1); //2 console.log('1' + 1); //11 console.log(1 + '1'); //11 console.log(1 - 1); //0 console.log('1' - 1); //0 (除+号外的运算符都会把数据转成数字类型) console.log(+12); //12 console.log(+'12'); //12(+号作为正号可以转换为数字型) console.log(Number('11')); //11 console.log(Number('str')); //NaN console.log(parseInt('12px')); //12 console.log(parseInt('12.55px')); //12 console.log(parseInt('abv12px')); //NaN console.log(parseFloat('12px')); //12 console.log(parseFloat('12.55px')); //12.55 console.log(parseFloat('abc12.98px')); //NaN int num = +prompt('请输入一个数字'); //+将输入的内容转换为数字型
2. 引用数据类型
- object对象
3. 运算符
-
赋值运算符=、+=、-+、*=、/=、%=
-
自增运算符num++、++num、num–、–num,++num先自加再运算,num++先运算再自加
-
比较运算符>、<、>=、<=、、=(值和类型是否都相同)、!==(是否不全等)
console.log('3' !== 3); //true console.log('3' === 3); //false console.log('3' == 3); //true console.log(undefined == null); //true console.log(undefined === null); //false console.log('a' < 'b'); //true,利用ASCII字符代码表 console.log('aa' < 'ab'); //true
-
逻辑运算符&&、||、!
四、语句
一段可以执行的代码,不一定有值
1. 分支语句
-
if
let a = prompt('a = ') let b = prompt('b = ') if (a > b) { console.log('max=' + a); } else { console.log('max=' + b); }
-
条件 ? 满足条件执行代码 : 不满足条件执行代码
console.log(a < b ? 'max = ' + a : 'max = ' + b);
-
switch
let a = +prompt('a = ') switch (a) { case 1: console.log(++a); break; case 2: console.log(--a); break; default: console.log(a); }
2. 循环语句
-
while
while (a) { console.log(a--); }
-
for
for (let index = 0; index < arr.length; index++) { console.log(arr[index]); } for (let k in arr){} //不推荐遍历数组,可以遍历对象
五、数组
一组按顺序保存的数据类型,将一组数据存储在单个变量名下
- let arr = [数据1, 数据2, … , 数据n],可以同时包含数字类型、字符串类型等等
- let arr = new Array(数据1, 数据2, … , 数据n) 构造函数声明
基本使用:
let arr = [1, 2, 3, 4];
console.log(arr);
console.log(arr[1]);
console.log(arr.length);
for (let index = 0; index < arr.length; index++) { //遍历数组
console.log(arr[index]);
}
1. 数组操作
-
增
- arr.push() 直接加数组后面
- arr.unshift() 在数组开头插入
let arr = [1, 2, 3, 4]; arr.push('red'); //直接加数组后面,可以加多个值,用‘,’隔开 console.log(arr.push('blue')); //打印添加后数组长度 arr.unshift('green'); //在数组开头插入 console.log(arr); let arr = [33, 99, 21, 42, 23, 89, 44]; //将一个数组中满足条件的插入另外一个数组 let arr1 = []; for (let index = 0; index < arr.length; index++) { if (arr[index] > 50) { arr1.push(arr[index]); } } console.log(arr1);
-
删
- arr.pop() 删除最后一个元素
- arr.shift() 删除开头的素
- arr.splice(start, deleteCount) 起始位置,删除几个元素
let arr = [1, 2, 3, 4, 5, 6, 7, 8]; arr.pop(); //删除最后一个元素 arr.shift(); //删除开头的素 console.log(arr); //arr.splice(start, deleteCount) 起始位置,删除几个元素 arr.splice(2, 1); console.log(arr);
-
查 console.log(arr[2]);
-
改 arr[0] = ‘red’
2. 数组解构
将数组的单元值快速批量赋值给一系列变量
[变量] = [单元值]
const [a, b, c] = [1, 2, 3]
const [e, f] = [1, 2, 3]
const [g, h, i] = [1, 2,] //i = undefined
const [l, m, ...n] = [1, 2, 3, 4] //n = [3, 4]
const [o = 1, p = 2] =[3] //o = 3, p = 2
//const [a, b, c] = [1, 2, [3, 4]] c = [3, 4]
//const [a, b, [c, d]] = [1, 2, [3, 4]] c = 3, d = 4
let z = 1
let w = 2; //必须要加;
[z, w] = [w, z]
使用数组时需要加;
3. 静态方法
3.1 map迭代数组
map是一个高阶函数,可用于对数组中的每个元素应用相同的操作并返回一个新的数组
// 示例3:使用匿名函数将数组中的数字转换成字符串
const scores = [90, 85, 95];
const stringScores = scores.map(function(score) {
return score.toString();
});
console.log(stringScores); // 输出: ['90', '85', '95']
3.2 forEach遍历数组
调用数组的每个元素,并将元素传递给回调函数
//被遍历的数组.forEach(function(当前数组元素, 当前元素索引号){})
//只起遍历作用,不返回值。for循环加强版,适合于遍历对象
const arr = ['a', 'b', 'c']
arr.forEach(function(item, index){
console.log(item) //'a', 'b', 'c'
console.log(index) //索引号
})
3.3 reduce累计数组
返回累计处理的结果,一般用于求和
//arr.reduce(function(perv, current){}) 上一次的值prev,当前值current
const arr = [1, 2, 3, 4]
//没有初始值
arr.reduce(function(perv, current){
return prev + current //10
})
//有初始值
arr.reduce(function(perv, current){
return prev + current //30
}, 20)
3.4 join
join是一个数组的方法,它用于将数组的所有元素连接成一个字符串
const fruits = ['apple', 'banana', 'orange'];
const joinedString = fruits.join(', ');
console.log(joinedString); // 输出: "apple, banana, orange"
const joinedString1 = fruits.join('');
console.log(joinedString1); // 输出: "applebananaorange"
const joinedString2 = fruits.join();
console.log(joinedString2); // 输出: "apple,banana,orange"
join方法还可以用于将数组元素连接成不同的字符串形式。例如,如果我们不传递任何参数给join方法,它将默认使用逗号作为分隔符。如果我们想用空字符串将数组元素连接起来,我们可以传递一个空字符串作为参数:fruits.join(‘’)。
六、函数
被设计执行特定任务的代码块,function。(代码复用,提高效率)
1. 声明调用
function name(params) {}
name()
参数
-
形参:声明函数时的参数,形式参数
-
实参:调用函数时的参数,实际参数
-
动态参数arguments:函数内部内置的伪数组变量,包含调用函数时传入的所有实参(只存在于函数)
-
剩余参数…:能够使用剩余参数,置于最末函数形参之前,用于获取多余实参。是一个真数组(只存在于函数)
展开运算符也是…可以展开数组,不会修改原数组。
arr = [1, 2, 3] //…arr === 1, 2, 3
arr1 = [4, 5, 6]
求数组最大值Math.max(1, 2, 3) === Math.max(…arr)
合并数组arr2 = […arr, …arr1] //arr2[1, 2, 3, 4, 5, 6]
function getSum() { let sum for (let i = 0; i < arguments.length;i++){ sum += arguments[i] } console.log(sum); } getSum(1, 2); //3 getSum(1, 2, 3); //6 getSum(1, 2, 3, 4); //6 function getSum(a, b, ...arr) { let sum = a + b; for (let i = 0; i < arr.length;i++){ sum += arr[i] } console.log(arr); } getSum('a', 'b', 'c', 'd') //['c', 'd']
-
函数名相同后面覆盖前面
-
实参小于形参会自动补undefined,形参小于实参,多余的实参被忽略(函数内部有一个arguments,里面装着所有实参)。
function getSum(a, b) { console.log(a + b); } function getSum(a, b, c) { console.log(a + b + c); } getSum(1, 2); //NaN getSum(1, 2, 3); //6 getSum(1, 2, 3, 4); //6
2. 作用域
规定了变量能被访问的范围
-
局部作用域
-
函数内部的,函数作用域
-
局部变量,函数里面用let定义的变量
-
块作用域
使用{}包裹的代码称为代码块,代码块内部声明的变量外部将可能无法访问称。var不会产生块作用域
-
-
全局作用域
全局作用域中声明的变量,任何其他作用域都可以被访问。不建议使用window变量、函数中未使用任何关键字声明的变量
-
写在
-
2.1 JS回收机制
全局变量一般不会回收,一般情况下局部变量不用了就会被回收
内存泄漏:程序分配的内存由于某种原因未释放或无法释放
算法说明:
栈:由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里
堆:一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里
-
引用计数法
内存不再使用,一个对象是否由指向它的引用,没有引用了就回收对象
let arr=['123', '1232'] arr = null //null普通数据,收回指向堆的箭头释放 let obj = { name : 'a', age : 18 } let p = obj obj = 1 //释放obj p = null //释放p function(){ let a = {} let b = {} a.a = b b.a = a //相互引用无法释放(缺点) return }
-
标记清除法
将不再使用的对象定义为”无法达到的对象“。能从根部查找到的为使用的,不回收;无法从根部(在JS中就是全局变量)触发触及到的对象被标记为不再使用,稍后进行回收
2.2 闭包
一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
闭包 = 内层函数 + 外层函数变量
(function(){
const a = 1
function(){
console.log(a)
}
})()
//外部使用函数内部变量
function fn(){
const a = 1
function fnn(){
console.log(a)
}
return fnn
}
//fn() === fnn === function fnn(){}
const fun = fn()
fun() //可以防止数据被修改,将变量设置为局部变量。i没有被回收内存泄漏
2.3 变量提升
允许在变量声明之前被访问(仅存在于var声明变量,先将变量提升不提升赋值)
var name
console.log(name)
name = 'abc'
//函数提升
fn()
function fn(){}
//报错,必须先声明赋值后调用
fn()
var fn = function (){}
var fn //fn未赋值,报错。类型未确定
fn()
function (){}
3. 匿名函数
-
函数表达式,必须先声明赋值后调用。具名函数可以写到任何位置
let fn = function(){ console.log('abc'); } fn();
-
立即执行函数,后面需要加;
(function(){ console.log(123); })(); //类似于name(); (function(){}) == name
4. 箭头函数
更简短的函数写法并且不绑定this,箭头函数语法比函数表达式更简洁
const fn = (x) => {
console.log(x)
}
fn(123)
//只有一个形参可以省略小括号
const fn = x => {
console.log(x)
}
fn(123)
//只有一行代码可以省略大括号
const fn = x => console.log(x)
fn(123)
//只有一行代码可以省略return
const fn = x => x + x
console.log(1) //2
//可以直接返回一个对象
const fn = (name) => ({name : name})
fn('123')
-
箭头函数参数没有arguments,只有…arr
-
箭头函数的this指向上一层作用域
const obj = { name: 'ab', fn: function (){ console.log(this) //obj } } const obj = { name: 'ab', fn: () => { console.log(this) //window,箭头函数没有this } }
5. 构造函数
一种特殊的函数,主要用来初始化对象。构造函数没有return,直接返回一个对象
在js底层中将简单数据类型包装成了引用数据类型
function Pig(name, age) {
this.name = name
this.age = age
}
let p = new Pig('a', 1)
let i = new Pig('b', 2)
- 实例成员:实例对象的属性和方法为实例成员,同一个构造函数创建的实例对象彼此独立互不相同,p和i相互不影响
- 静态成员:构造函数的属性和方法被称为静态成员,静态成员的this指向构造函数
静态方法
只有构造函数Object可以调用
-
Object.keys 获取对象中所有属性,返回一个数组
const obj = {name:'a', age:12} const k = Object.keys(obj) //['name', 'age'] const v = Object.values(obj) //['a', 12]
-
Object.assign 用于对象拷贝,用于添加属性
const obj = {name:'a', age:12} const o = {} Object.assign(o, obj)
七、对象
一种数据类型object,一种无序的数据集合。
1. 声明方式
let 对象名 = {}
let 对象名 = new Object()
对象名.name = 'a'
let 对象名 = new Object({name : 'a'})
Object对象提供了一种创建自定义对象的简单方式,不需要程序员再次定义构造函数。由于在程序运行时可以为JavaScript对象添加属性,因此使用Object对象很容易创建出自定义对象。
null类似于let obj = {}
使用方法
let 对象名 = {
属性名 : 属性值,
方法名 : 函数
}
let obj = {
name : 'abc',
age : 18,
sex : '男'
}
2. 对象属性的操作
-
增(对象名.属性名 = 属性值),原来对象中有时为改,没有为增
-
删 (delete 对象名.属性名)
-
查(对象名.属性名 | 对象名[‘属性名’])
-
改(对象名.属性名 = 新值)
let obj = { name : 'abc', age : 18, sex : '男' } console.log(obj); console.log(obj.age); obj.age = 20; console.log(obj.age); obj.password = 123; console.log(obj); delete obj.password; console.log(obj);
3. 对象方法
let obj = {
//方法
song : function(){
console.log(12);
}
}
obj.song() //方法调用 对象名.方法名
-
遍历对象
let obj = { name : 'abc', age : 18, sex : '男' } for (let key in obj){ //let key为string类型 console.log(key); console.log(obj.key); //obj.'key' console.log(obj[key]); } 输出 name undefined abc age undefined 18 sex undefined 男
-
遍历数组
let arr = [{name : 1, age : 2}, {name : 3, age : 4}, {name : 5, age : 6}] for (let index = 0; index < arr.length; index++) { console.log(arr[index]); console.log(arr[index].age); } 输出 {name: 1, age: 2} 2 {name: 3, age: 4} 4 {name: 5, age: 6} 6
-
表格
<style> table{ width: 300px; text-align: center; border-collapse: collapse; /* border-spacing: 0px; */ } table tr:first-child{ background-color: #ddd; } table tr td{ border: 1px solid #ccc; } caption{ font-size: 10px; } </style> <table> <tr> <th>名字</th> <th>性别</th> <th>年龄</th> </tr> <script> let arr = [ {name : 'a', sex : 'm', age : 18}, {name : 'b', sex : 'w', age : 24}, {name : 'c', sex : 'm', age : 14}, {name : 'd', sex : 'm', age : 37}, {name : 'e', sex : 'w', age : 20} ] for (let i = 0; i < arr.length; i++){ document.write(` <tr> <td>${i + 1}</td> <td>${arr[i].name}</td> <td>${arr[i].sex}</td> <td>${arr[i].age}</td> </tr> `) } </script> </table>
4. 内置对象
JavaScript内部可以调用的对象
Math
-
Math方法
console.log(Math.PI); console.log(Math.LN2); console.log(Math.ceil(12.3)); console.log(Math.ceil(12.8)); 输出 3.141592653589793 0.6931471805599453 13 13
-
Math.random() 左闭右开[0, 1)
-
Math.floor(Math.random() * (10 + 1)) 取到0到10之间的整数
-
数组里面随机取值
let arr = ['a', 'b', 'c', 'd'] let random = Math.floor(Math.random() * arr.length) console.log(arr[random]);
-
Date对象
通过创建Date对象,可以获取计算机中的时间。由于脚本总是在客户端中运行,而不是服务器,所以获取的是本地计算机的时间
方法 | 功能 |
---|---|
getDate | 返回一个月中的某一天(1~31) |
getDay | 返回一周中的某一天(0~6),0为周日,1为周一,以此类推 |
getFullYear | 以四位数返回年份 |
getHours | 返回小时(0~23) |
getMilliseconds | 返回毫秒 |
getMinutes | 返回分钟(0~59) |
getMonth | 返回月份(0~11),0为一月,1为二月,以此类推 |
getSeconds | 返回秒数(0~59) |
getTime | 返回1970年1月1日至今的毫秒数(时间戳,1s = 1000ms)。获取的为时间戳 |
+new Date() | 将new Date()转换为数字型,可以获取当前的时间戳或某个时间点的时间戳,+new Date(‘2024-12-21 15:30:56’) |
Date.now() | 只能获取当前的时间戳 |
** Image对象**
在网页内使用图片,只需要使用标签,然后在src属性中设置图片的绝对路径或者相对路径即可。如果需要在网页里实现动画或者图像效果,那么就得在JavaScript里使用Image对象。创建Image对象picObj,并设置src属性,指定的图片就可以从服务器中下载并放入本地计算机的内存中。
with语句
在一段连续的程序代码中,如果多次使用到某个对象的属性或方法,那么只需要在with关键字后的小括号中写出这个对象的名称,然后就可以在其后大括号中的执行语句里直接引用该对象的属性名或方法名,不必再在每个属性和方法名前都加上对象实例名和点(.)。
function showTime(){
var today = new Date();
with(today){
var year=getFullYear();//获得年
var month=getMonth()+1;//获得月
var day=getDate();//获得日
var hh =getHours(); //获得小时、分钟和秒
var mm =getMinutes();//获得分钟
var ss =getSeconds();//获得秒
}
document.getElementById("myclock").innerHTML=year+"年"+month+"月"+day+"日"+hh+":"+mm+":"+ss;
}
window.setInterval("showTime()",1000);//每隔1秒取一次当前的时间
window.onload=showTime;
5. 对象解构
将对象属性和方法快速批量赋值给一系列变量的简洁语法
const {name, age} = {name:'a', age=18}
//等价于const name = obj.name,属性名需要与对象名一致
//name = a, age = 18
const {name} = {name:'a', age=18}
//较多属性时,可以只取一个属性,name = 'a'
const {name: uname, age} = {name:'a', age=18}
//改名,旧变量名 : 新变量名
const obj = [{name:'a', age:18}]
const [{name, age}] = obj
const obj = {name:'a', age=18, friends:{a:'a', b:'b'}}
const {name, age, friends{a, b}} = obj //a = 'a', b = 'b'
Web APIs
作用:使用js去操作HTML和浏览器
一、DOM(文档对象模型)
用来呈现以及与任意HTML或XML文档交互的API,开发网页内容特效和实现用户交互。
1. 基础原理
1.1 DOM树
将HTML文档以树状结构直观表现出来,文档直观的体现了标签与标签之间的关系。
DOM树里面每一个内容都称为节点,比如元素节点(div标签)、属性节点(class属性)、文本节点等
-
查找节点
-
父节点查找:子元素.parentNode,返回最近一级父节点找不到返回null
-
子节点查找:
- 父元素.childNodes,获取所有子节点(包括文本节点、注释节点)
- 父元素.children,仅获得所有元素节点,返回一个伪数组
-
兄弟节点查找:
- 下一个兄弟节点nextElementSibling属性
- 上一个兄弟节点previousElementSibling属性
<div class="yy"> <div class="dad"> <div class="baby">x</div> </div> </div> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <script> const baby = document.querySelector('.baby') console.log(baby); //返回dom对象 console.log(baby.parentNode); //返回dom对象 const dad = document.querySelector('.dad') console.log(dad); //返回dom对象 console.log(dad.children); //返回dom对象 const li2 = document.querySelector('ul li:nth-child(2)') console.log(li2.nextElementSibling); console.log(li2.previousElementSibling); </script>
-
-
增加节点
- 创建一个新的网页元素,再添加到网页。先创建节点然后插入节点(想要在界面看到还需要插到某个父元素中)document.creatElement(‘标签名’)
- 追加节点
- 插到父元素最后一个子节点,父元素.appendChild(要插入的元素)
- 插到父元素某个子元素的前面,父元素.insertBefore(要插入的元素, 放到哪里)
- 克隆节点
- 克隆一个跟原标签一样的元素,元素.cloneNode(布尔值)(true表示包含后代节点一起克隆,false克隆不包含后代节点,默认为false)
<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <script> const ul = document.querySelector('ul') const li = document.createElement('li') li.innerHTML = 'abc' //同时出现两种插入时会被后面的覆盖 ul.insertBefore(li, ul.children[0]) ul.appendChild(li) </script>
-
删除节点
通过父元素删除,父元素.removeChild(要删除的元素)
ul.removeChild(ul.children[2])
1.2 DOM对象
浏览器根据HTML标签生成的JS对象
所有标签属性都可以在对象上面找到,修改这个对象的属性会自动映射到标签身上
<div>123</div>
<script>
const div = document.querySelector('div') //div对象
//打印对象
console.log(div)
</script>
//DOM核心思想,将网页内容当对象来处理
1.3 document对象操作
- 提供的方法和属性都是用来访问和操作网页内容的
- 网页所有内容都在document里面
2. 获取DOM对象
-
根据CSS获取
-
document.querySelector | .querySelectorAll(‘css选择器’),css怎么写css选择器怎么写
- 一个(document.querySelector(‘css选择器’))返回匹配的第一个元素,一个HTMLElement对象,没有匹配到返回null,可以直接操作
- 多个(document.querySelectorAll(‘css选择器’))NodeList对象集合,得到伪数组,需要遍历得到每一个元素
.box{ width : 20px; height : 20px; } <div class="box">123</div> <div class="box">abc</div> <script> const box = document.querySelector('.box'); const box1 = document.querySelectorAll('.box'); box.style.width = '300px'; //可以修改盒子的宽度 console.log(box); console.log(box1); </script>
-
获取其他方法
- document.getElementById(‘nav’) 根据id获取一个元素
- document.getElementByTagName(‘div’) 根据标签获取一类元素 获取页面所有div
- document.getElementByClassName(‘w’) 根据类名获取元素 获取页面所有类名为w的
3. 操作元素内容
-
对象.innerText,修改文字内容,不解析标签
<script> const box = document.querySelector('.box'); box.innerText = 111; console.log(box.innerText); //111 </script> <h1>adx</h1> <script> const arr = ['a', 'b', 'c', 'd', 'f']; const n = Math.floor(Math.random() * arr.length); const h = document.querySelector('h1'); h.innerText = arr[n]; arr.splice(n, 1); //如果是抽奖可以将已选择的删除
-
对象.innerHTML,解析标签
box.innerHTML = <b>111</b>
4. 操作元素属性
4.1 常用属性修改
<img src="./img/1648191596_146165.jpg" alt="">
<script>
const img = document.querySelector('img');
img.src = './img/f74bbd07485b29eeeb61d5fadf3113e6.jpg';
img.title = '文文'
</script>
4.2 控制样式属性
-
通过style属性改写,生成的行内样式表,权重较高
.box{ width : 20px; height : 20px; background-color : red; } <div class="box">123</div> <script> const box = document.querySelector('.box'); 吧 box.style.width = '300px'; //可以修改盒子的宽度 box.style.backgroundColor = 'pink'; //多组单词采用小驼峰命名法 //body只有一个,可以直接看作一个对象 document.body.style.backgroundImage ='./img/f74bbd07485b29eeeb61d5fadf3113e6.jpg'; </script>
-
className(元素.className = ‘active’),class是关键字所有用className代替
.box{ height: 200px; width: 200px; background-color: pink; } .nav{ height: 300px; background-color: antiquewhite; } <div class="box"></div> <script> const div = document.querySelector('div') div.className = 'nav' //直接覆盖以前的类 div.className = 'nav box' //同时两个类,有相同属性时取后一个类的属性值 </script>
-
classList,追加删除类名(解决完全覆盖类的情况)
const div = document.querySelector('div')
div.classList.add('box nav') //添加类 add()
div.classList.remove('box') //删除类 remove()
div.classList.toggle('active') //切换,有该类删除,没有添加 toggle()
<div class="box">
<img src="./img/8a857fa4d50e9e2dc6b7b2610deeb11f9fa70bcf.jpg" alt="">
<div class="boxm">
<ul>
<li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</div>
<script>
const arr = [
{src : './img/8a857fa4d50e9e2dc6b7b2610deeb11f9fa70bcf.jpg'},
{src : './img/5792d049afcabc7e275f42176fd5ad7c715d3c2ef56c5-A5BZ2U_fw658.jpg'},
{src : './img/01098560aeed8611013f47200c2129.jpg'},
{src : './img/1648191596_146165.jpg'},
{src : './img/f74bbd07485b29eeeb61d5fadf3113e6.jpg'},
{src : './img/src=http___c-ssl.duitang.com_uploads_item_202002_29_20200229195339_cmE5z.jpeg&refer=http___c-ssl.duitang.webp'},
{src : './img/v2-5d9f09abc2ea5b0a12eeb1a3ec612cc9_r.jpg'},
{src : './img/v2-a595f653f22a013d940196380d414c92_r.jpg'},
{src : './img/v2-ca93ba37d8d8cea364bc5409cbb5ff18_1440w.jpg'},
]
let n = 0
function fn() {
// let n = parseInt(Math.random()*9);
const img = document.querySelector('img')
img.src = arr[n].src
document.querySelector('.active').classList.remove('active')
const li = document.querySelector(`li:nth-child(${n+ 1})`).classList.add('active')
++n;
if (n == 9) n = 0;
}
setInterval(fn, 2000)
</script>
4.3 操作表单元素属性
获取:DOM对象.属性名,设置:DOM对象.属性名 = 新值
-
表单.value = ‘username’ 表单.type = ‘password’
用户名:<input type="text" value="电脑"> <script> const uname = document.querySelector('input') console.log(uname.value); //不用innerHTML,innerHTML只用于普通元素 uname.type = 'password'; //可以直接修改 console.log(uname.type); </script> //输出 //电脑 //password
-
选中checked,返回true/false
-
禁用disabled,返回true/false
-
下拉框seleced
用户名:<input type="checkbox" value="电脑"> <button>按钮</button> <script> const ipt = document.querySelector('input') console.log(ipt.checked); //false ipt.checked = true const but = document.querySelector('button') console.log(but.disabled); but.disabled = true </script>
4.4 自定义属性
html5推出专门的data-自定义属性,在标签上加data-开头,在DOM对象上以dataset对象方式获取
<div data-id="1" data-a="b">1</div>
<div data-id="2">2</div>
<div data-id="3">3</div>
<div data-id="4">4</div>
<div data-id="5">5</div>
<script>
const one = document.querySelector('div')
console.log(one.dataset); //DOMStringMap {id: '1'}
console.log(one.dataset.id); //1
console.log(one.dataset.a); //b
console.log(one.dataset.id='3'); //3
</script>
5. 定时器-间歇函数
能够重复执行代码
5.1 开启定时器
setInterval(函数/函数名, 间隔时间),每间隔一段时间重复执行,单位毫秒ms
let n = setInterval(function(){
console.log('一秒执行一次');
}, 1000)
console.log(n); //1
let a = function fn(){
console.log('一秒执行一次');
}
setInterval(fn, 1000)
console.log(a); //2
clearInterval(a); //关闭第二个定时器
a = setInterval(fn, 1000) //重新打开计时器
5.2 关闭定时器
clearInterval(n),可以是数字也可以是变量,比如上列代码的n
<button class="btn" disabled>还需阅读6秒</button>
<script>
const btn = document.querySelector('button')
let t = 6
function fn(){
if (t >= 1){
console.log(btn.innerHTML = `还需阅读${t--}秒`);
} else {
console.log(btn.innerHTML = '已完成阅读');
clearInterval(a)
btn.disabled = false
}
}
const a = setInterval(fn, 1000)
</script>
<!--
还需阅读6秒
还需阅读5秒
还需阅读4秒
还需阅读3秒
还需阅读2秒
还需阅读1秒
已完成阅读 -->
6. 事件
6.1 事件监听
元素对象.addEventListener(‘事件类型’, 要执行函数)
-
事件源:哪个dom元素被事件触发了,要获取dom元素
-
事件类型:用什么方式触发,如单击click、鼠标经过mouseover等
-
事件调用的函数:要做什么事
btn.addEventListener('click', function () { alert('下午好') })
DOM L0 事件监听:事件源.on事件 = function(){},只能做冒泡,不能捕获
DOM L2 事件监听:事件源.addEventListener(‘事件类型’, 要执行函数),能做冒泡,也能捕获
on方式会被覆盖,addEventListener方式可以绑定多次,拥有事件更多特性
6.2 事件类型
鼠标事件 | 焦点事件 | 键盘事件 | 文本事件 |
---|---|---|---|
click鼠标点击 | focus获取焦点 | keydown键盘按下触发 | input用户输入事件 |
mouseenter鼠标经过 | blur失去焦点 | keyup键盘抬起触发 | |
mouseleave鼠标离开 |
let n = 0
let timerid = setInterval(function () {
next.click()
}, 2000);
const next = document.querySelector('.next')
next.addEventListener('click',function fn() {
// let n = parseInt(Math.random()*9);
++n;
if (n == 9) n = 0;
const img = document.querySelector('img')
img.src = arr[n].src
document.querySelector('.active').classList.remove('active')
document.querySelector(`li:nth-child(${n+ 1})`).classList.add('active')
})
const pre = document.querySelector('.pre')
pre.addEventListener('click',function fn() {
--n;
if (n < 0) n = 8;
const img = document.querySelector('img')
img.src = arr[n].src
document.querySelector('.active').classList.remove('active')
document.querySelector(`li:nth-child(${n+ 1})`).classList.add('active')
})
const box = document.querySelector('.box')
box.addEventListener('mouseenter', function () {
clearInterval(timerid)
})
box.addEventListener('mouseleave', function () {
timerid = setInterval(function () {
next.click()
}, 2000);
})
const input = document.querySelector('input')
input.addEventListener('focus', function(){
document.querySelector('.list').style.display = 'block'
})
input.addEventListener('blur', function(){
document.querySelector('.list').style.display = 'none'
}) //搜索框下出现列表
mouseover、mouseout和mouseenter、mouseleave的区别在前者会有冒泡,比如存在冒泡的情况,当子盒子有鼠标经过时,父盒子会跟着触发事件
6.3 事件对象
有事件触发时的相关信息的对象,事件绑定的回调函数的第一个参数就是事件对象
input.addEventListener('blur', function(e){}) //e是事件对象
常用属性
-
type 获取当前事件类型
-
clientX / clientY 获取光标相对于浏览器可见窗口左上角位置
-
offsetX / offsetY 获取光标相当于当前DOM元素左上角位置
-
key 用户按下的建盘建的值
input.addEventListener('keyup', function(e){ if (e.key === 'Enter'){ console.log('按下了回车键'); } })
6.4 环境对象
函数内部特殊的变量this,它表示当前函数运行时所处的环境。每个函数里面都有this,普通函数里面this指向window(谁调用指向谁)
const box = document.querySelector('.box')
box.addEventListener('mouseenter', function () {
box.style.height = '200px'
this.style.height = '200px'
//含有相同,此时this指向box
})
6.5 回调函数
函数A作为参数来传递给另外一个函数的时候
let timerid = setInterval(function () {
next.click()
}, 2000);
6.6 事件流
DOM.addEventener(事件类型, 事件处理函数, 是否使用捕获机制)
同一事件
- 事件捕获,第三个参数为true,很少用
- 事件冒泡,传入为false(默认),当用L0事件监听时没有捕获,只有冒泡。
阻止事件流:事件对象.stopPropagation()
document.addEventListener('click', function (){
alert('fa')
}, true)
fa.addEventListener('click', function (){
alert('fa')
}, true)
son.addEventListener('click', function (e){
alert('son')
//e.stopPropagation() 阻止冒泡或捕获,当出现该弹窗时,后面的弹窗将不再出现
//e.preventDefault() 阻止默认行为
}, true)
//事件类型一样,捕获从大到小、从父到子(比如湖北 武汉 江夏区)
//没有第三个参数或为false时,冒泡从小到大、子到父,弹窗先出现son
btn.onclick = function(){
alter('a')
}
son.onclick = null //事件解绑
btn.addEventListener('click', fn)
btn.removeEventListener('click', fn) //事件解绑(匿名函数无法解绑)
利用冒泡法可以实现事件委托,将事件写在父级上,通过子级来触发父级事件
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
const ul = document.querySelector('ul')
ul.addEventListener('click', fuction(e){
console.log(e.target) //点击的对象
//可以通过e.target.tagName === 'li'来选定只有对象名字li才发生
e.target.style.color = 'red'
//点击的对象改变颜色
})
</script>
6.7 页面事件
页面加载事件load,等待加载外部资源,加载完毕触发
window.addEventListener('load', fn)
//等待图片加载完成再执行里面的代码
img.addEventListener('load', fn)
//无需等待样式表、图像等完全加载
document.addEventListener('DOMContentLoaded', fn)
页面滚动事件scroll
获取位置scrollLeft和scrollTop,获取被卷进去看不到的距离大小,这两个值都可读
window.addEventlistener('scroll', function(){
const n = document.documentElement.scrollTop
if (n >= 100){
div.style.display = 'block'
} else {
div.style.display = 'none'
}
//页面向上滚动100px出现,否则隐藏
})
document.addEventlistener('scroll', function(){
console.log(this.scrollTop)
})
const div = document.querySelector('div')
div.addEventListener('scroll', fn)
页面尺寸事件resize
//浏览器在窗口尺寸改变的时候触发
window.addEventListener('resize', function(){
//检测屏幕可见宽度(不含边框、margin、滚动条等)
console.log(document.documentElement.clientWidth)
//检测屏幕可见高度(不含边框、margin、滚动条等)
console.log(document.documentElement.clientHeight)
})
6.8 M端事件
触屏事件touch对象代表一个触摸点(可以是手指,可以是触控笔),只在移动端出现。
触屏touch事件 | 说明 |
---|---|
touchstart | 手指触摸到一个DOM元素时触发 |
touchmove | 手指在一个DOM元素上滑动时触发 |
touchend | 手指从一个DOM元素上移开时触发 |
综合案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
padding: 0;
margin: 0;
}
h1{
text-align: center;
}
.nav{
width: 500px;
margin-bottom: 50px;
}
input{
width: 50px;
}
select{
width: 50px;
}
.tab{
width: 500px;
}
th, td{
width: 75px;
border: 0.5px solid blue;
}
th{
background-color: cornflowerblue;
}
td{
text-align: center;
}
table{
border-collapse: collapse;
}
</style>
</head>
<body>
<div class="nav">
<h1>新增成员</h1>
<form action="" class="info">
姓名:<input type="text" name="name" class = 'name'>
年龄:<input type="text" name="age" class="age">
性别:<select name="sex" id="sex" class="sex">
<option value="男">男</option>
<option value="女">女</option>
</select>
薪资:<input type="text" name="money" class="money">
<button class="add">录入</button>
</form>
</div>
<div class="tab">
<h1>就业榜</h1>
<table>
<thead>
<th>序号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>薪资</th>
<th>操作</th>
</thead>
<tbody>
</tbody>
</table>
</div>
<script>
const name = document.querySelector('.name')
const age = document.querySelector('.age')
const sex = document.querySelector('.sex')
const money = document.querySelector('.money')
const info = document.querySelector('.info')
const tbody = document.querySelector('tbody')
let arr = []
info.addEventListener('submit', function(e){
e.preventDefault()
const obj = {
id : arr.length + 1,
name : name.value,
age : age.value,
sex : sex.value,
money : money.value
}
arr.push(obj)
this.reset()
add()
})
function add(){
tbody.innerHTML=``
for (let i = 0; i < arr.length; i++) {
const tr = document.createElement('tr')
tr.innerHTML = `
<td>${i + 1}</td>
<td>${arr[i].name}</td>
<td>${arr[i].age}</td>
<td>${arr[i].sex}</td>
<td>${arr[i].money}</td>
<td>
<a href="javascript:" data-id=${i}>删除</a>
</td>
`
tbody.appendChild(tr)
}
}
tbody.addEventListener('click', function (e) {
if (e.target.tagName === "A") {
arr.splice(e.target.dataset.id, 1)
}
add()
})
</script>
</body>
</html>
二、BOM(浏览器对象模型)
window包含navigator、location、document、history、screen
所有通过var定义的全局变量都会变成window对象属性和方法
1. 定时器-延时函数
setTimeout(回调函数, 等待的毫秒数),仅执行一次,将一段代码延迟执行
let timer = setTimeout(function(){}, 2000)
clearTimeout(timer) //清除延时函数
2. JS执行机制(event loop)
JavaScript单线程,同一时间只能做一件事。为了解决这个问题HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程。于是JS出现了同步和异步。先执行执行栈中的同步任务,再到任务队列中取出任务执行,重复获取任务,执行任务再获取任务,执行任务形成循环。
-
同步任务都在主线程上执行,形成一个执行线
-
异步任务通过回调函数实现(放置任务队列中)
- 普通事件click、resize
- 资源加载load、error
- 定时器setInterval、setTimeout
3. window对象
3.1 navigator
数据类型为对象,记录浏览器自生相关信息
通过userAgent检测浏览器版本及平台
//检测userAgent
(function(){
const userAgent = navigator.userAgent
const android = userAgent.match(/(Android);?[¥s¥/]+[¥d.]?/)
const iphone = userAgent.match(/(iPhone¥sOS)¥s[¥d_]+)/)
if (android || iphone){
location.href = 'http://baidu.com'
}
})()
3.2 location
location的数据类型是对象,它拆分保存了URL地址的各个组成部分
常见属性和方法
-
href属性获取完整的URL地址,对其赋值时用于地址跳转
//可以获取当前文件URL地址 console.log(location.href) //通过js方法跳转到目的地址 location.href = 'http://www.itcast.cn'
-
search属性获取地址中携带的参数,符号?后面部分
登录注册时获取地址数据为 ‘?username=123&pwd=123’
-
hash属性获取地址中的哈希值,符号#后面部分。用于不刷新网页,显示不同页面
<a href="#/my"></a> <a href="#/content"></a>
-
reload方法用来刷新当前页面,传入参数true表示强制刷新
<button> 点击刷新 </button> <script> let btn = document.queryElement('button') btn.addEventListener('click', function(){ location.reload(true) }) </script>
3.3 history
主要管理历史记录,该对象与浏览器地址栏的操作相对应
常用对象方法
-
back(),后退功能
-
forward(),前进功能
-
go(参数),前进后退功能,参数为1前进一个页面,为-1后退一个页面
history.back() history.forward() history.go(-1) //和history.back()一样
4. 本地存储
数据存储用户浏览器中,设置、读取方便、刷新页面数据不丢失。容量较大sessionStorage和localStorage约5M左右
4.1 localStorage
本地存储分类localStorage,将数据永久存储在本地,除法手动删除,否则关闭页面也会存在。可以多个窗口共享,以键值对的形式存储使用。本地存储只能存储字符串类型
//存储数据
localStorage.setItem('name', 'pink')
//获取方法,输出在控制台
console.log(localStorage.getItem('name'));
//删除本地存储
localStorage.removeItem('name')
//更改数据
localStorage.setItem('name', 'red')
4.2 sessionStorage
生命周期为关闭浏览器,其他与localStorage基本相同
4.3 复杂数据类型
存储复杂数据类型,直接像字符串形式存储无法直接使用。需要先将复杂数据类型转换成JSON字符串,存储到本地
//JSON.stringify(复杂数据类型),
//1.将复杂数据类型转换成字符串 JSON.stringify(obj)
//2.将字符串转换成复杂数据类型 JSON.parse(localStorage.getItem('obj'))
const obj = {
name : 'pink',
age : 18,
sex : 'woman'
}
localStorage.setItem('obj', JSON.stringify(obj))
//存储格式:{"name":"pink","age":18,"sex":"woman"}
//JSON对象 属性和值都有双引号
console.log(JSON.parse(localStorage.getItem('obj')))
三、正则表达式
匹配字符串中字符组合的模式
const 变量名 = /表达式/
/表达式/i //匹配的字母不区分大小写
/表达式/g //匹配所有满足正则表达式的结果
-
test()方法检查正则表达式是否和指定字符串匹配
const str = '小喆喆大憨憨' const reg = /喆喆/ console.log(reg.test(str)); //true
-
exec()方法在一个指定字符串中执行一个搜索匹配
console.log(reg.exec(str)); //['喆喆', index: 1, input: '小喆喆大憨憨', groups: undefined]
找得到返回一个数组,没有找到返回null
元字符
具有特殊含义的字符,极大的提高了灵活性和强大的匹配功能。比如26个英文字母可以写成[a-z]
1. 边界符
提示字符所处位置,主要有:^ 表示匹配行首的文本;$表示匹配行尾的文本
const str = '小喆喆大憨憨'
const reg1 = /^喆喆/
console.log(reg1.test(str)); //false
const reg2 = /喆喆$/
console.log(reg2.test(str)); //false
console.log(/^喆$/.test('喆喆')); //false
console.log(/^喆$/.test('喆')); //true,需要精确匹配
2. 量词
设定某个模式出现的次数。* 重复零次或更多次;+ 重复一次或更多次;? 重复零次或一次;{n} 重复n次;{n,} 重复n次或更多次;{n,m} 重复n到m次(逗号左右不要出现空格)
console.log(/^喆*$/.test('喆喆')); //true
console.log(/^喆*$/.test('')); //true
console.log(/^喆+$/.test('喆喆')); //true
console.log(/^喆+$/.test('')); //false
console.log(/^喆?$/.test('喆喆')); //false
console.log(/^喆?$/.test('')); //true
console.log(/^喆{2}$/.test('喆喆喆')); //false
console.log(/^喆{2}$/.test('喆喆')); //true
console.log(/^喆{2}$/.test('')); //false
console.log(/^喆{2,}$/.test('喆喆喆喆')); //true
console.log(/^喆{2,}$/.test('喆喆')); //true
console.log(/^喆{2,}$/.test('')); //false
console.log(/^喆{2,3}$/.test('喆喆喆')); //true
console.log(/^喆{2,3}$/.test('喆喆喆喆')); //false
console.log(/^喆{2,3}$/.test('')); //false
3. 字符类
[]匹配字符合集
//后面的字符串只要包含reg中任意一个字符都返回true
console.log(/[abc]/.test('apple')); //true
console.log(/[abc]/.test('ban')); //true
console.log(/[abc]/.test('can')); //true
console.log(/[abc]/.test('div')); //false
console.log(/^[a-xA-Z0-9]$/.test('a')); //true
console.log(/^[1-9][0-9]$/.test('02'));
//false,第一个数字范围1-9,第二个数字范围0-9
console.log(/^[abc]$/.test('b')); //true
console.log(/^[abc]$/.test('c')); //true
console.log(/^[abc]$/.test('ab')); //false
console.log(/^[abc]{2}$/.test('ab')); //true
console.log(/[^abc]{2}$/.test('ab'));
//false,^ 在中括号里面取反,除了……都可以
\d = [0-9] | \w = [a-zA-Z0-9] | \s = [\t\r\n\v\f],大写字母取反
4. 修饰符
字符串.replace(/正则表达式/, ‘替换文本’)
面向对象
面向对象是将项目分解成一个个对象,然后对象之间分工与合作。面向对象编程oop灵活、代码可复用、容易维护等,适合大型项目
面向对象特征:封装性、继承性、多态性
JS面向对象可以通过构造函数来实现封装
- 之前的构造函数存在浪费内存的问题,主要是复杂数据类型时
一、原型
1. 原型对象
解决构造函数内存浪费的问题,利用原型对象实现方法共享。JavaScript规定每一个构造函数都有一个prototype属性,指向另外一个对象。构造函数和原型对象中的this都指向实例化对象
function Pig(name, age) {
this.name = name
this.age = age
//this.say = () => console.log('a')
}
Pig.prototype.say = () => console.log('a')
let p = new Pig('a', 1)
let i = new Pig('b', 2)
Array.prototype.sum = function(){
return this.reduce((prev, item) => prev + item,0)
}
Array.prototype.max = function(){
return Math.max(...this)
}
每个原型对象都有个constructor属性,该属性指向该原型对象的构造函数
Pig.prototype = {
//重新指回创造这个原型对象的构造函数
constructor : Pig,
say:function(){},
}
2. 对象原型
对象都有一个属性__proto__
指向构造函数的prototype原型对象,用来表明当前实例对象指向哪个原型对象prototype(对象原型指向原型对象)。__proto__
对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数
3. 原型继承
JavaScript中大多是借助原型对象实现继承的特性
function Star(){
this.age = 18
this.say = function(){}
}
const zw = new Star()
const zz = new Star() //zw !== zz 每个实例对象都不一样
原型链:原型的链状结构关系
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上
function Person(){}
const zw = new Person()
zw instanceof Person //true
zw instanceof Object //true
zw instanceof Array //false
[1, 2, 3] instanceof Array //true
Array instanceof Object //true
二、拷贝
在开发中直接赋值一个对象,修改时会将原对象一起改动。所以出现了深浅拷贝,深浅拷贝都只针对引用类型
1. 浅拷贝
拷贝对象之后,里面的属性值是简单的数据类型直接拷贝值。属性是引用数据类型拷贝的是地址
常见方法:
-
拷贝对象:Object.assgin() 或 展开运算符 {…obj}
const obj = { name : 'a', age : 12 } const o = {...obj} const j = {} Object.assgin(j, obj) o.name = 'b' j.name = 'c' console.log(obj.name) //a console.log(o.name) //b console.log(j.name) //c
-
拷贝数组:Array.prototype.concat() 或 […arr]
2. 深拷贝
拷贝的是对象,不是地址
常见方法:
-
通过递归实现深拷贝
const obj = { name : 'a', age : 12, arr : ['a', 'b'], ob : { day : 'one' } } const o = {} function deepCopy(newObj, oldObj) { for (let k in oldObj) { if (oldObj[k] instanceof Array) { let newObj[k] = []; deepCopy(newObj[k], oldObj[k]); } else { if (oldObj[k] instanceof Object) { let newObj[k] = {}; deepCopy(newObj[k], oldObj[k]); } else { newObj[k] = oldObj[k]; } } } } deepCopy(o, obj);
-
cloneDeep
const obj = { name : 'a', age : 12, arr : ['a', 'b'], ob : { day : 'one' } } const o = _.cloneDeep(obj) //引入./lodash.min.js或直接node导入
-
通过JSON.stringify()实现
const obj = { name : 'a', age : 12, arr : ['a', 'b'], ob : { day : 'one' } } const o = JSON.parse(JSON.stringify(obj)) //将对象转换为JSON字符串
三、异常处理
预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继承
1. throw抛异常
function counter(x, y) {
if (!x || !y) {
//throw '参数不能为空!'
throw new Error('参数不能为空!')
}
return x + y
}
counter()
throw抛出异常信息,程序会终止执行,throw后面跟的是错误信息。Error对象配合throw使用,能够设置更详细的错误信息。
2. try/catch捕获错误信息
function fn(){
try {
//可能发送错误的代码 要写到try
const p = document.querySelector('.p')
p.style.color = 'red'
} catch (err) {
//拦截错误,提示浏览器提供的错误提醒,但是不中断程序的执行
console.log(err.message)
throw new Error('error')
//需要加return 中断程序
//return
} finally {
//不管程序对不对,一定会执行的代码
alert('a')
}
}
fn()
3. debugger
debugger //向下调试
function deepCopy(newObj, oldObj) {
for (let k in oldObj) {
if (oldObj[k] instanceof Array) {
let newObj[k] = [];
deepCopy(newObj[k], oldObj[k]);
} else {
if (oldObj[k] instanceof Object) {
let newObj[k] = {};
deepCopy(newObj[k], oldObj[k]);
} else {
newObj[k] = oldObj[k];
}
}
}
}
四、处理this
1. this指向
-
普通函数:普通函数的调用方法决定this的值,没有明确调用者时this值为window,严格模式下没有调用者时this的值为undefined
-
箭头函数:默认绑定外层this的值(箭头函数中this的值和外层的this是一样的),箭头函数中的this引用就是最近作用域中的this。向外层作用域中,一层一层查找this,直到有this的定义
console.log(this) //此处为window //箭头函数 const sayHi = function() => { //该箭头函数中的this为函数声明环境中this一致 console.log(this) }
2. 改变this
-
call()
使用call方法调用函数,同时指定被调用函数中的this的值
fun.call(thisArg, arg1, arg2, ...)
-
apply()
使用apply方法调用函数,同时指定被调用函数中的this的值。后面只能接数组
//fun.apply(thisArg, [argsArray]) const obj ={ a: 'a' } function fn(){ console.log(this) } fn.apply(obj) //指向obj
-
bind()
只改变this指向,不会调用
//fun.bind(thisArg, arg1, arg2, ...) const obj ={ a: 'a' } function fn(){ console.log(this) } const fun = fn.bind(obj) //指向obj fun()
五、防抖
单位时间内,频繁触发事件,只执行最后一次
1. lodash防抖
//_.debounce(func, [wait=0], [options=])
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
//box.addEventListener('mousemove', mouseMove)
//鼠标每移动1像素会触发一次函数
box.addEventListener('mousemove', _.debounce(mouseMove, 500))
2. 自己编写
核心思想:利用setTimeout定时器来实现
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
function debounce(fn, t) {
let timer
return function() {
if (timer) clearTimeout(timer)
timer = setTimeout(function (){
fn()
}, t)
}
}
box.addEventListener('mousemove', debounce(mouseMove, 500))
六、节流
节流throttle:单位时间内,频繁触发事件,只执行一次
1. lodash节流
//_.throttle.(func, [wait=0], [options=])
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
//500毫秒之内,无论触发多少事件,只会执行一次
box.addEventListener('mousemove', _.throttle(mouseMove, 500))
2. 自己编写
核心思想:利用setTimeout定时器来实现
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
function debounce(fn, t) {
let timer = null
return function() {
if (!timer) {
timer = setTimeout(function (){
fn()
timer = null
}, t)
}
}
}
box.addEventListener('mousemove', debounce(mouseMove, 500))
bj[k] = oldObj[k];
}
}
}
}
## 四、处理this
### 1. this指向
1. 普通函数:普通函数的调用方法决定this的值,没有明确调用者时this值为window,严格模式下没有调用者时this的值为undefined
2. 箭头函数:默认绑定外层this的值(箭头函数中this的值和外层的this是一样的),箭头函数中的this引用就是最近作用域中的this。向外层作用域中,一层一层查找this,直到有this的定义
```js
console.log(this) //此处为window
//箭头函数
const sayHi = function() => {
//该箭头函数中的this为函数声明环境中this一致
console.log(this)
}
2. 改变this
-
call()
使用call方法调用函数,同时指定被调用函数中的this的值
fun.call(thisArg, arg1, arg2, ...)
-
apply()
使用apply方法调用函数,同时指定被调用函数中的this的值。后面只能接数组
//fun.apply(thisArg, [argsArray]) const obj ={ a: 'a' } function fn(){ console.log(this) } fn.apply(obj) //指向obj
-
bind()
只改变this指向,不会调用
//fun.bind(thisArg, arg1, arg2, ...) const obj ={ a: 'a' } function fn(){ console.log(this) } const fun = fn.bind(obj) //指向obj fun()
五、防抖
单位时间内,频繁触发事件,只执行最后一次
1. lodash防抖
//_.debounce(func, [wait=0], [options=])
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
//box.addEventListener('mousemove', mouseMove)
//鼠标每移动1像素会触发一次函数
box.addEventListener('mousemove', _.debounce(mouseMove, 500))
2. 自己编写
核心思想:利用setTimeout定时器来实现
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
function debounce(fn, t) {
let timer
return function() {
if (timer) clearTimeout(timer)
timer = setTimeout(function (){
fn()
}, t)
}
}
box.addEventListener('mousemove', debounce(mouseMove, 500))
六、节流
节流throttle:单位时间内,频繁触发事件,只执行一次
1. lodash节流
//_.throttle.(func, [wait=0], [options=])
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
//500毫秒之内,无论触发多少事件,只会执行一次
box.addEventListener('mousemove', _.throttle(mouseMove, 500))
2. 自己编写
核心思想:利用setTimeout定时器来实现
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
function debounce(fn, t) {
let timer = null
return function() {
if (!timer) {
timer = setTimeout(function (){
fn()
timer = null
}, t)
}
}
}
box.addEventListener('mousemove', debounce(mouseMove, 500))