ES6学习

let和const命名

let基本用法-块级作用域

在es6中可以使用let声明变量,用法类似于var

⚠️ let声明的变量,只在let命令所在的代码块内有效

{let a = 10;var b = 20;
}
console.log(a); //a is not defined
console.log(b); //20
不存在变量提升

var命令会发生变量提升现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定在声明后使用,否则报错

//var的情况
console.log(c);//输出undefined
var c = 30;//let的情况
console.log(c);// 报错ReferenceError
let c = 30;
不允许重复声明

let不允许在相同作用域内,重复声明同一个变量(var允许)

let c = 10;
let c = 30;
console.log(c); //报错function func(arg) {let arg; // 报错
}
暂时性死区

暂时性死区,说的就是letconst命令声明变量的特征。

在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区(temporal dead zone,简称 TDZ)

ES6明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域,凡是在声明之前就是使用这些变量,就会报错

//暂时性死区
var tmp = 20;
if(true){// tmp='abc';//ReferenceError: Cannot access 'tmp' before initializationlet tmp;console.log(tmp);//undefinedtmp=123;console.log(tmp);//123
}// typeof x;//ReferenceError: Cannot access 'x' before initialization
let x;
//暂时性死区也意味着typeof不再是一个百分之百的安全操作
//如果一个变量根本没有被声明,使用typeof反而不会报错
console.log(typeof c);//undefined
为什么需要块级作用域
原因一:内层变量可能会覆盖外层变量
function foo(a){console.log(a);if(1===2){var a = 'hello 小马哥';}
}
var a = 10;
foo(a);
原因二:用来计数的循环遍历泄露为全局变量
var arr = []
for(var i = 0; i < 10; i++){arr[i] = function(){return i;}
}
console.log(arr[5]());

变量i只用来控制循环,但是循环结束后,它并没有消失,用于变量提升,泄露成了全局变量。

解决循环计数问题

//解决方式一:使用闭包
var arr = []
for(var i = 0; i < 10; i++){arr[i] = (function(n){return function(){return n;}})(i)
}
//解决方式二:使用let声明ivar arr = []
for(let i = 0; i < 10; i++){arr[i] = function () {return i;}
}
const基本用法-声明只读的常量

这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。对于const来说,只声明不赋值,就会报错。

const a = 10;
a = 20;//报错const b; //报错
let命令相同点
  • 块级作用域
  • 暂时性死区
  • 不可重复声明
letconst使用建议

在默认情况下用const,而只有你在知道变量值需要被修改的情况下使用let

模板字符串

传统的 JavaScript 语言,输出模板通常是这样写的

const oBox = document.querySelector('.box');
// 模板字符串
let id = 1,name = '小马哥';
let htmlTel = "<ul><li><p>id:" + id + "</p><p>name:" + name + "</p></li></ul>";
oBox.innerHTML = htmlTel;

上面的这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题

let htmlTel = `<ul><li><p>id:${id}</p><p>name:${name}</p></li>
</ul>`;

函数

function pick(obj, ...keys){// ...keys解决了arguments的问题let result = Object.create(null);for(let i=0; i<keys.length; i++){result[keys[i]] = obj[keys[i]];}return result;
}let book = {title: 'es6教程',author: '小马哥',year: 2019
}
let bookData = pick(book, 'year', 'author');
console.log(bookData);
function pick(obj){let result = Object.create(null);for(let i=i; i<arguments.length; i++){result[arguments[i]] = obj[arguments[i]];}return result;
}
let book = {title: 'es6教程',author: '小马哥',year: 2019
}
let bookData = pick(book, 'author', 'year');
console.log(bookData);

扩展运算符

//剩余运算符:把多个独立的合并到一个数组中
//扩展运算法:将一个数组分割,并将各个项作为分离的参数传给函数
const maxNum = Math.max(20, 30);
console.loh(maxNum);//处理数组中的最大值,使用apply
const arr = [10, 20, 50, 30, 90, 100, 40];
console.log(Math.max.apply(null, arr));//es6 扩展运算符
console.log(Math.max(...arr));

箭头函数

//使用=>来定义 function(){}等于与()=>{}//let add = function(a,b){return a+b};
//let add = (a, b) => {return a + b};
let add = (a, b) => a + b;
console.log(add(10, 20));let fn = ()=> 'hello world' + 123;
console.log(fn());
let fn = (function(){return function(){console.log('hello es6');}
})();let fn = (()=>{return ()=>{console.log('hello ea62');}
})();
fn();let getObj = id => {return {id: id,name: '小马'}
}
let getObj = id => ({id: id,name: '小马'
})let obj = getObj(1);
console.log(obj);

箭头函数this指向和注意事项

//没有this指向
//es5中this指向:取决于调用该函数的上下文对象
let PageHandle = {id: 123,//init函数不要使用箭头函数,因为会指向windowinit: function(){//箭头函数没有this指向 箭头函数内部this值只能通过查找作用域链来确定,一旦使用箭头函数,当前就不存在作用域document.addEventListener('click', (event) => {this.doSomeThings is not a function//console.log(this);//documentthis.documentThings(event.type);}, false)//false不冒泡//this.documentThings(event.type);//}.bind(this), false)//false不冒泡//document.addEventListener('click', function(event){//this.doSomeThings is not a function//console.log(this);//document//this.documentThings(event.type);//})},doSomeThings: function(type){console.log(`事件类型:${type},当前id:${this.id}`)}
}
PageHandle.init();//使用箭头函数的注意事项:
//1.使用箭头函数 函数内部没有arguments
let getVal = (a, b) => {console.log(argments);//is not definedreturn a + b;
}
console.log(getVal(1, 3));
//2.箭头函数不能使用new关键字来实例化对象
let Person = ()=>{};
let p = new Person();//Person is not a constructor

解构赋值

解构赋值是对赋值运算符的一种扩展。它通常针对数组和对象进行操作。

优点:代码书写简洁且易读性高

数组解构

在以前,为变量赋值,只能直接指定值

let a = 1;
let b = 2;
let c = 3;

ES6允许我们这样写:

let [a,b,c] = [1,2,3];

如果解构不成功,变量的值就等于undefined

let [foo] = [];
let [bar, foo] = [1];

foo的值都会等于`undefined

对象解构

解构可以用于对象

let node = {type:'identifier',name:'foo'
}let {type,name} = node;
console.log(type,name)//identifier foo

对象的解构赋值时,可以对属性忽略和使用剩余运算符

let obj = {a:{name:'张三'},b:[],c:'hello world'
}
//可忽略 忽略b,c属性
let {a} = obj;
//剩余运算符 使用此法将其它属性展开到一个对象中存储
let {a,...res} = obj;
console.log(a,res);
//res{b:[], c:'hello world'}

默认值

let {a,b = 10} = {a:20};
函数参数解构赋值
function add([x, y]){return x + y;
}add([1, 2]); // 3

使用默认值

function addCart(n,num=0){return n+num;
}
addCart(10);//10
addCart(10,20); //30
用途
  • 交换变量的值

    let x = 1;
    let y = 2;
    let [x,y] = [y,x];
    

    上面代码交换变量xy的值,这样的写法不仅简洁,而且易读,语义非常清晰。

  • 从函数返回多个值

    函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。

// 返回一个数组function example() {return [1, 2, 3];
}
let [a, b, c] = example();// 返回一个对象function example() {return {foo: 1,bar: 2};
}
let { foo, bar } = example();
  • 函数参数的定义

​ 解构赋值可以方便地将一组参数与变量名对应起来。

// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
  • 提取JSON数据

​ 解构赋值对提取 JSON 对象中的数据,尤其有用

let jsonData = {id: 42,status: "OK",data: [867, 5309]
};//data: number把data重命名为number
let { id, status, data: number } = jsonData;
//对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
console.log(id, status, number);
// 42, "OK", [867, 5309]
  • 函数参数的默认值

​ 输入模块的指定方法

​ 加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。

const {ajax} = require('xxx')ajax()

函数的扩展

带参数默认值的函数

ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法

function log(x,y){y = y || 'world';console.log(x,y);
}
log('hello');//hello world
log('hello','china') //hello china
log('hello','')//hello world

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {console.log(x, y);
}log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

ES6 的写法还有两个好处:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。

默认的表达式可以是一个函数

function getVal(val) {return val + 5;
}
function add2(a, b = getVal(5)) {return a + b;
}
console.log(add2(10));//20

小练习

请问下面两种写法有什么区别?

// 写法一
function m1({x = 0, y = 0} = {}) {return [x, y];
}// 写法二
function m2({x, y} = { x: 0, y: 0 }) {return [x, y];
}

上面两种写法都对函数的参数设定了默认值,区别是写法一函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;写法二函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。

// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
rest参数

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {let sum = 0;for (var val of values) {sum += val;}return sum;
}add(2, 5, 3) // 10

上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。

箭头函数 ***

ES6允许使用箭头=>定义函数

let f = v=>v;
//等同于
let f = function(v){return v;
}// 有一个参数
let add = value => value;// 有两个参数
let add = (value,value2) => value + value2;let add = (value1,value2)=>{return value1 + value2;
} 
// 无参数
let fn = () => "hello world";let doThing = () => {}
//如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
let getId = id => ({id: id,name: 'mjj'}) //注意
let obj = getId(1);
箭头函数的作用
  • 使表达更加简洁

    const isEven = n => n % 2 == 0;
    const square = n => n * n;
    
  • 简化回调函数

    // 正常函数写法
    [1,2,3].map(function (x) {return x * x;//[1,4,6]
    });// 箭头函数写法
    [1,2,3].map(x => x * x);
    
使用注意点
  • 没有this绑定

    let PageHandler = {id:123,init:function(){document.addEventListener('click',function(event) {this.doSomeThings(event.type);},false);},doSomeThings:function(type){console.log(`事件类型:${type},当前id:${this.id}`);}
    }
    PageHandler.init();//解决this指向问题
    let PageHandler = {id: 123,init: function () {// 使用bind来改变内部函数this的指向document.addEventListener('click', function (event) {this.doSomeThings(event.type);}.bind(this), false);},doSomeThings: function (type) {console.log(`事件类型:${type},当前id:${this.id}`);}
    }
    PageHandler.init();let PageHandler = {id: 123,init: function () {// 箭头函数没有this的指向,箭头函数内部的this值只能通过查找作用域链来确定// 如果箭头函数被一个非箭头函数所包括,那么this的值与该函数的所属对象相等,否则 则是全局的window对象document.addEventListener('click', (event) => {console.log(this);this.doSomeThings(event.type);}, false);},doSomeThings: function (type) {console.log(`事件类型:${type},当前id:${this.id}`);}
    }
    PageHandler.init();
    
  • 箭头函数中没有arguments对象

    var getVal = (a,b) => {console.log(arguments);return a + b;
    }
    console.log(getVal(1,2)); //arguments is not defined
    
  • 箭头函数不能使用new关键字来实例化对象

    let Person = ()=>{}
    let p1 = new Person();// Person is not a constructor
    

对象的扩展

属性的简洁表示法
const name = '张三';
const age = 19;
const person = {name, //等同于name:nameage,// 方法也可以简写sayName() {console.log(this.name);}
}
person.sayName();

这种写法用于函数的返回值,将会非常方便。

function getPoint() {const x = 1;const y = 10;return {x, y};
}getPoint()
// {x:1, y:10}
对象扩展运算符
const [a, ...b] = [1, 2, 3];
a // 1
b // [2, 3]
解构赋值

对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }

解构赋值必须是最后一个参数,否则会报错

let { ...x, y, z } = obj; // 句法错误
let { x, ...y, ...z } = obj; // 句法错误
扩展运算符

对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

扩展运算符可以用于合并两个对象。

let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);
对象的方法
//is() ===
// 比较两个值是否严格相等
console.log(NaN === NaN);//false
console.log(Object.is(NaN, NaN));//true//assign()
//对象的合并
//Obejct.assign(target, obj1, obj2);//返回合并之后的新对象
let newObj = Object.assign({}, {a: 1}, {b: 2});
console.log(newObj);//{a:1,b:2}

Symbol类型

原始数据类型Symbol,它表示是独一无二的值

最大的用途:用来定义对象的私有变量

const name1 = Symbol('name');
const name2 = Symbol('name');
console.log(name === name);//falselet s1 = Symbol('s1');
console.log(s1);//Symbol(s1)
let obj = {};
obj[s1] = '张三';
// 如果用Symbol定义的对象中的变量,取值时一定要用[变量名]
console.log(obj[s1]);//张三
console.log(obj.s1);//undefindedlet obj2 = {[s1]: '张三';
};
console.log(obj2[s1]);//张三//用.声明时,obj输出的是{sa: '张三'}
//用[]声明时,输出obj是{Symbol('name'): '张三'}//获取Symbol声明的属性
let s = Object.getOwnPropertySymbols(obj2);
console.log(s[0]);//张三
let m = Reflect.ownKeys(obj2);
console.log(m[0]);//张三

Set集合数据类型

Set集合:表示无重复值的有序列表
在这里插入图片描述

let set = new Set();
console.log(set);//Set(0) {}//添加元素 add()
set.add(2);
set.add('4');
set.add('4');
set.add([1,2,3]);
console.log(set);//set(3) {2, "4", Array(3)}
//删除元素 delete()
set.delete(2);
//判断元素是否存在集合中  has()
console.log(set.has('4'));//true
//获取集合长度
console.log(set.size);//2
//遍历集合
set.forEach((val, key)=>{//键值一样console.log(val);console.log(key);
});//将set转换成数组
let set2 = new Set([1,2,3,3,3,4]);
console.log(set2);//Set(4) {1,2,3,4}
let arr = [...set2];
console.log(arr);//(4) [1,2,3,4]
//1.set中对象的引用无法被释放
let set3 = new Set();
let obj1 = {};
set3.add(obj1);
//释放当前的资源
obj1 = null;
console.log(set3);//set3还存在objlet set4 = new WeakSet();
let obj2 = {};
//释放当前的资源
obj2 = null;
console.log(set4);//set4没有obj//WeakSet
//1.不能传入非对象类型的参数
//2.不可迭代
//3.没有forEach()
//4.没有size属性

Map数据类型在这里插入图片描述

let map = new Map();
console.log(map);//Map(0) {}
map.set('name', '张三');
map.set('age', '20');
console.log(map);// Map(2) {"name" => "张三", "age" => 20}
console.log(map.has('name'));//true
map.delete('name');
console.log(map);//Map(1) {"age" => 20}
map.clear();
console.log(map);//Map(0) {}map.set(['a',[1,2,3]], 'hello');
console.log(map);//Map(1) {Array(2) => "hello"}let m = new Map([['a', 1],['c', 2]
]);
console.log(m);//Map(2) {'a' => 1, 'c' => 2}//WeakMap
//1.不能传入非对象类型的参数
//2.不可迭代
//3.没有forEach()
//4.没有size属性

数组的扩展方法

//1.from() 将伪数组转换成真正的数组
function add(){console.log(arguments);//Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]//es5转换let arr = [].slice.call(arguments);console.log(arr);//(3) [1, 2, 3]//es6写法let arr1 = a=Array.from(arguments);console.log(arr);//(3) [1, 2, 3]
}
add(1,2,3);let lis = document.querySelectorAll('li');
console.log(lis);//NodeList(4) [li,li,li,li]
console.log(Array.from(lis));//(4) [li,li,li,li]
// 扩展运算符 将伪数组转换成真正的数组
console.log([...lis]);//(4) [li,li,li,li]//from() 还可以接受第二个参数,用来对每个元素进行处理
let liContents = Array.from(lis, ele => ele.textContent);
console.log(liContents);//(4) ["1", "2", "3", "4"]//2.of() 将一组任意数据类型的值,转换成数组
console.log(Array.of(3,11,20,'30'));//(4) [3,11,20,"30"]//3.copyWithin() 数组内部将制订位置的元素复制到其他位置,返回当前数组
// 从3位置往后的所有数值,替换从0位置往后的3个数值
[1,2,3,8,9,10].copyWithin(0, 3);//[8,9,10,8,9,10]//4.find() findIndex()
//find()找出第一个符合条件的数值成员
let num = [1,2,-10,-20,9,2].find(n => n<0);
console.log(num);// -10//findIndex()找出第一个符合条件的数据成员的索引
let index = [1,2,-10,-20,9,2].findIndex(n => n<0);
console.log(index);// 2
// 5.entries() keys() values() 返回一个遍历器 可以使用for...of循环进行遍历
console.log(['a', 'b'].keys());//Array Iterator {}
//取键名
for(let index of ['a', 'b'].keys()){console.log(index);// 0  1
}
//取值
for(let value of ['a', 'b'].values()){console.log(value);// a  b
}
//取键值对
for(let [index,value] of ['a','b'].entries()){console.log(index,value);// 0 "a"  1 "b"
}
let letter = ['a', 'b', 'c'];
let it = letter.entries();
console.log(it.next().value);//(2) [0, "a"]
console.log(it.next().value);//(2) [1, "b"]
console.log(it.next().value);//(2) [2, "c"]
console.log(it.next().value);//undefined// 6.includes() 返回一个布尔值,表示某个数组是否包含给定的值
console.log([1,2,3].includes(2));//true
console.log([1,2,3].includes('4'));//false
// 之前 indexOf() 返回值:存在是1 不存在是-1

迭代器Interator

Interator是一种新的遍历机制

//1.迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator来创建迭代器,通过迭代器的next()方法获取迭代之后的结果
//2.迭代器是用于遍历数据结构的指针(数据库的游标)//使用迭代
const items = ['one', 'two', 'three'];
// 1.创建新的迭代器
const ite = items[Symbol.iterator]();
console.log(ite.next());//{value: "one", done: false}  done为false表示遍历没有完成,为true表示遍历完成了
console.log(ite.next());//{value: "two", done: false} 
console.log(ite.next());//{value: "three", done: false} 
console.log(ite.next());//{value: undefined, done: true} 

生成器Generator

generator函数,可以通过yield关键字,将函数挂起,为了改变执行流提供了可能,同时为了做异步编程提供了方案

//generator和普通函数的区别
//1.function后面 函数名之前有个*
//2.只能在函数内部使用yield表达式,让函数挂起function* func(){console.log('one');yield 2;console.log('two');yield 3;console.log('end'); 
}
let fn = func();
console.log(fn.next());//one  {value: 2, done: false}
console.log(fn.next());//two  {value: 3, done: false}
console.log(fn.next());//end  {value: undefined, done: true}//总结:generator函数是分段执行的,yield语句是暂停执行,而next()是恢复执行的 function* add(){console.log('start');// x不是yield '2'的返回值,它是next()调用时恢复yield()执行时传入的实参let x = yield '2';console.log('one: ' + x);let y = yield '3';console.log('two: ' + y);return x + y;
}
const fn = add();
console.log(fn.next());//start  {value:'2',done:false}
console.log(fn.next(20));//one: 20  {value:'2',done:false}
console.log(fn.next(30));//two: 30  {value: 50  ,done:false}//利用生成器给没有迭代器接口的对象创建一个迭代器接口
//使用场景:为不具备Interator接口的对象提供了遍历操作
function* objectEntries(obj){//获取对象的所有key保存到数组中  [name,age]const propKeys = Object.keys(obj);for(const propkey of propKeys){yield [propkey, obj[propkey]]}
}
const obj = {name: '张三',age: 20
}
obj[Symbol.iterator] = objectEntries;
console.log(obj);//obj对象现在具备了Symbol.iteratorfor(let [key,value] of objectEntries(obj)){console.log(`${key}:${value}`);
}//另外一种方式
Object.prototype【Symbol.iterator】 = function* gerne() {let keys = Object.keys(this);  //this指向传入实例化的对象本身for (let key of keys) {yield obj【key】;}}
let obj = new Object({
name:"wkf",
age:23
})
for(let 【key,val】 of obj【symbol.iterator】(){
}

Generator的应用

Generator部署ajax操作,让异步代码同步化

<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script>// $.ajax({//     url: 'http://127.0.0.1/users',//     method: 'get',//     success(res) {//         console.log(res);//         $.ajax({//             url: 'http://127.0.0.1/users',//             method: 'get',//             success(res) {//                 console.log(res);//             }//         });//     }// });//解决回调地狱的问题function* main(){let res = yield request('http://127.0.0.1/users');console.log(res);//执行后面的操作console.log('数据请求完成,可以继续操作')}const ite = main();ite.next();function request(url){$.ajax({url:'http://127.0.0.1/users',method: 'get',success(res){ite.next(res);}});}function* load(){loadUI();yield showData();hideUI();}let itLoad = load();itLoad.next();//加载loading...页面//数据加载完成...(异步操作)//loading关闭掉function loadUI(){console.log('加载loading...页面')}function showData(){//模拟异步操作setTimeout(() => {console.log('数据加载完成...');itLoad.next();}, 1000)}function hideUI(){console.log('隐藏loading...页面')}//loadUI();//showData();//hideUI();//加载loading...页面//隐藏loading...页面//数据加载完成...
</script>

Promise 对象

异步编程模块在前端开发中,显得越来越重要。从最开始的XHR到封装后的Ajax都在试图解决异步编程过程中的问题。随着ES6新标准的到来,处理异步数据流又有了新的解决方案。在传统的ajax请求中,当异步请求之间的数据存在依赖关系的时候,就可能产生不优雅的多层回调,俗称”回调地域“(callback hell),这却让人望而生畏,Promise的出现让我们告别回调地域,写出更优雅的异步代码。

回调地狱带来的负面作用有以下几点:

  • 代码臃肿。
  • 可读性差。
  • 耦合度过高,可维护性差。
  • 代码复用性差。
  • 容易滋生 bug。
  • 只能在回调里处理异常。

在实践过程中,却发现Promise并不完美,Async/Await是近年来JavaScript添加的最革命性的的特性之一,Async/Await提供了一种使得异步代码看起来像同步代码的替代方法。接下来我们介绍这两种处理异步编程的方案。

什么是Promise

Promise 是异步编程的一种解决方案:

从语法上讲,Promise是一个对象,通过它可以获取异步操作的消息;

从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。

promise有三种状态pending(等待态),fulfilled(成功态),rejected(失败态)

状态一旦改变,就不会再变。

创造promise实例后,它会立即执行。

在这里插入图片描述

看段习以为常的代码:

// Promise是一个构造函数,自己身上有all,reject,resolve,race方法,原型上有then、catch等方法
let p = new Promise((resolve,reject)=>{// 做一些异步操作setTimeout(()=>{/* 	let res = {ok:1,data:{name:"张三"}} */let res = {ok:0,error:new Error('有错')}if(res.ok === 1){resolve(res.data);}else{reject(res.error.message)}}, 1000)
})
p.then((val)=>{console.log(val);//res.data的值
},(err)=>{console.log(err);//res.error.message的值
})
Promise的状态和值

Promise对象存在以下三种状态

  • Pending(进行中)
  • Fulfilled(已成功)
  • Rejected(已失败)

状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。

Promise的值是指状态改变时传递给回调函数的值

上面例子中的参数为resolve和reject,他们都是函数,用他们可以改变Promise的状态和传入的Promise的值

resolve` 和 `reject
  • resolve : 将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)
  • reject : 将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败)
  • resolvereject 都可以传入任意类型的值作为实参,表示 Promise 对象成功(Fulfilled)和失败(Rejected)的值
then方法
p.then((data)=>{console.log(data);return data;
},(error)=>{console.log(error)
}).then(data=>{console.log(data);
})

promise的then方法返回一个promise对象,所以可以继续链式调用

上述代码我们可以继续改造,因为上述代码不能传参

function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(() => {resolve('hello world')}, ms);})
}
timeout(1000).then((value) => {console.log(value);//hello world
})
then方法的规则
  • then方法下一次的输入需要上一次的输出
  • 如果一个promise执行完后 返回的还是一个promise,会把这个promise 的执行结果,传递给下一次then
  • 如果then中返回的不是Promise对象而是一个普通值,则会将这个结果作为下次then的成功的结果
  • 如果当前then中失败了 会走下一个then的失败
  • 如果返回的是undefined 不管当前是成功还是失败 都会走下一次的成功
  • catch是错误没有处理的情况下才会走
  • then中不写方法则值会穿透,传入下一个then
Promise封装XHR对象(ajax)
const getJSON = function (url) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open('GET', url);xhr.onreadystatechange = handler;xhr.responseType = 'json';xhr.setRequestHeader('Accept', 'application/json');xhr.send();function handler() {console.log(this.readyState);//2  3   4if (this.readyState !== 4) {//4表示数据接收完成return;}if (this.status === 200) {//成功resolve(this.response);} else {reject(new Error(this.statusText));}}})
}
getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976').then((res) => {console.log(res);}, function (error) {console.error(error);})//then方法的链式调用
getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976').then((res)=>{return res.HeWeather6;
}).then((HeWeather6)=>{console.log(HeWeather6);
})
catch方法
//catch(err=>{})`方法等价于`then(null,err=>{})
getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976').then((json) => {console.log(json);
}).then(null,err=>{console.log(err);   
})
//等价于
getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976').then((json) => {console.log(json);
}).catch(err=>{console.log(err);   
})
resove()

resolve()方法将现有对象转换成Promise对象,该实例的状态为fulfilled

let p = Promise.resolve('foo');
//等价于 new Promise(resolve=>resolve('foo'));
p.then((val)=>{console.log(val);//foo
})
reject()

reject()方法返回一个新的Promise实例,该实例的状态为rejected

let p2 = Promise.reject(new Error('出错了'));
//等价于 let p2 = new Promise((resolve,reject)=>reject(new Error('出错了)));
p2.catch(err => {console.log(err);
})
all()方法

all()方法提供了并行执行异步操作的能力,并且再所有异步操作执行完后才执行回调

试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all实现如下

let meInfoPro = new Promise( (resolve, reject)=> {setTimeout(resolve, 500, 'P1');
});
let youInfoPro = new Promise( (resolve, reject)=> {setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([meInfoPro, youInfoPro]).then( (results)=> {console.log(results); // 获得一个Array: ['P1', 'P2']
});
race()方法

有些时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:

let meInfoPro1 = new Promise( (resolve, reject)=> {setTimeout(resolve, 500, 'P1');
});
let meInfoPro2 = new Promise( (resolve, reject)=> {setTimeout(resolve, 600, 'P2');
});
Promise.race([meInfoPro1, meInfoPro2]).then((result)=> {console.log(result); // P1
});

Promise.all接受一个promise对象的数组,待全部完成之后,统一执行success;

Promise.race接受一个包含多个promise对象的数组,只要有一个完成,就执行success

举个更具体的例子,加深对race()方法的理解

当我们请求某个图片资源,会导致时间过长,给用户反馈

用race给某个异步请求设置超时时间,并且在超时后执行相应的操作

function requestImg(imgSrc) {return new Promise((resolve, reject) => {var img = new Image();img.onload = function () {resolve(img);}img.src = imgSrc;});
}
//延时函数,用于给请求计时
function timeout() {return new Promise((resolve, reject) => {setTimeout(() => {reject('图片请求超时');}, 3000);});
}
Promise.race([requestImg('images/2.png'), timeout()]).then((data) => {console.log(data);document.body.appendChild(data);
}).catch((err) => {console.log(err);
}); 

async 函数

异步操作是JavaScript编程的麻烦事,很多人认为async函数是异步编程的解决方案

Async/await介绍
  • async/await是写异步代码的新方式,优于回调函数和Promise。
  • async/await是基于Promise实现的,它不能用于普通的回调函数。
  • async/await与Promise一样,是非阻塞的。
  • async/await使得异步代码看起来像同步代码,再也没有回调函数。但是改变不了JS单线程、异步的本质。(异步代码同步化)
Async/await的使用规则
  • 凡是在前面添加了async的函数在执行后都会自动返回一个Promise对象

    async function test() {}let result = test()
    console.log(result)  //即便代码里test函数什么都没返回,我们依然打出了Promise对象
    
  • await必须在async函数里使用,不能单独使用

    async test() {let result = await Promise.resolve('success')console.log(result)
    }
    test();
    
  • await后面需要跟Promise对象,不然就没有意义,而且await后面的Promise对象不必写then,因为await的作用之一就是获取后面Promise对象成功状态传递出来的参数。

    function fn() {return new Promise((resolve, reject) => {setTimeout(() => {resolve('success')})})
    }async test() {let result = await fn() //因为fn会返回一个Promise对象console.log(result)    //这里会打出Promise成功后传递过来的'success'
    }test();
    
Async/Await的用法
  • 使用await,函数必须用async标识
  • await后面跟的是一个Promise实例
function loadImg(src) {const promise = new Promise(function (resolve, reject) {const img = document.createElement('img')img.onload = function () {resolve(img)}img.onerror = function () {reject('图片加载失败')}img.src = src})return promise
}
const src1 = 'https://hcdn1.luffycity.com/static/frontend/index/banner@2x_1574647618.8112254.png'
const src2 = 'https://hcdn2.luffycity.com/media/frontend/index/%E7%94%BB%E6%9D%BF.png'
const load = async function () {const result1 = await loadImg(src1)console.log(result1)const result2 = await loadImg(src2)console.log(result2)
}
load()
复制代码

当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。

async/await的错误处理

关于错误处理,如规则三所说,await可以直接获取到后面Promise成功状态传递的参数,但是却捕捉不到失败状态。在这里,我们通过给包裹await的async函数添加then/catch方法来解决,因为根据规则一,async函数本身就会返回一个Promise对象。

const load = async function () {try{const result1 = await loadImg(src1)console.log(result1)const result2 = await loadImg(src2)console.log(result2)}catch(err){console.log(err);}
}
load();
//需求:获取和风天气 现在now的数据
const getJSON = function (url) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open('GET', url);xhr.onreadystatechange = handler;xhr.responseType = 'json';xhr.setRequestHeader('Accept', 'application/json');xhr.send();function handler() {console.log(this.readyState);//2  3   4if (this.readyState !== 4) {//4表示数据接收完成return;}if (this.status === 200) {//成功resolve(this.response);} else {reject(new Error(this.statusText));}}})
}async function getNowWeather(url){//发送ajax 获取实况天气let res = await getJSON(url);console.log(res);//获取HeWeather6的数据  获取未来3~7天的天气状况let arr = await res.HeWeather6;return arr[0].now;
}
getNowWeather('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976').then(now=>{console.log(now);
})
为什么Async/Await更好?

Async/Await较Promise有诸多好处,以下介绍其中三种优势:

  • 简洁

    使用Async/Await明显节约了不少代码。我们不需要写.then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码。

  • 中间值

在前端编程中,我们偶尔会遇到这样一个场景:我们需要发送多个请求,而后面请求的发送总是需要依赖上一个请求返回的数据。对于这个问题,我们既可以用的Promise的链式调用来解决,也可以用async/await来解决,然而后者会更简洁些

const makeRequest = () => {return promise1().then(value1 => {return promise2(value1).then(value2 => {        return promise3(value1, value2)})})
}

使用async/await的话,代码会变得异常简单和直观

const makeRequest = async () => {const value1 = await promise1()const value2 = await promise2(value1)return promise3(value1, value2)
}
  • 提高可读性

下面示例中,需要获取数据,然后根据返回数据决定是直接返回,还是继续获取更多的数据。

const makeRequest = () => {return getJSON().then(data => {if (data.needsAnotherRequest) {return makeAnotherRequest(data).then(moreData => {console.log(moreData)return moreData})} else {console.log(data)return data}})
}

代码嵌套(6层)可读性较差,它们传达的意思只是需要将最终结果传递到最外层的Promise。使用async/await编写可以大大地提高可读性:

const makeRequest = async () => {const data = await getJSON()if (data.needsAnotherRequest) {const moreData = await makeAnotherRequest(data);console.log(moreData)return moreData} else {console.log(data)return data    }
}

Class的基本用法

简介

JavaScript语言中,生成实例对象的传统方法是通过构造函数

function Person(name,age) {this.name = name;this.age = age;
}
//原型
Person.prototype.sayName  = function() {return this.name;
}
let p = new Person('小马哥',18);
console.log(p);

上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样

class Person {// constructor方法 是类的默认方法,通过new命令生成对象实例时,自动调用该方法,一个类必须有constructor方法,如果没有定义,会被默认添加constructor(name, age) {this.name = name;this.age = age;}//等同于Person.prototype = function sayName(){}sayName(){return this.name;}
}
console.log(Person===Person.prototype.constructor);

类的方法内部如果含有this,它默认指向类的实例

类的继承
//使用关键字extends
class Animal{constructor(name, age){this.name = name;this.age = age;}sayName(){return this.name;}sayAge(){return this.age;}
}class Dog extends Animal{constructor(name, age, color){super(name, age);//相当于 Animal.call(this,name,age);this.color = color;}sayColor(){return `${this.name}${this.age}岁了,它的颜色是${this.color}`;}sayName(){return this.name = name + super.sayAge() + this.color;}
}
let d1 = new Dog('小黄', 28, 'red');
console.log(d1);
console.log(d1.sayName);

Module 模块化

概述

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

export命令

模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量

//module/index.js
export const name = 'zhangsan ';
export const age = 18;
export const color = 'red ';
export const sayName = function() {console.log(fristName);
}//也可以这样
const name = 'zhangsan ';
const age = 18;
const color = 'red ';
const sayName = function() {console.log(fristName);
}
export {name,age,color,sayName}
import命令

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

//main.js
import {name,age,color,sayName,fn} from './modules/index.js';

如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名

import * as obj from './modules/index.js';
console.log(obj);
export default 命令

使用export default命令为模块指定默认输出

//export-default.js
export default function(){console.log('foo');
}//或者写成
function foo() {console.log('foo');
}export default foo;

在其它模块加载该模块时,import命令可以为该匿名函数指定任意名字

//import-default.js
import customName from './export-default.js'
customNmae();//foo

如果想在一条import语句中,同事输入默认方法和其他接口,可以写成下面这样

import customName,{add} from 'export-default.js'

对应上面export语句如下

//export-default.js
export default function(){console.log('foo');
}export function add(){console.log('add')
}

export default也可以用来输出类。

// MyClass.js
export default class Person{ ... }// main.js
import Person from 'MyClass';
let o = new Person();
nJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。#### export命令模块功能主要由两个命令构成:`export``import``export`命令用于规定模块的对外接口,`import`命令用于输入其他模块提供的功能。一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用`export`关键字输出该变量```javascript
//module/index.js
export const name = 'zhangsan ';
export const age = 18;
export const color = 'red ';
export const sayName = function() {console.log(fristName);
}//也可以这样
const name = 'zhangsan ';
const age = 18;
const color = 'red ';
const sayName = function() {console.log(fristName);
}
export {name,age,color,sayName}
import命令

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

//main.js
import {name,age,color,sayName,fn} from './modules/index.js';

如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名

import * as obj from './modules/index.js';
console.log(obj);
export default 命令

使用export default命令为模块指定默认输出

//export-default.js
export default function(){console.log('foo');
}//或者写成
function foo() {console.log('foo');
}export default foo;

在其它模块加载该模块时,import命令可以为该匿名函数指定任意名字

//import-default.js
import customName from './export-default.js'
customNmae();//foo

如果想在一条import语句中,同事输入默认方法和其他接口,可以写成下面这样

import customName,{add} from 'export-default.js'

对应上面export语句如下

//export-default.js
export default function(){console.log('foo');
}export function add(){console.log('add')
}

export default也可以用来输出类。

// MyClass.js
export default class Person{ ... }// main.js
import Person from 'MyClass';
let o = new Person();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/188431.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【11】使用透视投影建立一个3D空间的测试

核心操作&#xff1a; 1.proj view model 这三个矩阵 glm::mat4 mvp m_Proj * m_View * model; m_Shader->Bind(); m_Shader->SetUniformMat4f("u_MVP", mvp);着色器里面就&#xff1a; proj:投影矩阵&#xff0c;可以选择正交投影&#xff0c;或者透视投影…

javaSE学习笔记(二)数组,类,对象,成员变量,匿名对象,构造方法,static,final,封装,继承,多态

目录 三、面向对象 1.概述 面向过程与面向对象 面向对象编程特点 面向对象三个基本特征 2.数组 数组定义格式 数组的初始化 动态初始化 静态初始化 数组的内存分配 Java中的内存分配 数组的内存分配 数组的角标 数组的基本操作 二维数组&#xff08;实际开发几乎…

【网络编程】网络层——IP协议

文章目录 基本概念路径选择主机和路由器 IP协议格式分片与组装网段划分IP地址的数量限制私网IP地址和公网IP地址深入认识局域网路由 基本概念 TCP作为传输层控制协议&#xff0c;其保证的是数据传输的可靠性和传输效率&#xff0c;但TCP提供的仅仅是数据传输的策略&#xff0c…

通过商品ID获取到京东商品详情页面数据,京东商品详情官方开放平台API接口,京东APP详情接口,可以拿到sku价格,销售价演示案例

淘宝SKU详情接口是指&#xff0c;获取指定商品的SKU的详细信息。SKU是指提供不同的商品参数组合的一个机制&#xff0c;通过不同的SKU来标识商品的不同组合形式&#xff0c;如颜色、尺寸等。SKU详情接口可以帮助开发者获取指定商品的SKU列表&#xff0c;以及每个SKU的属性、库存…

多目标优化框架

随着模型越来越复杂&#xff0c;优化目标越来越多&#xff0c;传统算法都慢慢地无法胜任复杂优化任务&#xff0c;更为智能的优化方法也就应运而生了。其中有一类是进化优化算法&#xff0c;这类算法的思想来源是自然界的“优胜劣汰”法则&#xff0c;通过不停地保留好的个体最…

ubuntu16.04 交叉编译 mbedtls

在为客户交叉编译项目时需要依赖 mbedtls&#xff0c; 客户的机器是 arm64 的 ubuntu 16.04&#xff0c; 交叉编译过程中遇到几个问题。 首先&#xff0c; mbedtls 需要依赖 python, 在 cmake 的过程中&#xff0c; 如果不是使用系统默认的 cmake 可能会导致&#xff0c;mbedt…

Matlab的多项式留数与极点的计算

Matlab的多项式留数与极点的计算 以下面的多项式为例&#xff1a; 运算代码&#xff1a; clc clear closesyms p % 定义多项式 Zp(5*p^571*p^370*p)/(2*p^635*p^4117*p^236); % 提取分子与分母 [I,D]numden(Zp); Idouble(coeffs(I,p,"All"));%分子 Ddouble(coeffs…

【数据结构】单链表OJ题(一)

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 前言一、移除链表元素二、寻找链表中间结点三、输出链表倒数第k个结点四、反转单链表五…

STM32MPU6050角度的读取(STM32驱动MPU6050)

注&#xff1a;文末附STM32驱动MPU6050代码工程链接&#xff0c;需要的读者请自取。 一、MPU6050介绍 MPU6050是一款集成了三轴陀螺仪和三轴加速度计的传感器芯片&#xff0c;由英国飞利浦半导体&#xff08;现为恩智浦半导体&#xff09;公司生产。它通过电子接口&#xff08…

conda环境中pytorch1.2.0版本安装包安装一直失败解决办法!!!

conda环境中pytorch1.2.0版本安装包安装一直失败解决办法 cuda10.0以及cudnn7.4现在以及安装完成&#xff0c;就差torch的安装了&#xff0c;现在torch我要装的是1.2.0版本的&#xff0c;安装包以及下载好了&#xff0c;安装包都是在这个网站里下载的&#xff08;点此进入&…

Postgres的级数生成函数generate_series应用

Postgres的级数生成函数generate_series应用 引用&#xff1a;http://postgres.cn/docs/12/functions-srf.html 函数文档 函数 参数类型 返回类型 描述 generate_series(start, stop) int、bigint或者numeric setof int、setof bigint或者setof numeric&#xff08;与参数类型相…

【React入门实战】实现Todo代办

文章目录 效果功能-状态管理相关接口定义相关方法定义 UIinput输入框&#xff1a;回车添加todo标题列表列表项Main 总体代码 非常简单入门的react-todo练习&#xff0c;代码写的很小白。 效果 技术栈&#xff1a;react-typeScript 数据分为代办Todo和已办完Done&#xff0c;可…

php实现钉钉机器人推送消息和图片内容(完整版)

先来看下实现效果: 代码如下: function send_dingtalk_markdown($webhook , $title , $message "", $atMobiles [], $atUserIds []) {$data ["msgtype" > "markdown","markdown" > ["title" > $title,&quo…

一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?

目录 1解题思路&#xff1a; 2代码如下&#xff1a; 3运行结果&#xff1a; 4总结&#xff1a; 5介绍&#xff1a; 1解题思路&#xff1a; 利用循环&#xff08;穷举法&#xff09;来 对 所 需要的数 进行确定 2代码如下&#xff1a; #include <stdio.h>int main() …

Java自学第9课:JSP基础及内置对象

目录&#xff1a; 目录 1 JSP基础知识架构 1 指令标识 1 Page命令 2 Including指令 3 taglib指令 2 脚本标识 1 JSP表达式 2 声明标识 3 代码片段 3 JSP注释 1 HTML注释 2 带有JSP表达式的注释 3 隐藏注释 4 动态注释 4 动作标识 1 包含文件标识 2 请求转发标…

原文远知行COO张力加盟逐际动力 自动驾驶进入视觉时代?

11月7日&#xff0c;通用足式机器人公司逐际动力LimX Dynamics官宣了两位核心成员的加入。原文远知行COO张力出任逐际动力联合创始人兼COO&#xff0c;香港大学长聘副教授潘佳博士为逐际动力首席科学家。 根据介绍&#xff0c;两位核心成员的加入&#xff0c;证明一家以技术驱…

Stable Diffusion webui 源码调试(三)

Stable Diffusion webui 源码调试&#xff08;三&#xff09; 个人模型主页&#xff1a;LibLibai stable-diffusion-webui 版本&#xff1a;v1.4.1 内容更新随机&#xff0c;看心情调试代码~ shared 变量 shared变量&#xff0c;简直是一锅大杂烩&#xff0c;shared变量存放…

第三届 “鹏城杯”(初赛)

第三届 “鹏城杯”&#xff08;初赛&#xff09; WEB Web-web1 反序列化tostring打Hack类 Payload:O%3A1%3A%22H%22%3A1%3A%7Bs%3A8%3A%22username%22%3BO%3A6%3A%22Hacker%22%3A2%3A%7Bs%3A11%3A%22%00Hacker%00exp%22%3BN%3Bs%3A11%3A%22%00Hacker%00cmd%22%3BN%3B%7D%7D…

Leetcode Hot100之六:42.接雨水

题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 提示&#xff1a; n height.length 1 < n < 2 * 10^4 0 < height[i] < 10^5 思路 暴力循环&#xff1a; 原本的思路是左边界i从左到…

原型制作神器ProtoPie的使用Unity与网页跨端交互

什么是ProtoPie&#xff1f; ProtoPie是一款面向设计师的软件原型设计工具&#xff0c;例如制作App界面交互展示&#xff0c;制作好的原型可以一键发布到Web服务器&#xff0c;就可以浏览器访问。由于其内置了大量常用交互类型&#xff0c;以及"程序化"模块&#xf…