JS基础学习
最近由于换公司了,相应语言有改变。本博客为基础es6的学习
基本语法
var,let,const
三个都是声明变量,var作用域为全局,let为对应的方法块。
var的变量在运行时可能会出现undefined,比如在初始化之前使用,可以重复var变量名相同。
let的变量如果在其作用域之外使用,由于他不存在,就会抛出一个错误,在同一作用域内不能let变量名相同的。
const为一个常量,一旦声明变量,就必须立即初始化,不能留到以后赋值。只在声明所在的块级作用域内有效。const是其引用不能改变,与java的final一致
var的变量在其声明前可以使用原因:js在加载时会发生变量提升,即脚本开始运行时,变量foo
已经存在了,但是没有值。
如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
promise
promise一个异步的操作,并包含回调函数,回调函数都是可选的。Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。并且会将里面的参数传入.then
的回调方法中例如
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(()->{},()->{}),2个函数都是可选的,resolve(表示成功进入到.then的第一个方法里)与reject(代表失败进入到.then的第二个方法里)的参数会带入对应的方法里面去,这一类返回的的都是新的promise,可以使用新的箭头函数
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
finally
方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled
还是rejected
。这表明,finally
方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
promise.all(),相同于CountDownLatch
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。相当于一个同步器类似countdownlatch。p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数(所以一个rejected,则会进入到then,而不会等待其全部运行结束)。
注意,如果作为参数的 Promise 实例,自己定义了catch
方法,那么它一旦被rejected
,并不会触发Promise.all()
的catch
方法。
Promise.race()
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。只要promise之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数
Promise.any()
Promise.any()
方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled
状态,包装实例就会变成fulfilled
状态;如果所有参数实例都变成rejected
状态,包装实例就会变成rejected
状态。
Promise.any()
跟Promise.race()
方法很像,只有一点不同,就是Promise.any()
不会因为某个 Promise 变成rejected
状态而结束,必须等到所有参数 Promise 变成rejected
状态才会结束。
迭代器iterator
迭代器模式,用于提供一致性遍历,提供统一性遍历接口规范,让其只关注于元素的遍历。js中只需在prototype中添加Symbol.iterator方法,并实现next(),next
方法返回一个对象,表示当前数据成员的信息。这个对象具有value
和done
两个属性,value
属性返回当前位置的成员,done
属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next
方法。
对比java中的他将用于判断是否还能继续遍历的hasNext()方法放入了 next()的返回值中,进行判断。
for of的循环语法糖,通过迭代器进行了判断
function Obj(value) {
this.value = value;
this.next = null;
}
Obj.prototype[Symbol.iterator] = function() {
var iterator = { next: next };
var current = this;
function next() {
if (current) {
var value = current.value;
current = current.next;
return { done: false, value: value };
}
return { done: true };
}
return iterator;
}
var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);
one.next = two;
two.next = three;
let iterator = one[Symbol.iterator]();
iterator.next();
iterator.next();
let temp = iterator.next();
console.log(temp);
使用了迭代器的语法
for of 遍历对象时默认调用他的Iterator
扩展运算符(...)也会调用默认的 Iterator 接口
yield*
后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
迭代器中的return()
Js迭代器中next()方法是必须的,而return()方法则是可选的。return()
方法的使用场合是,如果for...of
循环提前退出(通常是因为出错,或者有break
语句),就会调用return()
方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return()
方法。
例如
function readLinesSync(file) {
return {
[Symbol.iterator]() {
return {
next() {
return { done: false };
},
return() {
file.close();
return { done: true };
}
};
},
};
}
基本语法
格式:function <函数名>*
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next() //得到的值 { value: 'hello', done: false }
hw.next() //得到的值 { value: 'world', done: false }
hw.next() //得到的值 { value: 'ending', done: true }
hw.next() //得到的值 { value: 'undefined', done: true }
g.return('foo') // { value: "foo", done: true }
第一次调用next(),Generator 函数开始执行,直到遇到第一个yield
表达式为止(遇到yield就会暂停,调用next继续往下执行)。next
方法返回一个对象,它的value
属性就是当前yield
表达式的值hello
,done
属性的值false
,表示遍历还没有结束。
Generator 函数返回的遍历器对象,还有一个return()
方法,可以返回给定的值,并且终结遍历 Generator 函数。
如果没有yeild这个函数就相当于一个暂缓函数。
调用next时,如果将参数代入进去则会给yield的调用者赋值,没传值的话接受yield则默认是undefined,如下代码与注释所示
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false},因为没有传值则var y = 2 * undefined
a.next() // Object{value:NaN, done:true}
console.log("------");
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false },因为没有传值则var y = 2 * 12
b.next(13) // { value:42, done:true }
Generator.prototype.throw()
可以在函数体外抛出错误,然后在 Generator 函数体内捕获。例如
var g = function* () {
try {
console.log("gogogo ")
yield;
} catch (e) {
console.log('内部捕获', e);
}
};
var i = g();
i.next();
console.log("准备throw ")
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
}
// gogogo
// 准备throw
// 内部捕获 a
// 外部捕获 b
// 抛出了2个错误,一次被generator捕获,一次被外部函数捕获
next()、throw()、return() 的共同点
next()
、throw()
、return()
这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换yield
表达式。
next()
是将yield换成一个值,如果没有传递值则是一个undefined
throw()
是将yield
表达式替换成一个throw
语句
return()
是将yield
表达式替换成一个return
语句
async函数
todo 阅读 https://juejin.cn/post/6844903891021086734
async
函数就是将 Generator 函数的星号(*
)替换成async
,将yield
替换成await
,仅此而已
async
函数对 Generator 函数的改进,如下
- 内置执行器。调用函数只需要
函数名()
即可调用,不需要.next()
async
函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then
方法指定下一步的操作。
await的方法如果reject,则会进入.then()方法,会导致其后的await可能未执行,若想其全部执行可以将会出现await的放入try catch中
例如
const superagent = require('superagent');
const NUM_RETRIES = 3;
async function test() {
let i;
for (i = 0; i < NUM_RETRIES; ++i) {
try {
await superagent.get('http://google.com/this-throws-an-error');
break;
} catch(err) {}
}
console.log(i); // 3
}
test();
多个await
命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
let foo = await getFoo();
let bar = await getBar();
上面代码中,getFoo
和getBar
是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo
完成以后,才会执行getBar
,完全可以让它们同时触发。
下面写法可以让这2个异步的操作同时进行
// 写法一let [foo, bar] = await Promise.all([getFoo(), getBar()]);// 写法二let fooPromise = getFoo();let barPromise = getBar();let foo = await fooPromise;let bar = await barPromise;
一组异步操作,需要按照顺序完成,进行同步获取url链接,或者异步获取示例代码。
// 同步运行async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); }}// 异步运行async function logInOrder(urls) { // 并发读取远程URL const textPromises = urls.map(async url => { const response = await fetch(url); return response.text(); }); // 按次序输出 for (const textPromise of textPromises) { console.log(await textPromise); }}
类
在es的class中必须有构造函数(constructor),如果没有他会默认添加一个无参数的构造函数。
ES6 的类,完全可以看作构造函数的另一种写法。
class Point { // ...}typeof Point // "function"Point === Point.prototype.constructor // true
上面代码表明,类的数据类型就是函数,类本身就指向构造函数。
事实上,类的所有方法都定义在类的prototype
属性上面。因此,在类的实例上面调用方法,其实就是调用原型上的方法。
class B {}const b = new B();b.constructor === B.prototype.constructor // true
constructor()
方法默认返回实例对象(即this
),完全可以指定返回另外一个对象。
在“类”的内部可以使用get
和set
关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为
class MyClass { constructor() { // ... } get prop() { return 'getter'; } set prop(value) { console.log('setter: '+value); }}let inst = new MyClass();inst.prop = 123;// setter: 123inst.prop// 'getter'
采用 Class 表达式,可以写出立即执行的 Class。
let person = new class { constructor(name) { this.name = name; } sayName() { console.log(this.name); }}('张三');person.sayName(); // "张三"
class 的name属性
name
属性总是返回紧跟在class
关键字后面的类名。
class Point {}Point.name // "Point"
static,静态方法
与类绑定,不能通过实例调用,注意,如果静态方法包含this
关键字,这个this
指的是类,而不是实例。父类的静态方法,可以被子类继承。
其也拥有过静态属性,添加static即可
dig 域名看其环境