JavaScript进阶笔记
作用域
分类
局部作用域
变量和函数只能在特定的代码区域内被访问的范围。
eg:函数内部和块作用域({}包围的内部)
全局作用域
变量和函数在整个程序中都可以被访问的范围。
eg:script标签
作用域链:从小到大
1、最里面的函数
2、父级函数
3、全局作用域
注:子作用域可以访问父作用域,但是父作用域不能访问子作用域
垃圾回收机制
自动回收:js中内存的分配和回收机制都是自动完成的,内存在不使用的时候会被垃圾回收机制自动回收。
内存生命周期
内存分配:声明变量、函数、对象
内存使用:读写内存,使用变量,函数
内存回收:使用完毕,自动回收不再使用的内存(针对局部变量,全局变量一般不回收)
内存泄漏:程序中分配的内存由于某种原因,程序未释放或者无法释放
算法说明
栈:由操作系统自动分配释放函数参数、局部变量,基本数据类型放到栈里面
堆:一般由程序员分配释放,如果不手动释放,由垃圾回收机制回收,复杂数据类型放到堆里面。
引用计数法
看对象是否有指向它的引用,可没有就回收(如果数据互相指向,则算是有引用的)
标记清除法
从根部(全局对象)出发定时扫描内存中的对象,无法触及到的就会认为不再使用,会回收
闭包
本质:在内层函数使用外层函数定义的的变量
闭包==内层函数+外层函数的变量
外部使用闭包:return 内层函数,然后赋值给另一个外部函数,直接调用这个函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>闭包</title>
</head>
<body>
<script>
// 外部调用闭包
// 父函数
function outer() {
let a = 100;
// 子函数
function fn() {
console.log(a);
}
// 返回子函数(不是值)
return fn;
}
// 将outer返回值(fn函数)赋值给fun 相当于fun是一个函数
const fun = outer();
// 调用fun函数
fun();
// 应用
let i = 0;
// 普通形式:被调用i次 但是i是个全局变量,很容易被篡改
function fn() {
i++;
console.log(i);
}
// 闭包形式
function count() {
let n = 0;
function fn() {
n++;
console.log(n);
}
return fn;
}
// 调用呈现
const out = count();
out();
</script>
</body>
</html>
应用
实现数据私有:统计函数调用次数
风险
容易内存泄漏
变量提升
把变量声明提升到当前作用域的最前面(var),提升声明,不提升赋值,且要出现在相同作用域中。
声明变量必须在所有使用此变量的代码的最前面,不然就算undefined
函数进阶
函数提升
函数在定义之前调用是可执行的(把函数声明提升到当前作用域的最前面,提升声明,不提升调用。
注:在赋值时,函数只注提升自身,不提升赋值(如果将函数赋值给新的变量,那么这个变量不符合提升原则),所以函数表达式必须先声明赋值,再调用
函数参数
动态参数 arguments
在函数调用的时候括号里面写需要的参数,函数自动会由这些参数形成一个数组
得到伪数组
剩余参数
将不定数量的参数表示为一个数组
在函数括号里面写(参数名,参数名...数组名)
调用时的参数与参数名一一对应(参数名多少个都可以),剩下的加入括号里面定义的数组
得到真数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>函数-动态参数&剩余参数</title>
</head>
<body>
<script>
// 动态参数
function getSum() {
console.log(arguments);
let sum = 0;
for (let i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
console.log(sum);
// 展开运算符
console.log(...arguments);
// 应用:得到最大值
console.log(Math.max(...arguments));
}
getSum(2, 3, 8, 10);
// 剩余参数
function Sum(...arr) {
console.log(arr);
// 展开运算符
console.log(...arr);
// 应用:得到最小值
console.log(Math.min(...arr));
}
// 合并数组
const a1 = [1, 2, 3];
const a2 = [4, 5, 6];
const a3 = [...a1, ...a2];
console.log(a3);
Sum(2, 3, 4);
Sum(1, 2, 3, 4);
</script>
</body>
</html>
展开运算符
将数组元素展开
方法:...数组名
运用
求数组最大值:Math.max(...arr)
合并数组:const arr3=[...arr1,...arr2]
箭头函数
用来代替匿名函数
方法:const fn=()=>{}
没有动态函数,有剩余函数
this指向
不会自己创建this,只会从自己的作用域的上一层沿用(找上一层普通函数),所以回调函数最好不要用箭头函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>箭头函数</title>
</head>
<body>
<script>
// 箭头函数基本语法
// 无参
const fn = () => {
console.log(123);
};
fn();
// 有参 只有一个形参的时候可以const fn=x=>{}
const fn2 = (s) => {
console.log(s);
};
fn2(1);
// 也可以const fn3=x=>x+x(包括返回值)
const fn3 = (x) => {
return x + x;
};
console.log(fn3(1));
// 返回一个对象
const fn4 = (uname) => ({ name, uname });
fn4("张三");
// 调用剩余函数
const getSum = (...arr) => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
};
const resule = getSum(2, 3);
// 全局的this
console.log(this); //window
// 普通函数的this
function fn5() {
console.log(this); //window
}
fn5(); //window.fn()
// 前面有创建对象的函数的this
const obj = {
name: "andy",
sayHi: function () {
console.log(this); //obj
},
};
obj.sayHi();
// 箭头函数的this 指向上一层的作用域的this指向
const fn6 = () => {
console.log(this); //window
};
// 对象方法的箭头函数this\
const obj2 = {
uname: "pink",
sayHi2: () => {
console.log(this); //window 往上一层寻找
},
};
obj2.sayHi2();
// 嵌套函数
const obj3 = {
uname: "123",
sayHi3: function () {
//如果这个是箭头函数,则返回是window
let i = 10;
const count = () => {
console.log(this); //obj
};
count();
},
};
obj3.sayHi3();
</script>
</body>
</html>
解构赋值
数组解构
定义:将数组的单元值快速批量的赋值给一系列变量的简洁语法
方法:把数组里的元素赋值给变量
批量声明变量:const [a,b,c]=arr,输出直接输出a/b/c就行了
交互两个变量(必须是变量):let a=1;let b=2;[b,a]=[a,b]
注:两个立即执行函数之间要加“;”
变量多,元素少时,变量会与元素一一对应,多出来的显示undefined,反过来同理,解决方法:
1、变量多,元素少:设置变量的默认值
2、变量少,元素多:用剩余函数将剩下几个未对应的函数赋给最后一个变量,形成数组
3、按需定义:不需要的元素对应的变量位置直接空出来(eg:a,b,,d)
多维数组([1,2,[3,4]])
普通输出
输出第一第二个数:arr[0/1]
输出第三四个数:arr[2][0/1]
arr[2]输出的是[3,4]
数组解构方法输出
将多维数组直接批量定义给四个变量:[a,b,[c,d]]
输出时直接输出变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>数组解构</title>
</head>
<body>
<script>
const arr = [100, 60, 80];
// 批量声明变量
const [max, min, avg] = arr;
// 交互变量 必须是变量let
let a = 1;
let b = 2;
[b, a] = [a, b];
console.log(a);
console.log(b);
console.log(a, b);
// 解决元素多,变量少
const [c, d, ...e] = [1, 2, 3, 4];
console.log(a, b, c); //c的值为真数组
// 解决变量多,元素少(设置默认值)
const [f = 0, g = 0, h = 0, i = 0] = [1, 2, 3]; //没有赋值到的默认为0
// 多维数组解构
const [one, two, [three, forth]] = [1, 2, [3, 4]];
console.log(one, two, three, forth);
</script>
</body>
</html>
遍历数组方法
遍历的数组.forEach(function(item,index))
只遍历,不返回,适合遍历数组对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>数组遍历</title>
</head>
<body>
<script>
const arr = ["red", "green", "pink"];
arr.forEach(function (item, index) {
console.log(item);
console.log(index);
});
</script>
</body>
</html>
对象解构
属性名:改的名
定义
1、const {{uname,age}}={uname:'123',age:18}
属性名和变量名要一一对应且相同
输出:属性名
2、变量名可以是uname:自己起的名,age:自己起的名
防止对象太多,名字重合
输出:起的名
多级对象解构
const pig={name: ...,family:{......},age:...}
JSON对象解构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>对象解构</title>
</head>
<body>
<script>
// 对象解构
const [{ name: uname, age }] = [
{
name: "佩奇",
age: 18,
},
];
console.log(uname, age);
// 多级对象解构
const pig = {
name: "佩奇",
family: {
mather: "猪妈妈",
father: "猪爸爸",
sister: "乔治",
},
age: 6,
};
const {
name,
family: { mather, father, sister },
} = pig;
console.log(name, mather, father, sister);
// JSON对象解构 对象包数组,数组包对象
const msg = {
code: 200,
msg: "123",
data: [
{ id: 1, title: "456", count: 58 },
{
id: 2,
title: "789",
count: 56,
},
],
};
// 得到data数据
// const {data}=msg
// console.log(data);
// 将data当做参数传递给函数
function render({ data }) {
console.log(data);
}
render(msg);
// 改data的名字
function name({ data: myData }) {
console.log(myData);
}
name(msg);
</script>
</body>
</html>
深入对象
创建对象
const a={属性名:属性,...}
const b=new Object({属性名:属性,...})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>创建对象</title>
</head>
<body>
<script>
// 1
const a = {
uname: "789",
age: 15,
};
console.log(a);
// 2
const obj = new Object();
obj.uname = "123";
obj.age = 18;
console.log(obj);
// 2
const obj2 = new Object({
uname: "456",
age: 19,
});
console.log(obj2);
</script>
</body>
</html>
构造函数:初始化对象
命名:以大写字母开头
只能由new操作符执行(创建对象)
内置构造函数
引用类型
Object:创建普通对象
静态方法只有Object可以调用
获得所有的属性名:Object.keys(对象名)
获得所有的属性:Object.values(对象名)
拷贝对象(给对象添加属性):Object.assign(粘贴对象,复制对象)
Array方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Array常用方法</title>
</head>
<body>
<script>
const arr = ["red", "blue", "green"];
// 找元素
const search = arr.find(function (item) {
return item === "blue";
});
console.log(search);
const arr1 = [
{
name: "张三",
salary: 10000,
},
{
name: "李四",
salary: 10000,
},
{
name: "王五",
salary: 10000,
},
];
// 找张三这条数据 并返回这个对象
const a = arr1.find((item) => item.name === "张三");
console.log(a);
// 返回是否都符合条件
const arr2 = [10, 20, 30];
const flag = arr1.every((item) => item >= 20); //false,一个不满足都不行
console.log(flag);
</script>
</body>
</html>
forEach:遍历数组 不返回数组
filter:过滤数组 返回满足条件的新数组
map:迭代数组 返回处理之后的数组
reduce:累加器 返回累计处理的结果(求和)
find:查找数据 可以用来查找引用对象
from:把伪数组转换为真数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>伪数组转换为真数组</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
const lis = document.querySelectorAll("ul li");
console.log(lis); //伪数组 没有pop
// lis.pop()会报错
const liss = Array.from(lis); //把伪数组转换为真数组
console.log(liss);
liss.pop();
console.log(liss);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>包装类型</title>
</head>
<body>
<script>
// Object类型
const o = { uname: "pink", age: 18 };
console.log(Object.keys(o)); //获得所有的属性值 以数组的形式
console.log(Object.values(o)); //获得所有的属性
const oo = {}; //对象拷贝
Object.assign(oo, o);
console.log(oo);
// 添加对象 拷贝
Object.assign(oo, { gender: "男" });
console.log(oo);
// Array类型
// reduce:实现数组求和 累加
const arr = [1, 5, 8];
const total = arr.reduce(function (prev, current) {
return prev + current;
});
// 初始值为10,累加
console.log(total);
//也可以arr.reduce((prev,current)=>prev+current,10)
const total2 = arr.reduce(function (prev, current) {
return prev + current;
}, 10);
console.log(total2);
</script>
</body>
</html>
包装类型
String常见方法
1、split(设置从什么符号分隔)
2、substring(开始索引号,结束索引号) 省略结束的索引号,默认从设置了的索引号取到最后
3、startsWith:判断是不是以某个字符/字符串开头的
4、include:是不是包含某个字符/字符串
底层包装
js在创建基本对象时会先把简单的数据类型包装成引用数据类型
new String(数据)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>String常见方法</title>
</head>
<body>
<script>
// 把字符串转换为数组
const str = "pink";
const arr = str.split(","); //括号里面是设置数组从什么符号分隔
console.log(arr);
const str1 = "2022-4-8";
const arr1 = str1.split("-");
console.log(arr1);
// 字符串截取
const str3 = "今天要做核酸";
console.log(str3.substring("4"));
console.log(str3.substring("4", "5"));
// 判断是不是以某个字符开头的
const str4 = "pink老师上课中";
console.log(str4.startsWith("pink")); //true
console.log(str4.startsWith("ink")); //false
console.log(str4.startsWith("p")); //true
// 判断某个字符是不是包含在一个字符串里面
const str5 = "我是pink老师";
console.log(str5.includes("pink")); //true
console.log(str5.includes("pink", 3)); //false
</script>
</body>
</html>
实例成员&静态成员
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>实例成员&静态成员</title>
</head>
<body>
<script>
function Pig(name) {
this.name = name;
}
// const peiqi=new Pig('佩奇');//实例对象
// const qiaozhi =new Pig('乔治');
// peiqi.sayHi=()=>{//实例方法
// console.log('Hi');
// }
// console.log(peiqi);
// console.log(qiaozhi);
Pig.eyes = 2; //静态属性
console.log(Pig.eyes); //2
Pig.sayHi = function () {
//静态方法
console.log(this);
};
Pig.sayHi(); //指向构造函数
</script>
</body>
</html>
原型
解决构造函数浪费内存的问题,节省内存,实现方法共享
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>原型对象</title>
</head>
<body>
<script>
function Star(uname, age) {
this.uname = uname;
this.age = age;
//相当于每次创建对象都要创建一个新的这个函数,所以两者不相等
// this.sing=function(){
// console.log('唱歌');
// }
}
// 原型运用:在外面以构造函数创建函数对应对象,实现函数共享
Star.prototype.sing = function () {
console.log("唱歌");
};
const ldh = new Star("刘德华", 55);
const zxy = new Star("张学友", 58);
ldh.sing();
console.log(Star.prototype);
console.log(Star.prototype.constructor);
console.log(ldh.sing === zxy.sing); //true 来自同一个共享的函数
// constructor
function Pig(uname) {
this.uname = uname;
}
const peiqi = new Pig();
console.log(Pig.prototype);
console.log(Pig.prototype.constructor);
console.log(Pig.prototype.constructor === Pig); //constructor指向该原型的构造函数
// 应用
// 相当于给了一个新对象,把以前指向构造函数给覆盖了 相当于脱离原来的构造函数的关系了
Pig.prototype = {
// 重新指向回构造函数
constructor: Pig,
sing: function () {
console.log("唱歌");
},
dance: function () {
console.log("跳舞");
},
};
console.log(Pig.prototype);
</script>
</body>
</html>
prototype属性
指向另一个对象
构造函数名.prototype.函数名=function(){}
调用:实例对象名.函数名()
this指向实例化的对象
constructor:prototype里面的属性
构造函数名.prototype.constructor//指向构造函数
对象原型
构造函数指向实例对象和原型对象,而实例对象指向原型对象
_ proto_属性
实例化对象具有_proto_原型的存在,使得它可以指向原型对象
实例对象._proto_===构造函数.prototype
proto里面的constructor指向对象的构造函数
实例对象._proto_.constructor=构造函数
proto只读,不可赋值
实例对象用_proto_里面的constructor指向构造函数,用_proto_指向原型,原型用constructor指向构造函数
继承
对象.prototype=目标对象
但是如果有多个继承同一个函数的对象,且这些对象都有一些不同,则添加的时候会使得所有继承了的对象都修改这些属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>原型继承</title>
</head>
<body>
<script>
// 继承Person()
// const Person={
// eays:2,
// head:1
// }
// 解决对象指向同一个,导致要改一起改的现象
// 将Person弄成构造函数 父类
function Person() {
this.eyes = 2;
this.head = 1;
}
// 子类
function Woman() {
// this.eays=2;
// this.head=1;
}
// Woman继承Person()
// Woman.prototype=Person;//失去指向构造函数
Woman.prototype = new Person();
Woman.prototype.constructor = Woman;
// 添加方法
Woman.prototype.baby = function () {
console.log("宝贝~");
};
const red = new Woman();
console.log(Woman.prototype.eays);
console.log(Person);
console.log(red);
// 子类
function Man() {
// this.eays=2;
// this.head=1;
}
// Man继承Person()
// Man.prototype=Person//失去指向构造函数
Man.prototype = new Person();
Man.prototype.constructor = Man;
const pink = new Man();
console.log(Man.prototype.eays);
console.log(pink);
</script>
</body>
</html>
原型链
构造函数指向实例对象和原型对象,而实例对象指向原型对象
实例对象用_proto_里面的constructor指向构造函数,用_proto_指向原型,原型用constructor指向构造函数
可以用来为对象成员查找机制提供方向(要查找到能找到的属性,才能使用)
可以使用instanceof用于检测构造函数的prototype属性是否出现在某个实例对象原型链上(eg:prototype instanceof Object)
深浅拷贝
只针对引用数据类型(普通复制用赋值的方式复制对象会导致新的对象修改后原来的对象也会被修改)(相当于在栈里面就是指向同一个数据或者地址)
浅拷贝
拷贝地址
const o={...目标对象名}
Object.assign(粘贴对象,复制对象)
普通数据拷贝数据,所以可以独立修改,但是对象拷贝地址,所以相当于指向堆里面原对象指向的数据,修改就是改的堆里面的
深拷贝
对象复制
递归(看代码)
lodash/cloneDeep(要先引用:<script src=".lodash.min.js"></script>)
JSON.stringify()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>深浅拷贝</title>
<!-- <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> -->
</head>
<body>
<div></div>
<script>
const obj = {
uname: "pink",
age: 18,
};
// 浅拷贝 可以复制目标对象但又可以独立的修改相关的属性
// 1
const o = { ...obj };
console.log(o);
o.age = 20;
console.log(o);
console.log(obj);
// 2
const o2 = {};
Object.assign(o, obj);
o.age = 20;
console.log(o);
console.log(obj);
// 深拷贝
// 1
let i = 1;
function fn() {
console.log(`这是第${i}次`);
// 防止无限调用自己导致栈溢出
if (i >= 6) {
return;
} else {
i++;
fn();
}
}
fn();
// 应用
function getTime() {
document.querySelector("div").innerHTML = new Date().toLocaleString();
setTimeout(getTime, 1000);
}
getTime();
// 2
const obj2 = {
uname: "pink",
age: 18,
hobby: ["足球", "乒乓球"],
family: {
baby: "小baby",
},
};
const o3 = {};
// 拷贝函数 遍历 属于浅拷贝变深拷贝 但是如果有数组,则修改时是多个一起修改,无法独立修改
function deepCopy(newObj, oldObj) {
for (let k in oldObj) {
// 浅拷贝做法
// //k是属性名 oldObj[k]是属性值
// newObj[k]=oldObj[k]//=o.uname
// 解决方法 相当于数组先新建一个数组对象然后遍历拷贝到这个新数组,最后在把这个数组直接给这个拷贝的对象,使得数组脱离出堆里面的原堆,新建一个堆,然后新对象指向这个堆,这个堆复制旧堆里面的数组对象
// 深拷贝做法
if (oldObj[k] instanceof Array) {
newObj[k] = [];
// k是数组索引号
deepCopy(newObj[k], oldObj[k]);
// 对象判断 必须要先数组再对象 不能颠倒 要先把数组筛选完
} else if (oldObj[k] instanceof Object) {
newObj[k] = {};
// k是数组索引号
deepCopy(newObj[k], oldObj[k]);
} else {
newObj[k] = oldObj[k];
}
}
}
deepCopy(o3, obj2);
console.log(o3);
o3.age = 20;
console.log(obj2);
console.log(o3);
o3.hobby[1] = "篮球"; //普通的浅拷贝会让数组独立修改时把原对象的一起修改
o3.family.baby = "老baby";
// 3 lodash
// const o4 = _.cloneDeep(obj2);
// console.log(o4);
// 4 JSON 把对象转换为JSON字符串
const o5 = JSON.parse(JSON.stringify(obj2));
console.log(o5);
o5.family.baby = "123";
console.log(o5);
console.log(obj2);
</script>
</body>
</html>
异常处理
throw:抛出异常
会中断执行
有throw就不用return,没有就要
try...catch...finally捕获异常
debugger打断点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>异常处理</title>
</head>
<body>
<p>123</p>
<script>
// 捕获异常
// function fn(){
// if(!x||!y){
// // 出错时会在控制台显示括号里面的字 程序会终止执行
// throw new Error('没有参数')
// }
// return x+y
// }
// console.log(fn());
// 异常处理
function fn() {
// 可能出现的错误
try {
const p = document.querySelector(".p");
p.style.color = "red";
// 截取并报出错误 然后执行里面的程序
} catch (err) {
console.log(err.message);
throw new Error("你看看,选择器错误了吧");
// 中断程序
return;
} finally {
// 不管对不对都执行
alert("弹出");
}
console.log(11);
}
fn();
</script>
</body>
</html>
this
this指向
普通函数
一般指向window,谁调用函数就指向谁
箭头函数
指向最近作用域的函数
注:DOM对象里面如果要用this不要用箭头函数,原型对象也不要用箭头函数,构造函数不要用箭头函数
严格模式
指向undefined
改变this
1、call(要指向的名):调用函数,指定被调用函数的this值
同时也可以传递参数:call(目标名,参数......)//相当于实参
2、apply(要指向的名):通过调用函数,指向需要的对象
传递参数:apply(目标名,[参数......]
应用:求数组最大值
1)Math.max.apply(Math,arr)
2)Math.max(...,...,...,...,........)
3)Math.max(...arr)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>改变this</title>
</head>
<body>
<button>发送短信</button>
<script>
// 改变this
const obj = {
uname: "pink",
};
function fn() {
console.log(this);
}
fn();
// call()
// 调用函数,改变this的值
fn.call(obj);
// 同时改变参数
function fn1(x, y) {
console.log(this);
console.log(x + y);
}
fn1.call(obj, 1, 2);
// apply()
const obj2 = {
age: 18,
};
function fn2() {
console.log(this);
}
fn2.apply(); //调用函数
// 指向obj2
fn2.apply(obj2);
// 传递参数
const obj3 = {
age: 18,
};
function fn3(x, y) {
console.log(this);
console.log(x + y);
}
fn3.apply(obj3, [1, 2]);
// 求数组最大值
const arr = [1, 2, 8, 5, 9, 0];
// 1
const max = Math.max.apply(Math, arr);
// 2
const min = Math.min(...arr);
console.log(max, min);
// bind()
const obj4 = {
age: 18,
};
function fn4() {
console.log(this);
}
// 不会调用函数:fn.bind()
// 返回值是个函数,但是this更改了
const fun = fn4.bind(obj4);
fun();
// 应用 一个按钮,点击里面就禁用,2秒后开启 不适用箭头函数
document.querySelector("button").addEventListener("click", function () {
this.disabled = true;
// 没有被调用,这个是里面的一部分,算是普通函数,指向全局变量
setTimeout(
function () {
this.disabled = false; //this指向window
}.bind(this),
20000
); //修改this指向且不会马上调用,bind在函数外面btn里面,算是被btn调用
});
</script>
</body>
</html>
3、bind():不会调用函数
通过将函数.bind()复制到另一个函数里面,只是this指向变了
防抖:debounce
单位时间内,频繁触发事件,只执行最后一次
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>防抖</title>
<style>
.box {
width: 500px;
height: 500px;
background-color: gray;
color: white;
text-align: center;
font-size: 100px;
}
</style>
</head>
<body>
<div class="box">1</div>
<script>
// 鼠标滑动数字变大
const box = document.querySelector(".box");
let i = 1;
// 递归函数
function mouseMove() {
box.innerHTML = i++;
// box.addEventListener("mousemove", mouseMove);
}
// 手写防抖函数
// 利用setTiimeout 每次鼠标移动判断是否有定时器,如果有就清除以前的定时器,如果没有,则开启定时器,存入到定时器变量里面
// 定时器里面写函数调用
function debounce(fn, t) {
// 声明定时器变量
let timer;
// 返回一个匿名函数 实际上是立即调用了 debounce 函数并传入了 mouseMove,然后将 debounce 返回的新函数添加为事件监听器。
return function () {
// 判断是否有定时器
if (timer) clearTimeout(timer);
timer = setTimeout(function () {
fn(); //加()调用函数
}, t);
};
}
// 如果是调用函数,则是直接debounce绑定事件监听就行了,此时可以每次触发都能执行,但是里面有mouseMove函数,这使得debounce默认为返回值绑定事件监听,而不是debounce本身,如果没有返回值,则只会执行一次
box.addEventListener("mousemove", debounce(mouseMove, 3000)); //括号里面的函数只会执行一次,因为没有跟mousemove绑定
</script>
</body>
</html>
节流:throttle
单位时间内频繁出发时间,只执行一次
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>节流</title>
<style>
.box {
width: 500px;
height: 500px;
background-color: gray;
color: white;
text-align: center;
font-size: 100px;
}
</style>
</head>
<body>
<div class="box">1</div>
<script>
const box = document.querySelector(".box");
let i = 1;
// 递归函数
function mouseMove() {
box.innerHTML = i++;
// box.addEventListener("mousemove", mouseMove);
}
// 节流 利用定时器来实现
function throttle(fn, t) {
let timer = null;
return function () {
if (!timer) {
timer = setTimeout(function () {
fn();
// 清空定时器 因为清空是在定时器里面进行,所以clearTimeout不能实现
timer = null;
}, t);
}
};
}
box.addEventListener("mousemove", throttle(mouseMove, 3000));
</script>
</body>
</html>