JS基础学习

最近由于换公司了,相应语言有改变。本博客为基础es6的学习

基本语法

var,let,const

三个都是声明变量,var作用域为全局,let为对应的方法块。

var的变量在运行时可能会出现undefined,比如在初始化之前使用,可以重复var变量名相同。

let的变量如果在其作用域之外使用,由于他不存在,就会抛出一个错误,在同一作用域内不能let变量名相同的。

const为一个常量,一旦声明变量,就必须立即初始化,不能留到以后赋值。只在声明所在的块级作用域内有效。const是其引用不能改变,与java的final一致

var的变量在其声明前可以使用原因:js在加载时会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值。

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

promise

promise一个异步的操作,并包含回调函数,回调函数都是可选的。Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。并且会将里面的参数传入.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的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成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方法返回一个对象,表示当前数据成员的信息。这个对象具有valuedone两个属性,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表达式的值hellodone属性的值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 函数的改进,如下

  1. 内置执行器。调用函数只需要函数名()即可调用,不需要.next()
  2. 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();

上面代码中,getFoogetBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有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),完全可以指定返回另外一个对象。

在“类”的内部可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为

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 域名看其环境