高阶-作用域 函数 闭包
作用域:标识符的作用范围
标识符:变量/函数名/函数形参/对象的属性
- 全局作用域
- 局部作用域
- 块级作用域 ES6 {}
1.1 作用域链
作用域链:查找变量及变量的值,就近原则
查找方法:一级一级往上查找,如果没有找到,继续往上一层作用域继续查找,如果全局作用域仍然没有查找到变量,提醒变量未定义。
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.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 属性 指向
2.2 原型链
原型链查找原则:就近原则,一层一层网上找,直到找到 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.3引用数据类型的存取特点
引用数据类型 重新赋值给新的变量 新的变量值发生改变 会影响之前的变量
引用数据类型 赋值的时候 赋值的是 堆内存的地址
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 函数名()