仔细看下,内容如图,你觉得会输出什么?不要再想着直接复制粘贴到浏览器运行了
image.png
答案是
1 2 3 4 5 6 7 8 9 |
1、undefined, funtion arg(){} 2、undefined 3、108 4、undefind 5、我是熊大,年龄77 6、我是熊大,年龄188 7、我是熊大, 年龄88 复制代码 |
why?听我娓娓道来
第一条:demo(18)执行 触发console.log(obj1, arg)
答案undefined, funtion arg(){}
-
obj1 >>> undefined 这里可能大部分人都知道,因为函数的变量提升 -
arg >>> function 是为啥呢?为啥不是 18
这里要引出第一个知识点:预编译
预编译
运行函数demo的前一刻发生了预编译环节
-
第一步,生创建AO(Activation Object)对象:可以理解为demo这个函数拥有的一个冰箱,在执行内部代码时就通过这个冰箱来取东西(变量,函数…) -
第二步,将形参和变量声明当作AO的属性名,值为undefined。 -
第三步,将形参和实参相统一。 -
第四步,在函数体里找函数声明,将值赋为函数体。
实参18
其实是在第三步。而第四步又被函数体所覆盖。所以第一条arg是function…
第二条 执行console.log(length)
答案undefined
。这个不用讲。
第三条 执行console.log(length)
没有疑问吧
答案108
。这里已经执行了赋值length = 108。
第四条 执行if(console.log("undefined") || ....
答案undefined
, 没有想到吧?if判断条件内的也会被浏览器执行。不光是可以执行,而且还没有返回值。所以这个时候回走进后面的或 (!!”” + “1” && typeof typeof null && !!length) 然后进行一系列操作:
-
!!”” + “1” >>> 字符串false1(强制类型转换) -
typeof typeof null >>> typeof “object” >>> 字符串string
第五条 执行obj2.say(光头强,77)
没有疑问吧
答案熊大,77
。 setTimeout 你先等等!!为啥不是光头强呢?因为say的this是obj1呀。
第六条 执行promise.then : obj2.say(光头强,age)
第七条 执行obj1.say(熊三,88)
第六第七条,就是setTimeout 和promise的较量,也就是当然是微任务promise.then先执行了。
总结
最后,讲解更加详细:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
var name = "null"; var age = 38; var length = 10; var say = "说话"; // 2. 运行demo的前一刻发生了预编译环节,预编译发生在函数运行的前一刻。 // - 第一步,生创建AO(Activation Object)对象:可以理解为demo这个函数拥有的一个冰箱,在执行内部代码时就通过这个冰箱来取东西(变量,函数...)。 // AO: { // // } // - 第二步,将形参和变量声明当作AO的属性名,值为undefined。 // AO: { // arg: undefined, // obj1: undefined, // length: undefined, // obj2: undefined, // } // - 第三步,将形参和实参相统一。 // AO: { // arg: 18, // obj1: undefined, // length: undefined, // obj2: undefined, // } // - 第四步,在函数体里找函数声明,将值赋为函数体。 // AO: { // arg: function arg() {}, // obj1: undefined, // length: undefined, // obj2: undefined, // } // 预编译后,开始解释执行demo function demo(arg) { // 3. 上面说到AO就是这个函数的冰箱,在访问变量时首先去AO里面去找,如果没有再沿着上一层的AO找,一直递归下去,所以此时冰箱(AO)里面的obj1为undefined,arg在预编译时已经变成了函数体。 // 所以打印:undefined, function arg() {}。 console.log(obj1, arg); // 4. 这句代码还看不看?当然不看,因为在预编译第四部的时候已经提升了。 function arg() {} // 5. 将obj1的值赋值为代码中的对象。 var obj1 = { name: "熊大", age: 88, say: function () { return (name, age) => { console.log(`我是${this.name}, 年龄${age}`); }; }, }; // 6. 同理,预编译的第二部已经提升了,直接进入下一行。 var length; // 7. 冰箱(AO)里的length是什么?打印:undefined。 console.log(length); // 8. 将AO中的length赋值为108。 length = 108; // 9. 此时AO中的length的值为108,打印108。 console.log(length); // 10. 将obj2赋值为代码中的对象。 var obj2 = { name: "熊二", age: 58, // 14. 仔细看,赋值的say方法是obj1调用后的say,所以方法内的this指向为obj1,并非是obj2。 say: obj1.say(), }; // 11. 开始进入到if,首先判断console.log的返回值,要知道一个函数的返回值是不是要运行它?所以控制台输出字符串类型的undefined,返回值为原始类型undefined。 // || 运算符不成立进入到(!!"" + "1" && typeof typeof null && !!length)。 // !!"" + "1" === 字符串false1(强制类型转换)。 // typeof typeof null === typeof "object" === 字符串string // 判断成立进入到if语句中 if ( console.log("undefined") || (!!"" + "1" && typeof typeof null && !!length) ) { // 12. setTimeout?里面的代码需要看吗?不需要!啥时候执行再看,直接扔到计时器线程去计时吧,时间到了在事件队列里呆着吧,等我啥时候JS引擎空闲了再去执行事件队列中的回调函数。 // 什么?!你还不知道JS中的事件循环?别急,三天内我会出一篇精解版来教你事件循环,不会包赔! setTimeout(() => { // 21. 来吧,可算到我了,在宏队列里都等十年了,开始执行,与15同理,打印熊大,年龄88。至此,队列中的所有回调全部执行完毕。 obj1.say()("熊三", 88); }); // 13. 同步代码,进入到Obj2的say方法。⬆️ // 15. 在了解了obj2的say方法中的this指向后,所以打印什么? // (name, age) => { // console.log(`我是${this.name}, 年龄${age}`); // }; // 打印this.name,this为obj1,所以打印熊大,年龄为传入的age,77。 obj2.say("光头强", 77); } else { setTimeout(() => { obj2.say("肥波", 199); }); } // 16. 进入到Promise。 new Promise((resolve, reject) => { // 17. Promise内的代码都是同步的,如果我在这里加个console.log会立刻执行。resolve传入参数188。 // resolve后Promise的状态变成已决状态。将then放入到微队列中,等待执行。 resolve(188); }).then((age) => { // 20. 先看微队列,与第15步同理,打印熊大,年龄为age,188。微队列中无可执行回调,去看宏队列。⬆️ obj2.say("光头强", age); }); // 18. 发现demo执行完毕,demo的AO出栈。 } // 1. GO预编译完成,执行到demo()后开始执行demo。 demo(18); // 19. GO(Global Object)发现无执行代码,出栈,此时JS执行线程空闲。此时结束了吗? // 并没有,还记得我们上面放入的setTimeout和Promise.then的回调函数吗?他们都在事件列队中的宏/微任务等待执行呢 |
来源于:WEB 前端学习圈