js高阶常用

150 阅读9分钟

1.作用域

概念:标识符的有效范围
标识符:变量 对象的属性 函数名 函数的形参
作用域分类:
全局,局部,块级是在es6新增的let+{}
局部和块级有什么区别
相同点:他们都是只能在局部访问
不同点
局部作用域 : 仅限于 ‘函数体’ 内部声明的变量
块级作用域 : 一切大括号{} 内部使用let/const声明的变量

2.作用域链

变量值的查找规则:逐级向上,就近原则
根据作用域链进行查找;默认的是在当前局域查找如果找不到就它的上一层的查找该变量;

3.暂存死区

如果你在全局有一个变量叫let a=100;
然后你在局部里面先打印有在后面定义了一个leta=1000;
这样就形成了暂存死去浏览器会报错;要遵循let 先声名在使用的原则

4.函数的定义方式

声明式--
函数的提升:将函数的定义提升到当前作用域的最前面
function 变量名(){
    函数体
}

表达式

const 变量名 =function(){

    可以写多个
    get:(){
    }
}


函数的调用
直调 
函数名()
对调 方法的调用--面向对象封装
对象.属性名() 对象.方法名() 对象.函数名()
//回调 现在不调 回头再调
//有了返回数据时 调用回调函数
//自调==递归函数(类似于循环)一维数组


函数的参数
一般情况下,形参实参 必须要一一对应

参数默认值设置
function sum (a,b){
a=a 默认值
b=b 默认值
}
//es6
function sum(a=默认值,b=默认值){

}

参数列表arguments
类数组--实参列表
使用环境:函数的实参个数不确定

函数的返回值
一般情况下,函数都有返回值return;
函数运行中一旦遇到了return;终止函数的运行
没有return === 自动返回undefined【异步+直接操作dom的函数】


箭头函数-es6【掌握】
箭头函数于普通函数不同
1.箭头函数没有了arguments参数列表
2.箭头函数中没有this指向
表达式语句
去掉function,在(){}之间加=>()=>{}
如果形参有且只有一个可以省略()
如果函数体中只有一句代码 可以省略retutn关键字,同时省略{}
如果没有形参必须写()

实参列表 rest参数
rest就是一个真数组
它的语法 。。。变量

5.闭包的应用

概念:跨作用域传值
作用1;保护变量 不被外界污染
作用2:形成独立的局部作用域==es6出来let+在这个函数体中有{}替代了
闭包常见语法
函数中包含内部函数,且将内部函数返回
将函数内部的数据传递出去

6.对象-类概念

对象:描述单个 个体
类 :描述的具有相同属性的群体



什么时工厂函数
工厂:不断的生成对应的个体
工厂函数实现批量创建对象

8.构造函数

函数名必须大写
没有retunrn返回值
将属性挂载到this上
通过new关键字 实力换生成对象

9.new的关键字作用

创建空的对象
将this指向空对象
给this挂载属性和属性值
将有数据的this返回给实例对象

方法过载问题
构造函数中的方法,每次实例化对象时都会挂载一次到堆内存
当创建的实例对象很多时造成堆内存中的函数挂载非常多==
会使内存的溢出电脑卡顿蓝屏

需要将所有的方法挂载到实例对象的原型(prototype)上,
表示只在内存中存储一次。

10.原型

每个构造函数都有显示原型prototype是一个对象
每个实例对象都有隐式原型———proto——指向自己构造函数的显示原型
原型中constructor属性指的是构造函数本身

2.png

11.原型链

原型链的查找:逐级向上就近原则
构造函数的方法 构造函数的属性构造函数原型上的方法
this获取当前实列对象

12.修改数组的原有方法

重写push
Array.prototype.mypush=function(...rest){
    this代编当前对象的实例对象
    for(let i =0; i<rest.length;i++){
        this.this[rest.length]=rest[i]
    }
}
重写indexof
Array.prorotype.myindexof=item=>{
    //首先我们定义一个初始值
    let index = -1;
    for(let i =0; i<this.length;i++){
     if(this[i]==item){
         index = ;
         //终止循环的语句
         break
     }
    }
    return index;
}

重写map方法
Array.prototype.myMap = (item,index)=>{
let arr = [];
    for(let i = 0; i<this.length;i++){
    //call改变this的指向
        arr[i] = call(this[i],i)
    }
    return arr;
}

重写foreach方法
Array.prototype.myForEach = call=>{
for (let i=0;i<this.length;i++){
        call(this[i],i)
    }
}

13.this的指向

全局的this指的是window
构造函数的this指的是nwe出来的实例化对象
事件执行函数this指的是监听dom
对象方法中的this指的是当前对象
箭头函数中的this 其实箭头函数本身没有this,他指的是上一层作用域的this
函数中的this 指的是 谁调用这个函数这个this就指向谁
函数中包函数的this 指的是window
对象和数组中函数的this 指的是谁调用我指向谁
定时器延时器中的this 指的是windoww

14.浅拷贝

只要不改变原数组的值就算浅拷贝 
数组的对象的拷贝 只是第一层拷贝了数据,第二层是引用数据类型,第二层及以上都是拷贝的地址
所有js提供的跟拷贝相关的方法全是浅拷贝
赋值运算连浅拷贝都不算,地址的赋值

15.深拷贝

无论数组和对象有多少层次,都是拷贝数据
json 字符串的两次转换
json两次转换会丢失function的代码

let arr = [100,[200,300],[[[200]]]];
let arr1 = JSON.parse(JSON.stringify(arr)); // 两次转换 开辟新的堆内存的存储空间 
arr1[0] = 1000;
arr1[1][0] = 2000;
arr1[2][0][0][0] = 2000000;
console.log(arr1);
console.log(arr);  //不受新数组改变的影响
//创建一个对象 属性值 是function ==fucntion 调用会丢失


深拷贝算法 deepclone
核心思想:遇到了引用数据类型 创建一个 空的 对应的数据类型 来接收 所有的数据
算法实现:for...in 循环 +递归
var arr = [100,200,[33,[[[[200]]]],[[[[8]]]]]]

function deepclone (data){
//判断data是数组 ===创建一个空数组 还是 对象 ===创建空对象
var min = Array.isArray(data)?[]:{};
for(var key in data){
    //判断当前元素是不是引用数据类型
    if(typeof data[key] =='object'){
        //将应用数据类型的整个数据传递到这个函数 进行下一次大的调用 并且接受返回值
        min[key] = deepclone(data[key])
    }else{
    //是基本数据类型 直接赋值
    min[key] = data[key]
    }

}
    return min;
}

17.数据类型检测

1.typeof
可以区分数组 字符串 布尔值 undefined 函数
2.indstanceof
区分 基本数据和引用数据
如果false就是基本数据类型
如果是true就是引用数据类型

18.Array.isArray

验证是数据 是否是数组;是数组返回true

18.this方法借给谁,内部的this就指向谁

立即调用(借了马上用)
要接的对象.要借的函数.call(借给谁,参数1,参数2)
要接的对象.要借的函数.apply(借给谁,【参数1,参数2】)

永久借(一直可以用)
let 变量 = 要借的对象.要借的函数.bind(借给谁,参数1,参数2)
变量()

19.Object.prototype.toString()

根据传入的实例对象===this 返回该实例对象的 数据类型 构造函数的函数名
Object.prototype.toString.call('asdasdasd')
let toString = Object.prototype.toString;
console.log( toString.call('asdasdasd')    );
console.log(toString.call(1111));
console.log(toString.call(true));

20.工具函数的封装之数据类型万能检测

/**
 * utils  工具函数
 * author:cometang   time:2022-05-22
 */

const utils = {

    /**
     * @getDataType  获取任意数据的数据类型 
     * @data  任意数据  'sdad' 123  {a:1}
     * @return String  'Number' 'String' 'Boolean'
    */
     getDataType:data=>{
        let toString = Object.prototype.toString;
        let str = toString.call(data);
        str = str.substring(8,str.length-1)
        return str;
     }
}

window.utils = utils

21.原型链继承

子类 继承 父类的方法 和父类的属性

子类继承父类的属性
父类.call(this,参数1...) || 父类.apply(this,[参数1,参数2])

子类继承父类的方法
//修改子类的prototype 的——proto 指向 父类prototype
子类.prototype._proto_=父亲.prototype

22.寄生组合继承

继承属性
父类.call(this,参数1) ||父类.apply(this,[参数1,参数2])
继承方法
继承父类方法的语句 必须写在 子类自己的方法的前面
子类.prototype = Object.create(父类.prototype)
子类.prototype.constructor = 子类
//子类 
function Lamp( brand,color,ford,efficiency){
            // 自己的独有属性 挂载this 上
            this.brand = brand;
            this.color = color
            //继承父类的属性 
            Device.call(this,ford,efficiency)
}
    //子类方法前面
    // Object.create()  空对象  
    // 将父类的prototype 装 空对象中;子类的prototype中的 __proto__ 指向这个对象
    // 子类.prototype = Object.create(父类.prototype)
    // 弊端:将子类的prototype 身上存在的 constructor 指向了父类-需要手动修正 
    // 子类.prototype.constructor = 子类
    Lamp.prototype = Object.create(Device.prototype);
    Lamp.prototype.constructor = Lamp;

23.es6的继承

class 开端 +类名
属性都写到construor中
原型方法直接写到类里面
class 类名{
    //构造器中拥有所有的属性 挂载到this 
    constructor(属性1,属性2...){
        this.属性1 = 属性1;
        ....
    }
    //原型方法
    方法名(){  
    }
    方法名2(){ 
    }
    ...
}


es6的继承
子类 继承父类的方法 extends
super
class  子类  extends 父类{
    constructor(属性1,属性2....){
        //继承父类的属性---必须写在 自己的属性前面
        super(属性2,属性3......)
        
        //自己的属性 挂载到 this上
        this.属性1 = 属性1;
        ...  
    }

24.对象 数组解构赋值

对象数组赋值 简写方法
对象的解构赋值
let {属性1,} =对象
//如果获取一个 没有在对象中的属性名 拿到的值 是  undefined
//打乱获取的顺序 之后,可以获取到 正确的值吗 ,可以正确拿到值



数组的解构赋值
根据先后顺序进行 解构赋值
let arr=[100,200,300,400];
//arr[0] arr[1]
let [a,b,c,d] = arr;
//当作变量使用 a,b,c,d 均为新增的变量

//如果需要获取数组中的某个元素
let [,,aa] = arr;   //300
【y,x】=[x,y]


函数参数 解构赋值
function fn ([a,b,c]){
    return a+b+c
}
log fn([1,2,3])

25.展开运算符 剩余运算符...

合并多数组 合并多对象 是浅拷贝
将数据集合 数据列表,展开成单个数据的列表
let arr = [10,21,5,15]
let arr1 = [11212,5,45,458,485]
let arr2 = [2,4,5]
//数组合并
let arr3 = [...arr,...arr1,...arr2]

26.模块化语法

nodejs --commonjs规范
//导出
const user = {name:'张飒'}
const fn=()=>{}
module.exports = {
    user,
    fn,
}
//导入
const xx = require('文件相对路径')


es6新增模块化语法
导出单个变量
//导出暴露两个变量 b.js
export let aa = '你好呀'
export let user = {name:'张麻子',age:40}

//a.js
//导入的 大括号中的变量必须在导出js里面存在  
import {aa,user} from './b.js'
console.log(aa);
console.log(user);

//第二种 导入单个对象导出的变量==实现类似于批量导入
// 别名 就是一个对象 
//import * as 别名 from '文件路径'
import * as xxx from './b.js';
console.log(xxx.aa);
 <script src="./a.js" type="module"></script>
 

27.同步异步

同步代码执行顺序:从上至下 从左至右 依次执行
异步代码执行顺序:等待所有的同步代码执行结束之后,等待时间到了开始执行。

console.log(1)
dom.onclick = ()=>{
	console.log(2) 
}
console.log(3)
dom.click() //如果把他放在3前面就执行的123
//1  3 2

28.回调地狱问题

回调地狱是值函数进行嵌套使用 === 超过三级 成为回调地狱
代码的语法复杂

28.1 解决回调地狱的问题

async异步 await等待 异步转同步
异步转同步代码
1后面的代码可以获取到前面的异步代码的数据
2,代码简化
函数名 前面加上 async 表示该函数是异步函数
函数的内部 只要是异步代码 ,异步代码前面 加上await
//解决回调地狱---通过then的链式调用
axios('data.json',{}).then(res=>{
    return axios('data1.json',{})
}).then(res=>{
    return axios('data2.json',{})
})
.then(res=>{
    return axios('data3.json',{})
})
.then(res=>{
    return axios('data3.json',{})
})

// async   声明该函数为 异步函数 
async fn()=>{
    axios('data.json',{}).then(res=>{})
}
async fn()=>{
    // await  等待该语句的执行结果
    // 异步操作的代码前面 加 等待效果  就没有 then(res) 
   console.log(1)
  let res = await axios('data.json',{})
    console.log(res)
    console.log(2)
    let res1 = await axios('data1.json',{})
    console.log(3)
}


//异步转同步 从上之下 依次运行--更加完美解决回调地狱
  const getData = async ()=>{
       let res = await axios.get('/perfectGoods/list?id=1');
        console.log(res);
        let res1 = await axios.get('/life/list?id=1');
        console.log(res1);
    }
    getData()

28.2# try catch 异常捕获

try 当中的代码 正确执行 catch 函数 就不会执行
try 当中的代码 执行 代码报错 catch 函数自动执行 并且 获取error对象
try {} 形成了 块级作用域
如果需要在try 外面 使用 try 里面变量 :
变量的定义 提升到上一层的作用域;
所有的js代码 都写到 try 块中

 const getData = async () => {
        try {
            let res = await axios.get('/perfectGoods/list?id=1');
            let res1 = await axios.get('/life/list?id=1');
            console.log(res);
            console.log(res1);
        } catch (err) {
            console.log(err); //获取到的错误 error 对象
            alert(err.message)
        }
    }
    getData()

29.Promise

异步代码
Promise本身是一个构造函数 可以通过new关键字实例化
最简Promise
promise 定义
// promise 定义
let promise  = new Promise((resolve,reject)=>{
               if(true){  //成功情况
    				resolve(1000)
				} else{
                    reject('请求失败了....')  //失败的情况
                }        
})

//使用promise 实例对象
promise.then(res=>{
    console.log(res) // 1000
}).catch(err=>{
    console.log(err)  //'请求失败了....'
})
promise 状态记录的机器(状态机)


promise 三个状态
等待panding
成功 fulfilled resolve
失败 reject


promise 实例的使用
then 方法的数据是成功时resolve返回出来的
catch方法的数据是失败时reject返回出来的【如果没有写catch,reject返回报错】


promise代码环境
与ajax请求连用
<script src="./http.js"></script>
<script>

new Promise((resolve,reject)=>{
    //请求ajax 
    $http.get('../回调的问题/data.json',{},res=>{
        //等待ajax 的请求 返回值 
        // 判断code ===0 ---resolve()
        // 否则 reject()
        let {code,data,msg}  =res;
        if(code ==0){
            resolve(data)
        }else{
            reject(msg)
        }
    })
}).then(res=>{
    console.log(res)
}).catch(err=>{
    console.log(err)
})

</script>

通过promise实现层级调用
   new Promise((resolve,reject)=>{
        //ajax请求
        $http.get('./data.json',{},res=>{
            let {id} = res.data
            resolve(id)
        })
    }).then(id=>{
            new Promise((resolve,reject)=>{
                   //ajax请求
                $http.get('./data1.json',{id},res=>{
                    let {name} = res.data
                    resolve(name)
                })
            }).then(name=>{
                new Promise((resolve,reject)=>{
                          //ajax请求
                    $http.get('./data2.json',{name},res=>{
                        let {tell} = res.data
                        resolve(tell)
                    })
                }).then(tell=>{
                        new Promise((resolve,reject)=>{
                                     //ajax请求
                            $http.get('./data3.json',{tell},res=>{
                                let data= res.data
                                resolve(data)
                            })
                        }).then(res=>{
                            console.log(res);
                        })
                })
            })
    })
 //链式调用  then catch   
// 调用then 返回一个新的promise  
    new Promise((resolve,reject)=>{
        //ajax请求
        $http.get('./data.json',{},res=>{
            let {id} = res.data
            resolve(id)
        })
    }).then(id=>{
          return  new Promise((resolve,reject)=>{
                   //ajax请求
                $http.get('./data1.json',{id},res=>{
                    let {name} = res.data
                    resolve(name)
                })
            })
    })
    .then((name)=>{
            return  new Promise((resolve,reject)=>{
                   //ajax请求
                $http.get('./data2.json',{name},res=>{
                    let {tell} = res.data
                    resolve(tell)
                })
            })
    })
    .then(tell=>{
        return  new Promise((resolve,reject)=>{
                   //ajax请求
                $http.get('./data3.json',{tell},res=>{
                    resolve(res.data)
                })
            })
    })
    .then(res=>{
        console.log(res);
    })
//axios 封装 
function axios(url,data){
    return new Promise((resolve,reject)=>{
        //ajax请求
        $http.get(url,data,res=>{
            resolve(res.data)
        })
    })
} 
//{id} 函数的解构赋值===res.id
   axios('./data.json',{}).then(({id})=>axios('./data1.json',{id}))
    .then(({name})=>axios('./data2.json',{name}))
    .then(({tell})=>axios('./data3.json',{tell}))
    .then(res=>console.log(res);)

30.面向对象+ajax+promise 封装axios

const BASE_URL = 'https://www.fastmock.site/mock/08a15d0f6138e60e4015493d447ddb3a'

function format(data){
    let str = '';
    for(let key in data){
        str+= `&${key}=${data[key]}`
    }
    str = str.slice(1);
    return str;
}
const axios ={
    get:(url,data)=>{
       return new Promise((resolve)=>{
           let xhr = new XMLHttpRequest();
           xhr.open('get',BASE_URL+url+'?'+format(data)) 
           xhr.send()
           xhr.onreadystatechange =()=>{
            if(xhr.readyState ===4 && xhr.status ===200){
                let res = JSON.parse( xhr.responseText)
                resolve(res)
            }
           }
       }) 
    },
    post:(url,data)=>{
        return new Promise((resolve)=>{
            let xhr = new XMLHttpRequest();
            xhr.open('post',BASE_URL+url)
            xhr.setRequestHeader('content-type','application/json')
            xhr.send(JSON.stringify(data))
            xhr.onreadystatechange =()=>{
             if(xhr.readyState ===4 && xhr.status ===200){
                 let res = JSON.parse( xhr.responseText)
                 resolve(res)
             }
            }
        }) 
    },
    ajax:()=>{

    }
}

window.axios = axios;