const,let和var
// 1、声明提升
console.log(name) // undefined
var name='jing'
//2、没有局部作用域
function fun(){
for(var i;i<3;i++){
console.log(i)
}
console.log(i) // 3 let的话会报错
}
fun()
//3、声明覆盖 let的话会报错
var name = 'zhangsan'
var name = 'lisi'
console.log(name) // lisi
// 不可以只定义不赋值,不可以重复赋值
const a; // 报错
const b=1
b=2 // 报错
深拷贝和浅拷贝
基本数据类型,赋值 ,没有什么拷贝不拷贝的
// 1、基本数据类型
// 赋值 硬说的话是深拷贝
let a = 5
let b = a
b = 3
数组/对象赋值,浅拷贝
// 2、赋值-数组与对象的都是浅拷贝
let arr = [1,2,3]
let newArr = arr
newArr.push(4)
console.log(arr,newArr) // [1,2,3,4]
解构赋值-深拷贝?浅拷贝?
一维解构赋值深拷贝,多维解构赋值浅拷贝
let arr = [1,2]
let newArr = [...arr]
newArr.push(3) // 不影响arr
let arr2 = [[1,2],[3,4]]
let newArr2 = [...arr2]
newArr[0].push(6) // arr一样变化
JSON.parse(JSON.stringify())实现深拷贝
function不可以 // function=> 'function'
let arr = [{
name:'张三',
age:2,
},
{
name:'李四',
age:3,
}]
let newArr = JSON.parse(JSON.stringIfy(arr))
newArr.push({id:0}} // 不影响arr
手写deepClone方法,实现深拷贝
// 引用数据类型 (数组/对象)
function deepClone(obj) {
let targetObj = obj.constroctor === Array ? [] : {};
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
// obj[key]是引用数据类型
//维护层代码,可省略
targetObj[key] = obj[key].constroctor === Array ? [] : {};
// 递归
targetObj[key] = deepClone(obj[key]);
} else {
// 基本数据类型
targetObj[key] = obj[key];
}
}
return targetObj;
}
this指向问题
全局,指向window,全局方法指向window
console.log(this); // window
// 全局方法的this,指向window
function fun() {
console.log(this); // window
}
fun() // window.fun()
那么this是谁调用指向谁?不不不
let o = {
name: '小红',
fun1: function () {
console.log(this);// o 对象
}
}
o.fun1()
var obj = {
name: '小绿',
fun1: function () {
console.log(this.name);// 小绿
}
}
window.obj.fun1()
this指向上一级调用者,可以说是点前面的那个
var j = {
a: 10,
b: {
a: 12,
fn: function () {
console.log(this.a); // 12
console.log(this); // b对象
}
}
}
j.b.fn() // 12
// this指向上一级调用者,点前面的那个对象
剪头函数的this呢?
指向外层,因为箭头函数是没有作用域的
// 箭头函数
var id = 88
function fn5() {
let id = 9999
// 剪头函数没有作用域,没有this,指向外层
setTimeout(() => {
console.log(this.id); // 88
console.log(this); // window
}, 500)
}
fn5({
id: 66
})
var q = {
a: 10,
b: {
a: 12,
fn: ()=> {
console.log(this.a); // undefined
console.log('q',this); // window
}
}
}
q.b.fn()
改变this指向call,apply,bind
如果让fn5的内部打印的不是22,而且传入的age呢?
使用call,apply,bind
var age = 22
function fn5(args1, args2) {
console.log('fn5', args1, args2);
setTimeout(() => {
console.log(this.age);
console.log(this);
}, 500)
}
//改变this
//call apply,bind
//call 传参=> {} apply=>[] 会自动执行一次 , bind不会自动执行
fn5.call({
age: 44
}, 1, 2)
fn5.apply({
age: 55
}, [3, 4])
fn5.bind({
age: 66
}, 5, 6)() // 需要调用一下
区别
1、call和apply在改变完this。后会自动执行一次
bind,不会自动执行
2、 call传参方式是对象,apply传参是数组
闭包
闭包是什么?
方法返回一个方法!?感觉不准确,返回的方法里面需要引用了其中的变量
所以,闭包可以说是:包含被引用的变量(函数)的对象(看图解)
func1是闭包
产生闭包的条件?
- 存在函数嵌套(方法返回一个方法)
- 内部函数引用了外部函数的数据(变量/函数),并执行了外部函数 ( 执行引用了其变量(函数)的函数之后,才产生闭包
比如上图,再不行,看下图
闭包的作用
前言
let a = 'jing'
function fun() {
let b = 1
console.log(a);
}
// console.log(b); // 报错
fun()
为什么访问不到b,可以访问到a?
在js中,,会有AO(临时变量对象active object ) =>包含{方法,局部变量}
AO程序执行完后,就会被垃圾机制回收
全局变量不会被回收
如何外层访问到b?用闭包。那么闭包的作用是
<!-- 1、避免变量污染 -->
<!-- 2、私有化 -->
<!-- 3、保存变量,常驻内存,(不会被回收机制回收) -->
// demo 1
function fun2 () {
let a = 2
let b = 3
function fn(c) {
return {num:a+c,a}
}
// fn()
return fn
}
let a = 999
const fun3 = fun2()
console.log(fun3(2)); // {num:4,2}
console.log(fun3(3)); // {num:5,2}
// demo2
function closeDemo(){
let val = 0
function changeVal(num){
val+=num
}
return {
add1:(num)=>{
changeVal(1)
},
value:()=>{
return val
}
}
}
let closeFun = closeDemo()
closeFun.add1()
closeFun.add1()
console.log(closeFun.value()); // 2
1、 外部的a不影响内fun2中的a =》避免变量污染,私有化
2、分别传入c=2,c=3,可以得到 a=2,a+c 计算后的结果 =>保存变量,常驻内存
闭包的实际应用场景
防抖
固定时间内,多次触发合为一个,每次触发重头开始计算时间
const debounce = (fn, delay) => {
// 多个合为一个,每次触发重头开始计算时间
let timer = null
return () => {
clearTimeout(timer)
timer = setTimeout(fn(), delay)
}
}
节流
不论触发多少次,固定时间只能执行一次
const throttle = (fn, delay) => {
// 不论多少次,固定时间只能一次
let init = false
let timer;
return (event) => {
if (init) return
init = true // 记录状态
clearTimeout(timer) // 进入先清空延时
timer = setTimeout(() => {
// 到时间重置,可再次进入执行
init = false
}, delay)
fn(event)
}
}
obj = Object.create(null) 和 obj = {} 的区别
let obj ={}
let obj1 = Object.create(null)
let obj2 = new Object()
console.log('obj',obj,'obj1',obj1,'obj2',obj2);
// obj1 是很纯净的对象,没有原型链
// 因为没有原型链,循环时候效率会高一点
控制台打印结果
obj1 是很纯净的对象,没有原型链
因为没有原型链,循环时候效率会高一点
new关键字
function Person() {
this.name = 'jing'
this.fn = function () {
console.log(`名字是${this.name}`);
}
}
// let user = new Person()
// console.log('user',user);
new的一瞬间发生了什么
// 1、 创建一个空对象
let obj = new Object()
// 2、设置原型链
obj.__proto__ = Person.prototype
// 3、改变this的指向
let result = Person.call(obj)
// 4、判断返回类型
if(typeof result =='object'){
user = result
}else{
uesr = obj
}
为何判断返回类型?
普通函数默认返回值:undefined
构造函数默认返回值:新创建的对象
事件委托
js事件委托,就好比委托前台帮自己收快递(统一处理某个事件),即将来的同事也可以来了后委托前台帮忙收快递(新添加的元素)
什么是事件委托?
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的托幼事件
<!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 id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<button id="btn">点击加一</button>
<script>
let ul = document.getElementById('ul')
ul.onclick=function(event){
event= event||window.target
console.log(event);
let {target} = event
// 事件委托
if(target.nodeName==='LI'){
alert(target.innerText)
}
}
const btn = document.getElementById('btn')
btn.onclick = (()=>{
const el = document.createElement('li')
el.textContent='4'
ul.appendChild(el)
})
</script>
</body>
</html>
promise
为什么有pormise,之前是什么代替了promise的工作?
回调函数!之前对于异步后续要执行的逻辑。都用回调实现。一直回调,逻辑复杂会形成回调地狱。可维护性差
promise是什么
promise是一个构造函数,用于表示一个异步操作的最终完成 (或失败)及其结果值。
promise应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ES6-promise应用</title>
</head>
<body>
<script>
// 为什么会有promise,没有promise之前是谁实现了promise做的事情?
// 回调函数 :想要顺序执行异步,就需要回调。但是逻辑复杂后,回调地狱,可维护性差
// run(0)
// setTimeout(()=>{
// run(1)
// setTimeout(()=>{
// run(2)
// },500)
// ,1000})
// Promise怎么用
// Promise =》 承诺,诺言---->异步任务
/*
许一个承诺(送包包) new Promise ----> 开始等待 状态:pendding(等待)---->实现了 状态:(fulfilled 成功)--》送礼物resolve()
|
| 食言了(rejected 失败)
|
v
给出理由 reject()
*/
let isForget = false // 是否记得
let getLv = new Promise((resolve,reject)=>{
if(isForget){
let lv ={
color:'red',
price:'1999'
}
resolve(lv)
}else{
let err = new Error("加班太忙,忘记了")
reject(err)
}
})
let testFn = function(){
getLv.then((fulfilled)=>{
console.log('isForget = true,记得承诺,获包包');
console.log(fulfilled);
}).catch((rejected)=>{
console.log('isForget = false,忘记了,给出理由');
console.log(rejected.message);
})
}
testFn()
</script>
</body>
</html>
手写promise
数组相关方法
jion (链接),split(分割)
push (尾部添加)返回:长度 pop(尾部删除)返回:删除项
改变原数组
unshift(头部添加)返回:长度 shfit(头部删除)返回:删除项
改变原数组
splice(start,howmany,itemX)(移除,插入,更新)返回:更新/删除项
slice(start,end)(移除)返回:删除项
区别:1、 splice 3个参数,slice参数两个
2、splice 会改变原数组,slice不会
3、splice可以给数组查询新元素,slice不能
axios取消请求
方式一:使用 CancelToken.souce 工厂方法创建一个 cancel token,
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('https://mdn.github.io/dom-examples/abort-api/sintel.mp4', {
cancelToken: source.token
}).catch(function (thrown) {
// 判断请求是否已中止
if (axios.isCancel(thrown)) {
// 参数 thrown 是自定义的信息
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
那么我们是如何判断请求的中止状态呢?
axios 为我们提供了一个 isCancel() 方法,用于判断请求的中止状态。isCancel() 方法的参数,就是我们在中止请求时自定义的信息。
方式二:通过传递一个 executor 函数到 CancelToken 的构造函数来创建一个 cancel token:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// 取消请求
cancel('Operation canceled by the user.');
事件循环机制先执行宏任务还是微任务?
<!-- 脚本 1 -->
<script>
// 同步
console.log("start1");
// 异步宏
setTimeout(() => console.log("timer1"), 0);
new Promise((resolve, reject) => {
// 同步
console.log("p1");
resolve();
}).then(() => {
// 异步微
console.log("then1");
});
// 同步
console.log("end1");
</script>
<!-- 脚本 2 -->
<script>
// 同步
console.log("start2");
// 异步宏
setTimeout(() => console.log("timer2"), 0);
new Promise((resolve, reject) => {
// 同步
console.log("p2");
resolve();
}).then(() => {
// 异步微
console.log("then2");
});
// 同步
console.log("end2");
</script>
执行结果
- 宏任务包括:setTimeout setInterval Ajax DOM事件
- 微任务:Promise async/await
整个浏览器循环应该是 先执行 宏任务
-> 同步代码
-> 微任务
,直到当前宏任务中的微任务清理完毕,继续执行下一个宏任务,以此类推。Script是一个宏任务