js高阶总结

175 阅读15分钟

高阶-作用域 函数 闭包

作用域:标识符的作用范围

标识符:变量/函数名/函数形参/对象的属性

  • 全局作用域
  • 局部作用域
  • 块级作用域 ES6 {}

1.1 作用域链

作用域链:查找变量及变量的值,就近原则

查找方法:一级一级往上查找,如果没有找到,继续往上一层作用域继续查找,如果全局作用域仍然没有查找到变量,提醒变量未定义。

1.png

2.函数

2.1 ES5函数相关概念

  • 函数定义方式(声明式与表达式)

  • 函数参数(形参与实参)

  • 函数隐藏参数(arguments 伪数组)

    • ES6语法中 没有arguments
  • return

    • 没有return 返回undefined
  • 回调函数

    • 现在不调,满足特定条件调用
    • 返回异步数据
  • 形式参数默认值

    • ES5 默认参数

2.2 ES6函数新特性

  • 箭头函数 [简写函数的写法]

    • 省略function ,在() 与{}之间 加上 =>
    • 如果函数体只有一句代码,省略 {} 和 return
    • 如果参数只有一个,省略()
    • 如果没有形参,必须写 ()

注意:ES6的箭头函数 没有 arguments 及 this指向

  • ES6函数隐藏参数 (rest 参数)

    • 解决ES6中没有arguments参数的问题

    • let sum = (...data)=>{};

      • data 是一个数组,是所有参数的集合,所有的数组处理方法都能够使用
      • 前面的数据可以 单独用变量接收, rest 参数 获取除了前面已经用变量接收的数据以外的 数据集合
      • reset 参数 必须写在 所有形参的最后
// ...data  获取除了前面已经用变量接收的数据以外的 数据集合
let sum = (a,b,...data)=> {
     console.log(a);   //1
     console.log(b);   //2
    console.log(data);  // [ 2, 5, 5, 66, 5 ]
};
sum(1,2,2,5,5,66,5);

2.3 ES6处理回调的方法

  • Promise 处理回调地狱

2.4 ES6 函数形参默认值

  • 直接在形参中写默认值 赋值
let sum = (a=0,b=0)=>{
    return a+b;
}

2.5 ES5 IIFE立即执行函数

  • 一个js文件只写一个立即执行函数
  • 引入js马上执行这个函数中的内容
;(function(){
	console.log('aaaa');
})()


;(function(a){
    console.log(a);
})(123)

3.闭包

  • 解决问题

    • 局部定义的变量如何在全局作用域访问到?
function fn(){
    var a = 100;
    console.log(a);
}


//输出a
console.log(a);

  • 什么是闭包?

    • 函数的变量跨作用域访问
  • 闭包的一般写法

    • 外部函数包含内部函数,外部函数返回了内部函数
function fn(){
    var num = 100;
    return function(){
        num--;
        return num;
    }
}

外部函数包含内部函数,内部函数挂载到window

;(function(){
     var num = 100;
    window.output =  function(){
        num--;
        return num;
    }
})()

3.1 闭包-体验保管压岁钱

    //将钱交给妈妈保存
    function mom(){
        var money = 500;
       return  function(){
            money -=100;
            
            return money
        }
    }
//小源花钱
    var spendMoney = mom();
console.log(    spendMoney());
console.log(    spendMoney());


 3.2闭包实现-取号器

var add = (function(){
       var num = 100;
       return function(){
           return ++num;
       }
    })();
console.log(add())   // 101
console.log(add())   //102


3.3闭包-解决定时器问题

循环中有定时器,在定时器中输出循环的中间变量

 for(var i=0;i<5;i++){
            setTimeout(function(){
                console.log(i);   //输出5个5
            },1000)
    }

for(var i=0;i<5;i++){
       ;(function(index){
            setTimeout(function(){
                console.log(index);  //输出 0 1 2 3 4  
            },1000)
        })(i)
    }

3.4 闭包-事件监听

事件监听多个lis触发 输出 索引值 i ,无论点击哪个都是输出的都是5

     var lis = document.querySelectorAll('ul li');
            for(var i=0;i<lis.length;i++){
                lis[i].addEventListener('click',function(ev){
                    console.log(i);  // 5
                })
            }


       var lis = document.querySelectorAll('ul li');
            for(var i=0;i<lis.length;i++){
                ;(function(i){
                    lis[i].addEventListener('click',function(ev){
                    console.log(i); // 0 1 2 3 4
                    })
                })(i)
            }

3.5闭包作用

保护内部的标识符,防止外部污染.形成一个简单的模块或块级作用域

4.ES6块级作用域实现闭包

  • 解决事件监听问题
 var lis = document.querySelectorAll('ul li');
            for(let i=0;i<lis.length;i++){
                lis[i].addEventListener('click',function(ev){
                    console.log(i);  // 0 1 2 3 4 
                })
            }

  • 解决定时器问题
 for(let i=0;i<5;i++){
            setTimeout(function(){
                console.log(index);   //0 1 2 3 4
            },1000)
    }

高阶-对象-构造函数-原型链-this指向

1.1 面向过程与面向对象的区别

面向过程:编程注重功能过程实现

面向对象:更加注重整体把控

1.2 类与对象

类:是一群对象的总称,抽象的概念

人类 女朋友 男人 动物 汽车

对象:一个具体的事物,具象化

张三 红色的毛发的一个老虎 蓝色的一辆红旗H7

1 (1).png

2.png 注意:除了内置对象之外,所有的对象都是通过类进行创建的

1.3 创建对象的方式

    //通过实例化创建对象
    let user = new Object();
    //对象属性和方法
    user.name = '张麻子'
    user.age = 25
    user.eat = function(){
        console.log('我正在吃饭...')
    }

    //通过字面量创建对象  语法糖
    let user1 = {
        name:'黄四郎',
        age:25,
        eat:function(){
            console.log('我也要吃饭...')
        }
    }


问题:如果想要创建多个对象,只能通过复制粘贴的方式进行,如何才能够批量创建对象呢

1.4 工厂函数

  • 实现了批量创建对象
  • 不能够识别不同分类的对象
function createObj(name,age,fn){
    return {
        name,
        age,
        fn
    }
}

let user = createObj('张麻子',20,function(){
    conosle.log('我在吃饭')
})
let user1 = createObj('黄四郎',20,function(){
    conosle.log('我在吃饭')
})
let user2 = createObj('老三',20,function(){
    conosle.log('我在吃饭')
})
let houzi =createObj('猴子',5,function(){
    console.log('我在吃饭');
})


问题:如何在批量创建对象的同时,实现对 对象的分类呢?

1.5 构造函数【掌握】

  • 构造函数命名规则 大驼峰
  • 构造函数没有返回值
  • 构造函数的属性和方法 都是挂载到this 上面
  • 对象都是通过构造函数 实例化出来的 实例化关键字:new
//人类
    function Prosen(name,age){
        this.name = name
        this.age = age
        this.eat = function(){
            console.log('我在正吃饭...');
        } 
    }
    let user = new Prosen('张麻子',25);
    let user1 = new Prosen('黄四郎',30);
    //汽车类
    function Car(name,color){
        this.name = name;
        this.color = color;
        this.run = function(){
            console.log('正在以400km/小时的速度跑');
        }
    }
    let hq = new Car('红旗H7','黑色');
    let cc = new Car('长城H6','白色');


//动物类

1.5.1 new 关键字作用

  • 创建一个空对象
  • this指向这个空对象
  • 指向this的属性和方法
  • 将创建的对象返回 出 实例对象

问题:如果创建了一万个实例化对象,就创建了一万个独立方法,内置对象就不会出现这种情况,怎么实现?如何避免构造函数的方法过载?

2.原型与原型链 【掌握】

2.1原型概念

  • 构造函数都有 prototype (显式原型),值是一个对象
  • 实例对象都有__proto__ (隐式原型),指向构造函数的prototype
  • 构造函数的显式原型(prototype)中constructor 属性 指向

3.png

2.2 原型链

4.png 原型链查找原则:就近原则,一层一层网上找,直到找到 Object 在继续往上查找就没有了,Object就是所有的js对象的祖宗。

3.根据原型链概念解决方法过载问题

 function Prosen(name,age){
        this.name = name
        this.age = age
        // this.eat = function(){
        //     console.log('我在正吃饭...');
        // } 
       
    }
    // 原型方法
 	Prosen.prototype.eat = function(){
        cosnole.log('我正在吃饭...')
    }
	
	//示例对象
    let user = new Prosen('张麻子',25);
    let user1 = new Prosen('黄四郎',30);

    console.log(user.eat === user1.eat);  //true

4.自己手写数组的原型方法

  • 实现数组根据元素删除 arr.delete('张三')

    //实现数组根据元素删除  arr.delete('张三')  
    Array.prototype.delete = function(item){
        console.log(this);
       let index = this.indexOf(item);
       if(index >=0){
        this.splice(index,1);
       }
    }

    let arr = ['张麻子','黄四郎','张三','李四']
    arr.delete('李四')
    console.log(arr);

5.this指向【熟悉】

  • 在全局中的this 指向 window
  • 在一般函数中的this 谁调用指向谁
  • 在构造函数中的this 指向构造的实例对象
  • 在对象的方法中的this 谁调用指向谁
  • 在事件函数中的this 指向事件源
  • 在定时器中的this 指向window
  • 在箭头函数中的this 指向上一级
  //全局this window
    console.log(this);  //window
    console.log(this.location);

    //一般函数
    var name = '李四'
    //普通函数中的this
    function sum(a,b){
        var name = '张三'
        console.log(name);
        console.log(this.name);
    }
    sum(1,2)


//object 中的this指向 谁调用指向谁
    var name ='李四';
    let obj = {
        name:'张三',
        age:25,
        eat:function(){
            console.log(this.name)
        }
    }
    obj.eat()  //张三

    let fn = obj.eat;
    fn()    //李四

     //事件函数中的this   指向事件源
    document.querySelector('button').addEventListener('click',function(ev){
        console.log(this)   //<button>点我吧</button>
    })

   //定时器中的this
    window.setTimeout(function(){
        console.log(this)
    },1000)


    //箭头函数:箭头函数自己没有this  拿到上一层级的this 
	 document.querySelector('button').addEventListener('click',function(ev){
        console.log(this)   //<button>点我吧</button>
         
     let sum =(a,b)=>{
        console.log(this);
        return a+b;
    	}
   		sum();   //<button>点我吧</button>
    })	






   function fn(a,b){
        setTimeout(()=>{
                console.log(this)    //window
        },1000)
   }
   fn(1,2)

6.this的借用

  • 立即执行--调用

    • 要借的对象.要借的函数.call(借给谁,参数1,参数2...)
    • 要借的对象.要借的函数.apply(借给谁,[参数1,参数2])
  • 永久借用--新的函数

    • let fn = 要借的对象.要借的函数.bind(借给谁,参数1,参数2)
    let user = {
        name:'张三',
        show:function(a,b){
            console.log('我是:'+this.name);
            console.log(a,b);
        }
    }

    let user1 = {
        name:'张麻子'
    }

// console.log(user.show());   张三

//立即执行--调用函数
// - 要借的对象.要借的函数.call(借给谁,参数1,参数2...)
// - 要借的对象.要借的函数.apply(借给谁,[参数1,参数2])
user.show.call(user1)    //张麻子
user.show.apply(user1,[100,200])  // 我是:张麻子      100 200
                                  
//永远借用--赋值出来一个新的函数
// let fn = 要借的对象.要借的函数.bind(借给谁,参数1,参数2)
let fn = user.show.bind(user1,200,3200);
fn()    //我是:张麻子      200 3200


高阶-引用数据类型-变量检测-类的继承

1.1 数据类型回顾

  • ES6基本数据类型

    • number string null boolean undefined symbol
  • ES6引用数据类型

    • Object 【object array function】

1.2数据存储模式

基本数据类型的数据直接存到 栈内存

引用数据类型的数据 将数据存到 堆内存 将堆内存地址存到栈内存

1 (2).png

1.3引用数据类型的存取特点

引用数据类型 重新赋值给新的变量 新的变量值发生改变 会影响之前的变量

引用数据类型 赋值的时候 赋值的是 堆内存的地址

2 (1).png

1.4 引用数据类型的浅拷贝

浅拷贝:只拷贝第一层的数据,第二层及以上层 拷贝地址

深拷贝:所有层级都是拷贝数据而不是地址

  • 直接赋值,赋值的是地址,连浅拷贝都算不上
  • 通过for...in 遍历赋值 浅拷贝
  • 对象通过 Object.assign() 多对象合并实现 浅拷贝
  • 数组通过 concat() 多数组合并实现浅拷贝

1.5 深拷贝实现

  • 可以借助JSON字符串实现 【将引用数据类型 转换为JSON字符串 再转换为 引用数据类型 】深拷贝
  • 通过遍历所有层级 实现深拷贝【递归】【扩展】
  let user = {
        name:'张三',
        age:25,
        like:['打篮球','敲代码','看书']
    }

    let user1 =JSON.parse( JSON.stringify(user));
    
    user1.like[0] = '打游戏'

    console.log(user1);
    console.log(user);

	//写一个深拷贝
    function deepClone(obj){
        //首先判定是数组还是对象
        var objClone = Array.isArray(obj)?[]:{};
        
        //判断obj是否为空且为对象
        if(obj && typeof obj === "object"){

            //逐个考虑obj的属性
            for(key in obj){

                //obj本身是否有key这个属性(不包含原型对象(继承)来的)
                //如果不加这个if,就是连带着原型对象里包含的属性一块继承。
                if(obj.hasOwnProperty(key)){ 
                    if(obj[key] && typeof obj[key] === "object"){
                        //如果该属性值非空,并且还是对象,就递归调用深拷贝
                        objClone[key] = deepClone(obj[key]);    
                    }else{
                        //如果已经到了可以直接拷贝的基本数据类型,就直接浅拷贝完事
                        objClone[key] = obj[key];           
                    }
                }
            }
        }
        return objClone;
    }


2.变量类型检测

  • typeOf 检测 (null 对象 数组 都是 object)

    缺点:不能区分 null 对象 数组

    let str = 'xxx';
    let num = 123123;
    let data= null;
    let data1 = undefined;
    let bool = true;

    let arr = [1,2,3,3];
    let obj = {username:'xxx'};
    let fn = function(){console.log('xxxx');};

    console.log(typeof str);    //string
    console.log(typeof num);   //number
    console.log(typeof data);   // object
    console.log(typeof data1);  //undefined
    console.log(typeof bool);   //boolean


    console.log(typeof arr);    //object
    console.log(typeof obj);    //object
    console.log(typeof fn);    //function  
    
    // null 数组  对象  typeof检测出的数据类型都是 object 

instanceof 检测引用数据类型

缺:不能够检测出 数组和对象的区别,一般用作判断该数据是否为引用数据类型

    let arr = [1,2,3,3];
    let obj = {username:'xxx'};
    let fn = function(){console.log('xxxx');};

    // 变量名 instanceof Object     
    //返回值:true:是对象  false:不是对象
    //一般情况下 用作:是否是引用数据类型的检测

    console.log(arr instanceof Array);    
    console.log(arr instanceof Object); //true
    console.log(obj instanceof Object);  //true
    console.log(fn instanceof Object);   //true

Array.isArray()

作用:一般只用作数组的检测

 let arr = [1,2,3,6,5,6];
    let obj = {name:'张三'}
    console.log(Array.isArray(arr));   //true
    console.log(Array.isArray(obj));   //false

Object.prototype.toString.call()

优:所有的数据类型都能够被判断---完美

    let str = 'xxx';
    let num = 123123;
    let data= null;
    let data1 = undefined;
    let bool = true;

    let arr = [1,2,3,3];
    let obj = {username:'xxx'};
    let fn = function(){console.log('xxxx');};

    //借用Object 的原型方法 toString
    let toString = Object.prototype.toString


    console.log(toString.call(str));        //[object String]
    console.log(toString.call(num));        //[object Number]
    console.log(toString.call(data));       //[object Null]
    console.log(toString.call(data1));      //[object Undefined]
    console.log(toString.call(bool));       //[object Boolean]
    console.log(toString.call(arr));        //[object Array]
    console.log(toString.call(obj));        //[object Object]
    console.log(toString.call(fn));         //[object Function]



3.ES5子类继承父类【掌握】

  • 子类拥有父类的所有属性 及方法
  • 子类有自己的属性 及 方法

3.1继承的步骤

子类继承父类的属性

//借用父类属性
Prosen.call(this,name,age)

子类继承父类的方法

Student.prototype = Object.create(Prosen.prototype);

子类将constructor 指向构造函数自己

 Student.prototype.constructor = Student;


完整继承示例

  //人类
    function Prosen(name,age){
        this.name = name ;
        this.age = age ;
    }
    Prosen.prototype.eat = function(){
        console.log('吃东西')
    }

    //学生类
    function Student(name,age,sno){
        //1.子类集成父类的属性 借用父类的属性 call
        Prosen.call(this,name,age)
        // this.name = name;
        // this.age = age;
        this.sno = sno;
    }
        //2.子类集成父类的方法
        Student.prototype = Object.create(Prosen.prototype);
       // 3. 子类的constructor 指向自己
       Student.prototype.constructor = Student;

        //4.
        Student.prototype.study = function(){
            console.log('天天学习')
        }


    let user = new Prosen('张麻子',40)
    let students = new Student('小明',15,'学001');



高阶-ES6语法糖

CMAScript 6.0(以下简称 ES6)是JavaScript语言的下一代标准

2.解构赋值

2.1 数组的解构赋值

    //数组解构赋值
    let arr = [100,'小明',200];
    
    let [a,b,c] = arr;
    let [,,m] = arr;
    let [x,y] = [100,200];

2.2 对象的解构赋值

 //对象解构赋值
    //将对象中的某个属性 通过变量提取出来
    // 按照对象属性名进行匹配
    let user = {name:'张麻子',age:20};

    let {age,name} = user;
    console.log( name);
    console.log(age);

2.3 函数的解构赋值

   //函数解构赋值
    function sum([a,b]){
        return a+b;
    }

    console.log(  sum([100,200]));


    // 
    function userCreate({name,like='打麻将'}){
        return '我是:'+name+'我的爱好:'+like
    }
   console.log( userCreate({name:'张麻子;',age:20,sex:'男'}));

2.4 解构赋值案例

  • 请求json文件并拿到json数据
    $http.get('data.json',({id,status,data} )=>{
        console.log(id);
        console.log(status);
        console.log(data);
    })

3.展开运算符 ...

作用:将字符串 数组 对象 进行展开

  // 1.展开字符串
    let str = 'xxczxcxc';
    console.log(...str);
    // 字符串转为数组
    // let strArr = str.split('');
    let strArr = [...str];
    console.log(strArr);

    //2.展开数组
    let arr = ['xxx','aaaa','333'];
    let arr1 = ['张麻子','张三'];
    console.log(...arr);
    //数组合并【浅拷贝】
    // let arr2 = arr.concat(arr1);
    let arr2 = [...arr1,...arr];
    console.log(arr2);

    //将伪数组转换为真数组
    function sum(){
        let argArr = [...arguments];
        console.log(argArr);
    }   
    sum(1,5,69,8,9,5,5,5,5);


    //将对象进行展开
    let user={name:'张三',age:'20'};
    let info = {sex:'男',like:'打游戏'};
    //对象合并【浅拷贝】
    // let obj = Object.assign({},user,info);
    let obj = {...user,...info};
    console.log(obj);

4.模板字符串

作用: 字符串拼接 可以在字符串拼接中写变量 ${data}

let user={
        name:'张麻子',
        age:45,
        like:['抽烟','喝酒','烫头']
    }

    // let str = '我的名字:'+user.name+';'+'我的年龄:'+user.age+'我的爱好:'+user.like[0]+','+user.like[1]+','+user.like[2]; 
    
    let str = `
    我的名字:${user.name},
    我的年龄:${user.age},
    我的爱好:${user.like[0]}${user.like[1]},${user.like[2]}
    `
    console.log(str);

5.ES6的类

.1 最简单类的写法

    // ES6 人类
     class  Prosen{
        // 构造器 接收参数--构造属性
        constructor(name,age){
            this.name = name;
            this.age = age;
        }
        //方法
        eat(){
            console.log('吃.....');
        }
     }
     let user = new Prosen('黄四郎',25)

5.2 类的属性 类的静态方法

    // ES6 人类
     class  Prosen{
        //类特有的属性--常量
        have='两只眼睛';
        features = '一张嘴巴';
        // 构造器 接收参数--构造属性
        constructor(name,age){
            this.name = name;
            this.age = age;
        }
        //方法--公有方法
        eat(){
            console.log('吃.....');
        }
        //静态方法---类的私有方法
        //只有类才可以使用的方法
        static war(){
            console.log('打仗...');
        }
     }
     let user = new Prosen('黄四郎',25)

     console.log(Prosen.war());

5.2 ES6 类的 继承

  • ES6 子类可以完美继承所有父类所有的东西

    • 父类的属性(常量) 父类公共方法 父类静态方法 父类构造器中的属性
//子类写法
class  子类名  extends 父类名{
    constructor(){
        super(传递给父类的数据);
    }
}



 //人类
    class Prosen{
        features="两只眼睛 一张嘴巴";

        //构造器
        constructor(name,age){
            this.age= age;
            this.name = name;
        }
        //公共方法
        eat(){
            console.log('吃东西...')
        }

        //静态方法
        static war(){
            console.log('战争...');
        }
    }

    //医生类
    class Doctor extends Prosen{

        constructor(name,age,genre){
            // 继承父类的属性及方法
            super(name,age);
            this.genre=genre;
        }
        //公共方法
        show(){
            console.log('看病')
        }
        //静态方法
       static likeColor(){
            console.log('喜欢穿白色的衣服');
        }
    }

    let doctor1 = new Doctor('张麻子',40,'外科')


6.模块化

模块的优点:

1.高内聚:尽可能将一个功能的实现 写到一个模块中

2.低耦合:尽可能每个模块之间相互不产生影响

6.1 回顾node环境中的模块导入与导出

//导出
export.moudle = {xx}
//导入
require('./index.css');
const xx = requrie('./a.js');


6.2 ES6环境的模块导入导出

6.2.1 语法一

//导出.js
export let a= '张三';
export let age = '25';
export let arr = [11,32,3,556,2,5]


//导入js
import {a,age,arr} from './导出语法1.js'

console.log(a);
console.log(age);
console.log(arr)


   <!-- 引入最终的主模块到html页面 -->
   <script src="./导入.js" type="module"></script>


6.2.2 语法二【用得更多】

//导入js
let name= '张麻子';
let user ={
    name:'黄四郎',
    age:20
}
let likes = ['抽烟','喝酒','烫头'];
export default {
    name,
    user,
    likes
}


//导入js
//语法二
//1.接收一个对象
// import userInfo from './导出语法2.js'
// console.log(userInfo);

//2.对象解构赋值接收
//注意:必须先用一个变量对象接收了值 之后才能使用结构拿到每个属性
import userInfo from './导出语法2.js'
let {name,user,likes} = userInfo;
console.log(name);
console.log(user);
console.log(likes);


   <!-- 引入最终的主模块到html页面 -->
   <script src="./导入.js" type="module"></script>


高阶-Promise异步处理

同步:代码从上而下依次执行,后面的代码必须要等待前面的代码执行完成之后才能够执行

异步:需要等待才能执行的代码(定时器,事件触发的函数,ajax请求),如果执行代码遇到异步代码,先将异步放到一边,先执行后面的同步代码

2.ajax异步请求

  • 多个ajax 嵌套请求会形成回调地狱
  • 后期没有办法维护代码
console.log(1);
    //多个ajax 嵌套请求===形成回调地狱
    $http.get('data.json',res=>{
        
        if(res.id === 42){
            $http.get('data1.json',res1=>{
                //处理js

                if(res1.code == 0){

                    $http.get('data2.json',res2=>{

                        console.log(res2);
                    })
                }
            })
        }
      
    })

3.Promise

作用:处理回调地狱的问题

特点:Promise 自己是一个构造函数 实例化出一个对象

3.1 最简Promise

 //最简Promise  
    //resolve 成功时的回调函数
    //reject 失败时的回调函数  
    new Promise((resolve,reject)=>{
        let index = true
   
        if(index === true){
            resolve(100);  //成功触发
        }
        if(index === false){
            reject('出错啦');  //失败触发
        }
    })
    //then 接收成功函数的回调
    .then(function(res=1){  //成功的回调
        console.log(res);
    })
    //catch 接收失败函数的回调
    .catch(function(err =0){ //失败的回调
        console.log(err);
    })
    

3.2 体会带有异步的Promise

    //最简Promise  
    //resolve 成功时的回调函数  
    new Promise((resolve,reject)=>{
        //异步触发Promise 成功回调
        //3秒后触发成功
        setTimeout(()=>{
            resolve(2000)
        },3000)
    })
    .then(function(res=1){  //成功的回调
        console.log(res);
    })
    .catch(function(err =0){ //失败的回调
        console.log(err);
    })

3.3体验带有异步请求的Promise

  new Promise((resolve,reject)=>{
        //发送请求
        $http.get('data.json',res=>{
                //请求成功
                if(res.id == 42){
                    resolve(res)
                }else{
                    reject('请求失败')
                }
      
         })
    })
    .then((res)=>{
        console.log(res)
        console.log('请求成功');
    })
    .catch((err)=>{
        console.log('请求失败');
    })


3.4 利用Promise 重写 多层ajax 回调

   new Promise((resolve,reject)=>{
        //发送请求
        $http.get('data.json',res=>{
                //请求成功
                if(res.id == 42){
                    resolve(res)
                }
         })
    })
    .then((res)=>{
        console.log('第一层请求成功');
        console.log(res);
 
        //第二层请求开始 
        return new Promise((resolve,reject)=>{
            $http.get('data1.json?id='+res.id,res1=>{
                if(res1.code ==0){
                    resolve(res1);
                }    
            })
        })

    })
    .then((res1)=>{
        console.log('第二层请求成功');
        console.log(res1);
    
        //第三层请求
        return new Promise((resolve,reject)=>{
            $http.get('data2.json?id=3',res2=>{
                if(res2.orderId){
                    resolve(res2)
                }
            })
        })
    })
    .then(res2=>{
        console.log('第三层请求成功');
        console.log(res2);
    })
    

3.5 优化Promise ajax请求

//第一步 提取公共的js代码进行封装
  //获取参数
    //封装Promise 与ajax
    function getData(url,data){
        //如果有参数就拼接
        if(data){
            url +='?'+data;
        }
       return new Promise((resolve,reject)=>{
            //发送请求
            $http.get(url,res=>{
                    //请求成功
                    if(res.status ===0){
                        resolve(res)
                    }
            })
        })
    }


    getData('data.json').then((res)=>{
        console.log(res);
       return getData('data1.json','id='+res.id)
    })
    .then((res1)=>{
        console.log(res1);
      return  getData('data2.json','id=3')
    })
    .then(res2=>{
        console.log(res2);
    })
    

// 第二步 es6语法极致简化
   //获取参数
    //封装Promise 与ajax
    function getData(url,data){
        //如果有参数就拼接
        if(data){
            url +='?'+data;
        }
       return new Promise((resolve,reject)=>{
            //发送请求
            $http.get(url,res=>{
                    //请求成功
                    if(res.status ===0){
                        resolve(res)
                    }
            })
        })
    }
    getData('data.json')
    .then(res=>getData('data1.json','id='+res.id))
    .then(res1=>getData('data2.json','id=3'))
    .then(res2=>{
        console.log(res2);
    })

4.axios 插件

作用:ajax封装的插件,三方包

//实现上面的请求

axios('data.json')
    .then(res=>axios('data1.json',{params:{id:res.id}}))
    .then(res1=>axios('data2.json',{params:{id:3}}))
    .then(res2=>{
        console.log(res2.data);
    })

5.async await

异步问题:异步中某个变量的值重新改变,后面的代码拿不到最新的值

let a = 1;
//先同步后异步
setTimeout(()=>{
    a=2;
},0)

console.log(a);  //1


async await作用:将异步操作变为同步操作

5.1 声明异步函数

    //async await 语法
    //async 声明该函数有异步操作 一般写在函数 function 的前面
    //await 等待异步执行完毕,和async连用

    //声明异步函数
    async function getData(){

    }
    //表达式声明异步函数
    const getData1 = async function(){

    }
    
	//Es6 写法 箭头函数
    const getData2 = async ()=>{

    }



5.2 实现:将异步定时器改为同步

    console.log(1);
        const timer = ()=>{
            return new Promise((resolve,reject)=>{
                setTimeout(() => {
                    console.log(2);
                    resolve('成功了')
                }, 0)
            })
        }


      
    const num = async () => {
        console.log(1.5);
        await timer();
        console.log(3);
        console.log(4);
    }
    num()



5.3 完美实现:将异步ajax改为同步

 // 1.先把异步代码放到 声明的异步函数中
    const getData = async ()=>{
        //2.在异步操作的的前面加上await 实现同步化
        const res = await axios('data.json');
        const res1 = await axios('data1.json',{params:{id:res.id}});
        const res2 = await axios('data2.json',{params:{id:3}});
        console.log(res2.data);
    }
    getData();


函数全解

JavaScript 从一门不被看好的脚本语言,逐渐发展为主流开发语言,甚至拆分出诸多分支如:TypeScript Nodejs 之后仍旧能够和诸多后端语言(java C python)争榜肯定是一套非常牛逼的底层设计的,而这一套底层设计是如何做的,本文从函数角度触发分为三个部分逐步让你深入认识JavaScript的牛逼之处。

函数是什么,作用是啥?

遇到这个问题,很多资深的小伙伴可能有一大堆解释,甚至会照搬红宝书的概念,或者某位大佬的名言....

其实这些答案都不是你自己总结的,只是复刻别人的理解记忆为概念而已,每个人对事物的定义都应有不同。

我的理解:函数就是装了一段特定功能的js代码块,作用:为了减少重复的js代码

函数的体现形式有哪些?

1.普通函数 【带函数名的函数】function 函数名(){}

2.匿名函数【没有函数名的函数】 function (){}

3.立即执行函数【定义完成瞬间调用】 (function(){})()

4.箭头函数【只是普通函数的简写语法糖】const 函数名=()=>{}

普通函数的定义方式

两种定义方式:声明式定义 表达式定义

  • 声明式定义
//上来就写function 告诉浏览器:“我正在定义一个函数哟”
// 函数名要求:符合变量命名的要求(以英文字母、下划线、$开头,组成:英文字母、下划线、$、数字)
function 函数名(){
    //函数体 
}
//上来就写function 告诉浏览器:“我正在定义一个函数哟”
// 函数名要求:符合变量命名的要求(以英文字母、下划线、$开头,组成:英文字母、下划线、$、数字)
function 函数名(){
    //函数体 
}

  • 表达式定义
//定义方式 类似于 赋值表达式,将一个匿名函数赋值给 变量
const 函数名 = function(){
    //函数体
}

两种定义方式的区别:

声明式定义的函数,具有函数提升功能,即:在当前js文件中 任意位置定义,支持在当前js文件中 任意位置调用。

表达式定义的函数,遵循:先定义,后使用原则。

调用方式有哪些?

直调-直接调用 函数名()

对象-对象中有属性值是一个函数对象.属性名()

回调-在函数的调用中传递了函数【回调函数】

自调-在函数体中调用了自己【递归函数】

实例化-构造函数或者类的调用【new 关键字】new 函数名()