1.1 运行题

1.1.png

  1. var name = 'window'
  2. var person1 = {
  3. name: 'person1',
  4. foo1: function () { //形成作用域
  5. console.log(this.name)
  6. },
  7. foo2: () => console.log(this.name), //外层作用域是window,对象不属于作用域
  8. foo3: function () {
  9. return function () { //所调用的是哪个函数,call所指向的this对象会有所不同的答案
  10. console.log(this.name)
  11. }
  12. },
  13. foo4: function () {
  14. return () => {
  15. console.log(this.name) //被函数包裹着形成作用域,要是foo4函数里面没有相关name值,就继续向上一级作用域继续找nane变量,所以是找到person1.name 为止
  16. }
  17. }
  18. }
  19. var person2 = { name: 'person2' }
  20. person1.foo1() //person1
  21. person1.foo1.call(person2) //person2
  22. person1.foo2() //window
  23. person1.foo2.call(person2) //window
  24. person1.foo3()() //window
  25. person1.foo3.call(person2)() //window
  26. person1.foo3().call(person2) //person2
  27. person1.foo4()() //person1
  28. person1.foo4.call(person2)() //person2 指向person2对象 其里面有name变量
  29. person1.foo4().call(person2) //person1 与调用函数位置没有关系,与函数所在位置执行上下文有关系

1.2 运行题

1.2.png

  1. var name = 'window'
  2. function Person(name) {
  3. this.name = name
  4. this.foo1 = function () {
  5. console.log(this.name)
  6. },
  7. this.foo2 = () => console.log(this.name),
  8. this.foo3 = function () {
  9. return function () {
  10. console.log(this.name)
  11. }
  12. },
  13. this.foo4 = function () {
  14. return () => {
  15. console.log(this.name)
  16. }
  17. }
  18. }
  19. var person1 = new Person('person1')
  20. var person2 = new Person('person2')
  21. person1.foo1()//person1 .foo1的前面是person1,所以this指向person1对象
  22. person1.foo1.call(person2)//person2 通过call改变this指向成为person2对象
  23. person1.foo2()//person1 箭头函数的this执行上下文
  24. person1.foo2.call(person2)//person1 箭头函数的this不可以直接通过call、bind、apply直接进行修改
  25. person1.foo3()()//window
  26. person1.foo3.call(person2)()//window person1.foo3.call(person2)虽然执行person2的函数,但是返回的仍是function(){console.log(this.name)} this指向的是window作用域
  27. person1.foo3().call(person2)//person2 最后的函数进行调用的时候通过call改变this指向成为person2对象
  28. person1.foo4()()//person1 箭头函数,执行上下文,向上级作用域查找变量
  29. person1.foo4.call(person2)()//person2 第一个()是函数(非箭头函数)时通过call改变this指向成为person2对象,其有name变量
  30. person1.foo4().call(person2)//person1 箭头函数不能直接通过call改变this指向

1.3 运行题

1.3.png

  1. var name = 'window'
  2. function Person(name) {
  3. this.name = name
  4. this.obj = {
  5. name: 'obj',
  6. foo1: function () {
  7. return function () {
  8. console.log(this.name)
  9. }
  10. },
  11. foo2: function () {
  12. return () => {
  13. console.log(this.name)
  14. }
  15. }
  16. }
  17. }
  18. var person1 = new Person('person1')
  19. var person2 = new Person('person2')
  20. person1.obj.foo1()()// window
  21. person1.obj.foo1.call(person2)() //window 第一个()函数调用通过call改变this指向成为person2对象,但返回 function(){console.log(this.name)} 其this指向还是window
  22. person1.obj.foo1().call(person2) //person2 第二个()函数(非箭头函数)的this通过call改变成为person2对象
  23. person1.obj.foo2()()//obj 箭头函数,执行上下文,向上级作用域进行查找
  24. person1.obj.foo2.call(person2)()// person2 第一个()函数(非箭头函数)通过call改变this指向成为person2对象,其里面箭头函数的上级作用域就是person2
  25. person1.obj.foo2().call(person2)//obj 第二个()箭头函数执行,不能直接通过call来改变this指向,其箭头函数执行上下文向上级作用域进行查找

1.4 运行题

1.4.png

运行结果:输出 2
备注:var a = 2 改为 let a = 2,输出为 undefined

2.1 运行题

2.1.png
2.1.1.png

2.2 运行题

2.2.png
2.2.2.png

2.3 运行题

2.3.png

答案依次是: ‘resolve1’ ‘finally’ undefined ‘timer1’ Promise{: undefined}

  1. 注意的知识点:
  2. Promise的状态一旦改变就无法改变
  3. finally不管Promise的状态是resolved还是rejected都会执行,且它的回调函数是接收不到Promise的结果的,所以finally()中的res是一个迷惑项。
  4. 最后一个定时器打印出的p1其实是.finally的返回值,我们知道.finally的返回值如果在没有抛出错误的情况下默认会是上一个Promise的返回值, 而这道题中.finally上一个Promise是.then(),但是这个.then()并没有返回值,所以p1打印出来的Promise的值会是undefined,如果你在定时器的下面加上一个return 1,则值就会变成1

2.4 运行题

2.4.png

答案依次是:(使用的是浏览器chrome v80出现的结果) ‘test start…’ ‘执行testSometing’ ‘promise start…’

‘test end…’

‘testSometing’

‘执行testAsync’

‘promise’

‘hello async’

‘testSometing’ ‘hello async’

  1. //知识点:
  2. //紧跟着await后面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在Promise.then中
  3. //正常情况下,async中的await命令是一个Promise对象,返回该对象的结果。但如果不是Promise对象的话,就会直接返回对应的值,相当于Promise.resolve()
  4. //后面的数字代表出现的顺序
  5. async function testSometing() {
  6. console.log("执行testSometing"); //2
  7. return "testSometing";//5
  8. }
  9. async function testAsync() {
  10. console.log("执行testAsync"); //6
  11. return Promise.resolve("hello async");//8
  12. }
  13. async function test() {
  14. console.log("test start..."); //1
  15. const v1 = await testSometing();
  16. console.log(v1);//进入微任务1 --- 5
  17. const v2 = await testAsync(); //6
  18. console.log(v2); // 进入微任务1-1 --- 8
  19. console.log(v1, v2);// 9
  20. }
  21. test();//从这个开始
  22. var promise = new Promise(resolve => {
  23. console.log("promise start..."); //3
  24. resolve("promise");//进入微任务2 ---7
  25. });
  26. promise.then(val => console.log(val));//7
  27. console.log("test end..."); //4

2.5 运行题

2.5.png

答案依次是: 0 ‘Error: 0’ 1 2 3

  1. Promise.all()的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调。
  2. .race()的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被抛弃。
  3. Promise.all().then()结果中数组的顺序和Promise.all()接收到的数组顺序一致。
  4. allrace传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但并不会影响数组中其它的异步任务的执行。

2.6 运行题

2.6.png

答案依次是: ‘promise1里的内容’ ‘promise1’ Promise{} ‘promise2’ Promise{} ‘timer1’ test5.html:102 Uncaught (in promise) Error: error!!! at test.html:102 ‘timer2’ ‘promise1’ Promise{: “success”} ‘promise2’ Promise{: Error: error!!!}

  1. //后面的数字是执行的顺序
  2. const promise1 = new Promise((resolve, _reject) => {
  3. setTimeout(() => {//宏任务1 ---先执行
  4. resolve("success");//传递给下一then函数执行的命令
  5. console.log("timer1");//4
  6. }, 1000);
  7. console.log("promise1里的内容");//1
  8. });
  9. const promise2 = promise1.then(() => {//等待resolve()的调用才可以执行,微任务1
  10. throw new Error("error!!!");// 直接进行报错 Error: error!!! --- 5
  11. });
  12. console.log("promise1", promise1);//2
  13. console.log("promise2", promise2);//3
  14. setTimeout(() => {//宏任务2
  15. console.log("timer2");//6
  16. console.log("promise1", promise1);//7
  17. console.log("promise2", promise2);//8
  18. }, 2000);

2.7 运行题

2.7.png

答案依次是: 1
finally2
finally
inally2后面的then函数 2

需要注意的 知识点

  • .finally()方法不管Promise对象最后的状态如何都会执行
  • .finally()方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是没法知道Promise最终的状态是resolved还是rejected
  • 它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异常则返回异常的Promise对象。

    3.1 运行题

    3.1.png
    答案是 “” 空字符串
    1. function fn() {
    2. console.dir(this)
    3. console.log(this.name);
    4. }
    5. //...
    此时打印出来的结果
    3.1.1.png
    打开控制台上的window后查找 name 属性:
    3.1.1.name.png

    3.2 运行题

    3.2.png ```javascript var obj = { a: 1, foo: function (b) {
    1. b = b || this.a //前面是undefined和unll的时候取后面的数值
    2. console.log(b)//2 3
    3. return function (c) {
    4. console.log(this.a + b + c)
    5. }
    } } var a = 2 var obj2 = { a: 3 }

//讲解:obj.foo(a[此时实参a的值为2,赋值形参b数值为2]) === 此时的函数 function (c) {console.log(this.a + b + c)} 此时.call(obj2,1) this指向obj2,传入参数1 this.a为3(this指向obj2对象,其属性a值为3) b向上级作用域查找得2 传入参数1赋值给形参c 所以总共是6 obj.foo(a).call(obj2, 1) // 6

//讲解:obj.foo.call(obj2) obj.foo函数的this指向obj2对象 没有传入实参给形参b,此时b为undefined 经过 b=b||this.a 重新赋值为 3(obj2对象里面有a值为3) === function (c) {console.log(this.a + b + c)} 后又传输参数1,此时形参c被赋值为1 b通过上级作用域查找得到值为3 此时函数内的this指向window,所以this.a的值为2 所以总共是6 obj.foo.call(obj2)(1)// 6

  1. <a name="IDkZ2"></a>
  2. ## 3.3 运行题
  3. ![3.3.png](https://cdn.nlark.com/yuque/0/2021/png/466273/1618405095380-08f30128-44e5-4256-9f23-06b57ee5f067.png#height=1044&id=H83GQ&margin=%5Bobject%20Object%5D&name=3.3.png&originHeight=1044&originWidth=928&originalType=binary&ratio=1&size=140481&status=done&style=none&width=928)
  4. ```javascript
  5. var i = 20;
  6. function fn() {
  7. i -= 2; // i = 18
  8. return function (n) {//传入参数1
  9. console.log(++i - n); // 18+1-1=18 此时i值是19
  10. };
  11. }
  12. var f = fn();
  13. f(1);//此时i值是19
  1. function fn() {
  2. // 经过f(1)函数的调用,此时i是19
  3. i -= 2;
  4. return function (n) {// 传入参数 n=2
  5. console.log(++i - n); //19+1-2=18 此时i值是20
  6. };
  7. }
  8. var f = fn();
  9. f(1);
  10. f(2);//直接调用这个函数 function (n) {console.log(++i - n);}
  1. function fn() {
  2. // 经过f(2)函数的调用,此时i是20
  3. i -= 2; //i=18
  4. return function (n) {// 传入参数3
  5. console.log(++i - n); //18+1-3=16 此时i值是19
  6. };
  7. }
  8. var f = fn();
  9. f(1);
  10. f(2);
  11. fn()(3);//此时i值是19
  1. function fn() {
  2. // 经过fn()(3)函数的调用,此时i是19
  3. i -= 2; //i=17
  4. return function (n) {// 传入参数4
  5. console.log(++i - n); //17+1-4=14 此时i值是18
  6. };
  7. }
  8. var f = fn();
  9. f(1);
  10. f(2);
  11. fn()(3);
  12. fn()(4);//此时i值是18
  1. var i = 20;
  2. function fn() {
  3. // 经过fn()(4)函数的调用,此时i是18
  4. i -= 2;
  5. return function (n) {// 传入参数5
  6. console.log(++i - n); //18+1-5=14 此时i值是19
  7. };
  8. }
  9. var f = fn();
  10. f(1);
  11. f(2);
  12. fn()(3);
  13. fn()(4);
  14. f(5);//直接调用function (n) { console.log(++i - n) };
  15. console.log(i);//19
  16. //答案:18 18 16 14 14 (函数内部打印的结果)
  17. // 19(i 最后的值)

3.4 运行题

3.4.png
堆栈内存的问题.png

3.5 运行题

3.5.png

使用 var: 每一次for循环的时候,setTimeout都执行一次, 但是里面的函数没有被执行,而是被放到了任务队列里面,等待执行, for循环了4次,就放了4次,当主线程执行完成后,才进入任务队列里面执行。使用 let:for循环头部的let不仅将i绑定到for循环块中, 它也将其重新绑定到 循环体的每一次迭代 中,确保上一次迭代结束的值重新被赋值。 setTimeout里面的function()属于一个新的域, 通过 var 定义的变量是无法传入到这个函数执行域中的, 而通过使用 let 来声明块变量,这时候变量就能作用于这个块, 所以 function就能使用 i 这个变量了; 这个匿名函数的参数作用域 和 for参数的作用域 不一样。