[TOC] 1、结果是什么?为什么? ``` <script> function Foo() { getName = function () { console.log(1) } return this } Foo.getName = function () { console.log(2) } Foo.prototype.getName = function () { console.log(3) } function getName() { console.log(5) } var getName = function () { console.log(4) } // 运行结果 Foo.getName() // 2 getName() // 4 Foo().getName() // 1 getName() // 1 new Foo.getName() // 2 new Foo().getName() // 3 new new Foo().getName() // 3 /* * 预解析: * function Foo() { getName = function () { console.log(1) } return this } function getName() { console.log(5) } 重名:PK!!! getName = undefined // 没有值 失败了 最终结果就是只有两个函数: Foo、getName * 逐行解读代码: * 函数声明,不会改变值;函数表达式,会改变值; * Foo.getName = function() { console.log(2) } * Foo.prototype.getName = function() { console.log(3) } * getName = function() { console.log(4) } * * Foo.getName() // 2 自身上有停止向上查找 * getName() // 4 * Foo().getName() // 先执行Foo()函数,开启新的一轮预解析、解读代码;再执行getName()函数, * 预解析: * 空 * 逐行解读: * getName = function() { console.log(1) } * this指向的是window,因为Foo是window调用的; * Foo().getName()变形为:window.getName = function() { console.log(1) },全局作用域挂载了getName()方法 * 所以:getName = function() { console.log(4) } 被 window.getName = function() { console.log(1) }覆盖了 * 因此:Foo().getName() 结果就是:1 * getName() // 1 * new Foo.getName() // 点的优先级是最高的,因此变形为:Foo.getName() -> 结果是:2;接下来相当于 new function() { console.log(2) },所以最终结果肯定是:2 * new Foo().getName() // 点的优先级最高,但是更高的是先执行整体,因此变形为:new Foo()先执行 结果为:Foo实例对象,然后再在实例对象上查找getName(),变形为:Foo.getName(),按照实例对象的查找规则,自身上有停止,没有会按照原型链__proto__查找,结果就是:3 * new new Foo().getName() // 变形:new Foo().getName(),运行结果是: 3,然后new function() { console.log(3) },最终结果肯定是:3 * */ // new Foo.getName() 和 new Foo().getName() 不好理解看这里 function Foo() { // this.getName = function() { // console.log(1) // } } Foo.getName = function () { console.log(2) } Foo.prototype.getName = function() { console.log(3) } console.log(new Foo().getName()) </script> ``` ***** 2、运行结果是什么?为什么? ``` <script> var arr = [1,2,3] function test(arr) { arr = [] } test(arr) // 结果 console.log(arr) //1,2,3 /* * 预解析: * arr = undefined * function test(arr) { arr = [] } * 逐行解读代码: * arr = [1,2,3] * function test(arr) { arr = [] } // 函数声明,跳过 * test(arr) // 函数调用,开启新的预解析、解读代码 * 预解析: * arr = undefined * 解读代码: * arr = [] * console.log(arr) // 此arr非函数内部的arr,因为作用域额关系,因此结果就是1,2,3 * */ </script> ``` ***** 3、运行结果是什么?为什么? ``` <script> var a = 1; if (!(b in window)) { // false var b = 2; a+=1 } else { a+=2 } // 结果 console.log(a) // 3 console.log(b) // undefined /* * 预解析: * a = undefined * b = undefined * 逐行解读代码: * a = 1 * !(b in window) // 预解析中有了b,因此b是挂载到window上,所以b in window 为true,然后取反,所以是false,因此不走if,走了else,所以:a += 2,a最终结果就是:3 * console.log(a) // 3 * console.log(b) // undefined * */ </script> ``` ***** 4、运行结果是什么?为什么? ``` <script> var scope = 'global scope' function checkscope() { var scope = 'local scope' function f() { return scope } return f } // 结果 console.log(checkscope()()) // local scope /* * 预解析: * scope = undefined * function checkscope() { var scope = 'local scope'; function f() { return scope }; return f } * 逐行解读代码: * scope = 'global scope' * function checkscope() { var scope = 'local scope'; function f() { return scope }; return f } // 函数声明,跳过 * console.log(checkscope()()) // 分开两次执行:第一次执行,checkscope(),函数调用,开启新一轮的 * 预解析: * scope = undefined * function f() { return scope } * 逐行解读: * scope = 'local scope' * function f() { return scope }; // 函数声明 * return f // 返回出去 * checkscope() // 结果就是:function f() { return scope } * checkscope()() // 第二次执行:相当于:function f() { return scope } () 执行,开启新的预解析、解读代码: * 预解析: * 空 * 逐行解读: * return scope // 预解析后没有,按照作用域链向上级查找,上级的scope = 'local scope',因此最终结果就是:local scope * */ </script> ``` 5、运行结果是什么?为什么? ``` <script> var y = 10 if (!(x in window)) { var x = 10; } else { ++y } //结果 console.log(x) // undefined console.log(y) // 11 /* * 预解析: * y = undefined * x = undefined * 逐行解读代码: * y = 10 * (x in window) // x 在预解析中存在,因此是挂载在window上面的,所以结果是:true;但是!(x in window)结果就是:false,因此,if不执行 * else ++y // 前++在这里,结果是:11 * console.log(x) // undefined * console.log(y) // 11 * */ </script> ``` 6、运行结果是什么?为什么?怎么修改达到预期 ``` for(var i = 0; i< 5; i++) { setTimeout(function () { // 结果 console.log(i) // 5 }, 300) } ~~~ for( var i = 0;i<5;i++) { (function(i){ setTimeout(function () { console.log(i); },1000) })(i); } ~~~~~~ /* * 这个setTimout在for里面是异步执行的,在延迟输出的时候,i的值已经是5了,因此会输出 5,5,5,5,5 * */ for( var i = 0;i<5;i++) { (function(i){ setTimeout(function () { console.log(i); },1000) })(i); } /*得采用闭包的方法,将里面的i值保存下来;结果就是 01234*/ ``` 7、运行结果是什么?为什么? ``` var str1 = 'abc123def456'; var str2 = str1.replace(/\d+/gi, '*') // 结果 console.log(str2) // abc*def* ``` 8、1&&2和1||2的值分别是多少? ``` 1&&2 // 2 1||2 // 1 ``` 9、指出以下代码的区别 ``` function Person() {} var person = Person() var person = new Person() ``` 10、alert(undefined == null)弹出什么 ``` alert(undefined == null) // true ``` 11、以下代码输出的结果是什么 ``` console.log(1 + '2' + '2') console.log(1 ++ '2' + '2') console.log('A' - 'B' + '2') console.log('A' - 'B' + 2) ``` 12、以下代码输出的结果是什么? ``` A:var test = 1; typeof test = 'number' B:var test = 1.2; typeof test = 'float' C:var test = undefined; typeof test = 'undefined' D:var test = {}; typeof test = 'object' E:var test = '4299' - 0; typeof test = 'number' F:var test = null; typeof test = 'object' ``` 13、以下代码的运行结果是 ``` var arr = new Array(2,3,4,5,6) var newArr = 0 for(var i=1;i<arr.length;i++) { newArr+=arr[i] } // 结果 document.write(newArr) // 18 ``` 14、分析下面代码,结果是 ``` var s1 = parseInt('101 小四') document.write(s1) ``` ***** 15、看下列代码输出为何?解释原因 ``` var a = null; alert(typeof a); //object // 解释:null 是一个只有一个值的数据类型,这个值就是 null。表示一个空指针对象,所以用 typeof 检测会 返回”object” ``` ***** 16、看下列代码,输出什么?解释原因 ``` var undefined; undefined == null; // true 1 == true; // true 2 == true; // false 0 == false; // true 0 == ''; // true NaN == NaN; // false [] == false; // true [] == ![]; // true 解释: undefined 与 null 相等,但不恒等(===) 一个是 number 一个是 string 时,会尝试将 string 转换为 number 尝试将 boolean 转换为 number,0 或 1 尝试将 Object 转换成 number 或 string,取决于另外一个对比量的类型 所以,对于 0、空字符串的判断,建议使用 “===” 。“===”会先判断两边的值类型,类型不匹配时 为 false。 ``` ***** 17、看下面的代码,输出什么,foo 的值为什么 ``` var foo = "11"+2-"1"; console.log(foo); console.log(typeof foo); 结果: 执行完后 foo 的值为 111,foo 的类型为 String ``` 18、看代码给答案 ``` var a = new Object(); a.value = 1; b = a; b.value = 2; alert(a.value); 答案:2(考察引用数据类型细节) ``` 19、看程序,写结果 ``` function setN(obj){ obj.name='屌丝'; obj = new Object(); obj.name = '腐女'; }; var per = new Object(); setN(per); alert(per.name); //屌丝 ``` 20、下面输出多少 ``` var o1 = new Object(); var o2 = o1; o2.name = "CSSer"; console.log(o1.name); // CSSer ``` 21、a 输出多少 ~~~ var a = 6; setTimeout(function () { var a = 666; alert(a); // 666 }, 1000); /* * 逐行解读: * a = 6 * 遇到了setTimeout,执行function函数,开启新的预解析、逐行解读 * 预解析: * a = undefined * 逐行解读: * a = 666 * alert(a) // 666,作用域链查找规则:自己有,拿自己的,自己没有找父级的 * */ var a = 6; setTimeout(function () { alert(a); var a = 666; }, 1000); /* * 逐行解读: * a = 6 * 遇到了setTimeout,执行function函数,开启新的预解析、逐行解读 * 预解析: * a = undefined * 逐行解读: * alert(a) // undefined * a = 666 * */ var a = 6; setTimeout(function () { alert(a); var a = 66; }, 1000); a = 666; alert(a); /* * 逐行解读: * a = 6 * 遇到了setTimeout,执行function函数,开启新的预解析、逐行解读 * 预解析: * a = undefined * 逐行解读: * alert(a) // undefined * a = 66 * a = 666 * alert(a) // 666 * * */ /* * 预解析(总): * a = undefined * 逐行解读: * 直接看代码!!! * * */ ~~~ ***** 22、看下列代码输出为何?解释原因 ``` var a; alert(typeof a); // undefined alert(b); // 报错 ``` 解释:Undefined 是一个只有一个值的数据类型,这个值就是“undefined”,在使用 var 声明变量但并 未对其赋值进行初始化时,这个变量的值就是 undefined。而 b 由于未声明将报错。注意未申明的变量和 声明了未赋值的是不一样的 ***** 23、分析代码,得出正确的结果 ``` var a=10, b=20 , c=30; ++a; a++; e=++a + (++b) + (c++) + a++; alert(e); 弹出提示对话框:77 ``` 24、写出函数 DateDemo 的返回结果,系统时间假定为今天 ``` function DateDemo(){ var d, s="今天日期是:"; d = new Date(); s += d.getMonth() +1+ "/"; s += d.getDate() + "/"; s += d.getFullYear(); return s; } 结果:今天日期是:7/21/2016 ``` 25、写出程序运行的结果? ``` for(i=0, j=0; i<10, j<6; i++, j++){ k = i + j;} 结果:10 ``` 26、阅读以下代码,请分析出结果 ``` var arr = new Array(1 ,3 ,5); arr[4]='z'; arr2 = arr.reverse(); arr3 = arr.concat(arr2); alert(arr3); 弹出提示对话框:z,,5,3,1,z,,5,3,1 ``` 27、看题作答 ``` function f1(){ var tmp = 1; this.x = 3; console.log(tmp); //A console.log(this.x); //B } var obj = new f1(); //1 console.log(obj.x) //2 console.log(f1()); //3 这道题让我重新认识了对象和函数,首先看代码(1),这里实例话化了 f1 这个类。相当于执行了 f1 函数。所以这个时候 A 会输出 1, 而 B 这个时候的 this 代表的是 实例化的当前对象 obj B 输出 3.。 代码(2)毋庸置疑会输出 3, 重点 代码(3)首先这里将不再是一个类,它只是一个函数。那么 A 输 出 1,B 呢?这里的 this 代表的其实就是 window 对象,那么 this.x 就是一个全局变量 相当于在外部 的 一个全局变量。所以 B 输出 3。最后代码由于 f 没有返回值那么一个函数如果没返回值的话,将会返回 underfined ,所以答案就是 : 1, 3, 3, 1, 3, underfined 。 ``` 28、下面输出多少 ``` var o1 = new Object(); var o2 = o1; o2.name = "CSSer"; console.log(o1.name); 如果不看答案,你回答真确了的话,那么说明你对 javascript 的数据类型了解的还是比较清楚了。js 中有 两种数据类型,分别是:基本数据类型和引用数据类型(object Array)。对于保存基本类型值的变量, 变量是按值访问的,因为我们操作的是变量实际保存的值。对于保存引用类型值的变量,变量是按引用访 问的,我们操作的是变量值所引用(指向)的对象。答案就清楚了: //CSSer; ``` 29、再来一个 ``` function changeObjectProperty (o) { o.siteUrl = "http://www.csser.com/"; o = new Object(); o.siteUrl = "http://www.popcg.com/"; } var CSSer = new Object(); changeObjectProperty(CSSer); console.log(CSSer.siteUrl); // “http://www.csser.com/” 如果 CSSer 参数是按引用传递的,那么结果应该是"http://www.popcg.com/",但实际结果却仍是 "http://www.csser.com/"。事实是这样的:在函数内部修改了引用类型值的参数,该参数值的原始引用保持不变。我们可以把参数想象成局部变量,当参数被重写时,这个变量引用的就是一个局部变量,局部 变量的生存期仅限于函数执行的过程中,函数执行完毕,局部变量即被销毁以释放内存。 (补充:内部环境可以通过作用域链访问所有的外部环境中的变量对象,但外部环境无法访问内部环境。 每个环境都可以向上搜索作用域链,以查询变量和函数名,反之向下则不能。) ``` 30、a 输出多少? ``` var a = 6; setTimeout(function () { var a = 666; alert(a); // 输出 666, }, 1000); 因为 var a = 666;定义了局部变量 a,并且赋值为 666,根据变量作用域链, 全局变量处在作用域末端,优先访问了局部变量,从而覆盖了全局变量 。 var a = 6; setTimeout(function () { alert(a); // 输出 undefined var a = 666; }, 1000); 因为 var a = 666;定义了局部变量 a,同样覆盖了全局变量,但是在 alert(a);之前 a 并未赋值,所以输出 undefined。 var a = 6; setTimeout(function(){ alert(a); var a = 66; }, 1000); a = 666; alert(a); // 666, undefined; 记住: 异步处理,一切 OK 声明提前 ``` 31、精度问题: JS 精度不能精确到 0.1 所以 。。。。同时存在于值和差值中 ``` var n = 0.3,m = 0.2, i = 0.2, j = 0.1; alert((n - m) == (i - j)); //false alert((n-m) == 0.1); //false alert((i-j)==0.1); //true ``` 32、加减运算 ``` alert('5'+3); //53 string alert('5'+'3'); //53 string alert('5'-3); //2 number alert('5'-'3'); //2 number ``` 33、结果是什么? ``` function foo(){ foo.a = function(){alert(1)}; this.a = function(){alert(2)}; a = function(){alert(3)}; var a = function(){alert(4)}; }; foo.prototype.a = function(){alert(5)}; foo.a = function(){alert(6)}; foo.a(); //6 var obj = new foo(); obj.a(); //2 foo.a(); //1 ``` 34、输出结果 ``` var a = 5; function test(){ a = 0; alert(a); alert(this.a); //没有定义 a 这个属性 var a; alert(a) } test(); // 0, 5, 0 new test(); // 0, undefined, 0 //由于类它自身没有属性 a, 所以是 undefined ``` 35、结果是 ``` var bool = !!2; alert(bool);//true; 双向非操作可以把字符串和数字转换为布尔值。 ``` 36、下面这段代码想要循环输出结果 01234,请问输出结果是否正确,如果不正 确,请说明为什么,并修改循环内的代码使其输出正确结果 ~~~ for(var i=0;i<5;++i){ setTimeout(function(){ console.log(i+''); },100*i); } ~~~ 37、.JavaScript alert(0.4*0.2);结果是多少?和你预期的一样吗?如果不一样该如何 处理? 有误差,应该比准确结果偏大。 原因: java和JavaScript中计算小数运算时,都会先将十进制的小数换算到对应的二进制,一部分小数并不能完整的换算为二进制,这里就出现了第一次的误差 解决方案: 一般我会将小数变为整数来处理。当然原理也是一致先转为整数再计算。 ``` alert(((0.4*10)*(0.2*10))/10); ``` 38、下列 JavaScript 代码执行后,依次 alert 的结果是 ``` (function test(){ var a=b=5; alert(typeof a); alert(typeof b); })(); alert(typeof a); alert(typeof b); 答案:number number undefined number ``` 39、下列 JavaScript 代码执行后,iNum 的值是 ``` var iNum = 0; for(var i = 1; i< 10; i++){ if(i % 5 == 0){ continue; } iNum++; } 答案:8 ``` 40、输出结果是多少? ``` 1) var a; var b = a * 0; if (b == b) { console.log(b * 2 + "2" - 0 + 4); } else { console.log(!b * 2 + "2" - 0 + 4); } 答案:26 2) <script> var a = 1; </script> <script> var a; var b = a * 0; if (b == b) { console.log(b * 2 + "2" - 0 + 4); } else { console.log(!b * 2 + "2" - 0 + 4); } </script> 答案:6 3) var t = 10; function test(t){ var t = t++; }test(t); console.log(t); 答案:10 4) var t = 10; function test(test){ var t = test++; }test(t); console.log(t); 答案:10 6) var t = 10; function test(test){ t = test++; }test(t); console.log(t); 答案:10 7) var t = 10; function test(test){ t = t + test; console.log(t); var t = 3; }test(t); console.log(t); 答案:NaN 10 8) var a; var b = a / 0; if (b == b) { console.log(b * 2 + "2" - 0 + 4); } else { console.log(!b * 2 + "2" - 0 + 4); } 答案:26 9) <script> var a = 1; </script> <script> var a; var b = a / 0; if (b == b) { console.log(b * 2 + "2" + 4); } else { console.log(!b * 2 + "2" + 4); } </script> 答案:Infinity24 ``` 41、下列 JavaScript 代码执行后,运行的结果是 ``` <button id='btn'>点击我</button> var btn = document.getElementById('btn'); var handler = { id: '_eventHandler', exec: function(){ alert(this.id); } } btn.addEventListener('click', handler.exec); 答案:”btn" ``` 42、下列 JavaScript 代码执行后,依次 alert 的结果是 ``` var obj = {proto: {a:1,b:2}}; function F(){}; F.prototype = obj.proto; var f = new F(); obj.proto.c = 3; obj.proto = {a:-1, b:-2}; alert(f.a); alert(f.c); delete F.prototype['a']; alert(f.a); alert(obj.proto.a); 答案: 1 3 undefined -1 ``` 43、下列 JavaScript 代码执行后的效果是 ``` <ul id='list'> <li>item</li> <li>item</li> <li>item</li> <li>item</li> <li>item</li> </ul> var items = document.querySelectorAll('#list>li'); for(var i = 0;i < items.length; i++){ setTimeout(function(){ items[i].style.backgroundColor = '#fee'; }, 5); } 答案:报错,因为 i 一直等于 5,items[i]获取不到元素 ``` 44、下列 JavaScript 代码执行后的 li 元素的数量是 ``` <ul> <li>Item</li> <li></li> <li></li> <li>Item</li> <li>Item</li> </ul> var items = document.getElementsByTagName('li'); for(var i = 0; i< items.length; i++){ if(items[i].innerHTML == ''){ items[i].parentNode.removeChild(items[i]); } } ``` 45、数组和字符串 ``` <script lang="JavaScript" type="text/javascript"> function outPut(s) { document.writeln(s); } var a = "lashou"; var b = a; outPut(b); a = "拉手"; outPut(a); outPut(b); var a_array = [1, 2, 3]; var b_array = a_array; outPut(b_array); a_array[3] = 4; outPut(a_array); outPut(b_array); </script> 输出结果: 答案:lashou 拉手 lashou 1,2,3 1,2,3,4 1,2,3,4 ``` 46、下列控制台都输出什么 ``` 第 1 题: function setName(){ name="张三"; } setName(); console.log(name); 答案:"张三" 第 2 题: //考点:1、变量声明提升 2、变量搜索机制 var a=1; function test(){ console.log(a); var a=1; } test(); 答案:undefined 第 3 题: var b=2; function test2(){ window.b=3; console.log(b); } test2(); 答案:3 第 4 题: c=5;//声明一个全局变量 c function test3(){ window.c=3; console.log(c); //答案:undefined,原因:由于此时的 c 是一个局 部变量 c,并且没有被赋值 var c; console.log(window.c);//答案:3,原因:这里的 c 就是一个全局变量 c } test3(); 第 5 题: var arr = []; arr[0] = 'a'; arr[1] = 'b'; arr[10] = 'c'; alert(arr.length); //答案:11 console.log(arr[5]); //答案:undefined 第 6 题: var a=1; console.log(a++); //答案:1 console.log(++a); //答案:3 第 7 题: console.log(null==undefined); //答案:true console.log("1"==1); //答案:true,因为会将数字 1 先转换为字符串 1 console.log("1"===1); //答案:false,因为数据类型不一致 第 8 题: typeof 1; "number" typeof "hello"; "string" typeof /[0-9]/; "object" typeof {}; "object" typeof null; "object" typeof undefined; "undefined" typeof [1,2,3]; "object" typeof function(){}; //"function" 第 9 题: parseInt(3.14); //3 parseFloat("3asdf"); //3 parseInt("1.23abc456"); parseInt(true);//"true" NaN 第 10 题: //考点:函数声明提前 function bar() { return foo; foo = 10; function foo() {} //var foo = 11; } alert(typeof bar());//"function" 第 11 题: //考点:函数声明提前 var foo = 1; function bar() { foo = 10; return; function foo() {} } bar(); alert(foo);//答案:1 第 12 题: console.log(a);//是一个函数 var a = 3; function a(){} console.log(a);////3 第 13 题: //考点:对 arguments 的操作 function foo(a) { arguments[0] = 2; alert(a);//答案:2,因为:a、arguments 是对实参的访问,b、通过 arguments[i]可以修 改指定实参的值 } foo(1); 第 14 题: function foo(a) { alert(arguments.length);//答案:3,因为 arguments 是对实参的访问 } foo(1, 2, 3); 第 15 题 bar();//报错 var foo = function bar(name) { console.log("hello"+name); console.log(bar); }; //alert(typeof bar); foo("world");//"hello" console.log(bar);//undefined console.log(foo.toString()); bar();//报错 第 16 题: function test(){ console.log("test 函数"); } setTimeout(function(){ console.log("定时器回调函数"); }, 0) test(); 结果: test 函数 定时器回调函数 ``` 47、对作用域上下文和 this 的理解,看下列代码: ``` var User = { count: 1, getCount: function() { return this.count; } }; console.log(User.getCount()); // what? var func = User.getCount; console.log(func()); // what? 问两处 console 输出什么?为什么? 答案是 1 和 undefined。 func 是在 winodw 的上下文中被执行的,所以会访问不到 count 属性。 继续追问,那么如何确保 Uesr 总是能访问到 func 的上下文,即正确返回 1。正确的方法是使用 Function.prototype.bind。兼容各个浏览器完整代码如下: Function.prototype.bind = Function.prototype.bind || function(context){ var self = this; return function(){ return self.apply(context, arguments); }; } var func = User.getCount.bind(User); console.log(func()); ``` 48、运行结果 ~~~ console.log(a) var a = 12 function fn() { console.log(a) var a = 13 } fn() console.log(a) /* * 预解析: * a = undefined * function fn() { console.log(a); var a = 13 } * 逐行解读代码: * console.log(a) // undefined * var a = 12 * function fn() { console.log(a); var a = 13 } // 函数声明,不执行跳过 * fn() // 函数调用 * 预解析: * a = undefined * 逐行解读代码: * console.log(a) // undefined * * console.log(12) // 12 * */ ~~~ 49、运行结果 ~~~ console.log(a) var a = 12 function fn() { console.log(a) a = 13 } fn() console.log(a) /* * 预解析: * a = undefined * function fn() { console.log(a); a = 13 } * 逐行解读代码: * consosle.log(a) // undefined * var a = 12 * function fn() { console.log(a); a = 13 } // 不执行,因为是函数声明 * fn() // 这个函数调用,开启新的局部作用域解析机制 * 预解析: * 空 // 因为没有var、参数、function、this,预解析找的就是这些东西 * 逐行解读代码: * console.log(a) // 12 * a = 13 * console.log(a) // 13 * * */ ~~~ 50、运行结果 ~ O(∩_∩)O哈哈~ ~~~ console.log(a) a = 12 function fn() { console.log(a) a = 13 } fn() console.log(a) /* * 实际情况是下面这样: * 预解析: * function fn() { console.log(a); a = 13 } * 逐行解读代码: * console.log(a) // a is not defined,程序报错后续代码不执行 * */ /* * 把第一行注释掉就是下面的结果 * 预解析: * function fn() { console.log(a); a = 13 } * 逐行解读代码: * console.log(a) // a is not defined * a = 12 * function fn() { console.log(a); a = 13 } // 函数声明,不理会它,跳过 * fn() // 函数执行,开启新的局部预解析、逐行解读代码 * 预解析: * 空 * 逐行解读代码: * console.log(a) // 12 * a = 13 * console.log(a) // 13 * */ ~~~ 51、运行结果: ~~~ var foo = 1 function bar() { if (!foo) { var foo = 10 } console.log(foo) } bar() /* * 预解析: * foo = undefined * function bar() { if(!foo) { var foo = 100 } console.log(100) } * 逐行解读代码: * foo = 1 * function bar() { if(!foo) { var foo = 100 } console.log(100) } // 函数声明,不理会它,跳过 * bar() // 函数执行,开启新的局部解析作用域 * 预解析: * foo = undefined * 逐行解读代码: * if(!foo) { var foo = 100 } // 此时foo = undefined, undefined取反,不就是真么,所以条件成立 * foo = 10 * console.log(10) // 最终结果就是:10 * */ ~~~ 52、运行结果: ~~~ var n = 0 function a() { var n = 10 function b() { n++ console.log(n) } b() return b } var c = a() c() console.log(n) /* * 预解析: * n = undefined * function a() { var n = 10 function b() { n++ console.log(n) } b() return b } c = undefined 逐行解读代码: n = 0 c = a() c() // 执行a开启新的局部作用域 * 预解析: * n = undefined * function b() { n++; console.log(n) } * 逐行解读代码: * n = 10 * function b() { n++; console.log(n) } // 函数声明 跳过 不理它 * b() // 函数调用,开启新的局部作用域: * 预解析: * 空 * 逐行解读代码: * n++ // 按照作用域链线上及查找 * console.log(n) // 11 * return b // n++; console.log(n) c() 会导致B()函数的里面的n再次++,因此结果就是:n = 12 console.log(n) // 但是外部作用域无法访问内部的变量,结果就是:0 * * 最终结果就是:11 12 0 * * */ ~~~ 53、运行结果 ~~~ var a = 10, b = 11, c = 12 function test(a) { a = 1 var b = 2 c = 3 } test(10) console.log(a) console.log(b) console.log(c) /* * 预解析: * a = undefined * b = undefined * c = undefined * function test(a) { a = 1; var b = 2; c = 3 } * 逐行解读代码: * a = 10 * b = 11 * c = 12 * function test(a) { a = 1; var b = 2; c = 3 } // 跳过,不理会,因为是函数声明 * test(10) // 函数执行,开启新的作用域解析: * 预解析: * a = undefined * b = undefined * 逐行解读代码: * a = 10 // 实参传递进来的 * b = 2 // 私有的 * c = 3 // 全局的 * console.log(a) // 10 * console.log(b) // 11 * console.log(c) // 3 * */ ~~~ 54、分析运行结果 ~~~ if (!("a" in window)) { var a = 1 } console.log(a) /* * 预解析: * a = undefined * 在全局声明的变量相当于挂载到了window上面,因此window上面此时就存在了a变量 * 逐行解读代码: * if语句不成立,所以不进入 * console.log(a) // undefined * */ ~~~ 55、分析运行结果 ~~~ // 'use strict' var a = 4 function b(x, y, a) { console.log(a) arguments[2] = 10 console.log(a) } a = b(1, 2, 3) console.log(a) /* * 预解析: * a = undefined * function b(x, y, a) { console.log(a) arguments[2] = 10 console.log(a) } 逐行解读代码: a = 4 function b() {} // 函数声明,跳过,不执行 a = b(1, 2, 3) // 函数调用,执行代码,开启新的局部作用域: * 预解析: * x = undefined * y = undefined * a = undefined * 逐行解读代码: * console.log(a) // 3 * arguments[2] = 10 -> a = 10 * console.log(a) // 10 a = b(1, 2, 3) // 把b函数的返回值给我到a变量,而此时b没有返回值,所以 a = undefined console.log(a) // 结果就是:undefined * * * */ /* * 在JS的非严格模式下,函数的实参集合与形参变量存在映射关系,不管其中一个谁改变了,另外一个都会跟着改变 * function fn(a) { * // a = 10 * arguments[0] = 100 * // a = 100 * } * fn(10) * * 在JS严格模式下,arguments和形参变量的映射关系就被切断了,相互之间互不干扰 * */ ~~~ 56、分析结果: ~~~ var foo = 'hello'; (function (foo) { console.log(foo) var foo = foo || 'world' console.log(foo) })(foo) // 把全局foo的值当做实参传递给私有作用域中的形参 console.log(foo) /* * 预解析: * foo = undefined * 逐行解读代码: * foo = 'hello' * 自执行函数执行,开启函数内部的解析 * 预解析: * foo = undefined // 注意:这里的foo和外部全局的foo,不是同一个 * 逐行解读代码: * console.log(foo) // hello - 外部传递进来的数据是hello,虽然此foo非彼foo但是值是同一个 * var foo = foo || 'world' // * console.log(foo) // hello * console.log(foo) // hello * * * * 逻辑&& 与 逻辑|| * 在&&和||一起使用的时候: * &&的优先级高于|| * 在条件判断中: * &&:所有条件都为真,整体才为真 * ||:只要有一个条件为真,整体就为真 * 在赋值操作中: * ||:A||B,首先看A的真假,A为真返回的是A的值,A为假返回的是B的值(不管B是啥) * 1||2 => 1 * 0||false => false * &&:A&&B,首先看A的真假,A为假返回A的值,A为真返回B的值 * 1&&2 => 2 * 0&&false => 0 * // 在实际中的应用 function fn(num, callback) { // 如果num没有传值,让其默认为0 num = num || 0 // 这种做法不太严谨 callback && callback() // 如果传了就执行,如果不传就不执行 } * // 调用 fn(1, function() {}) * */ ~~~ 57、分析结果: ~~~ console.log(0||2&&false||3) /* * 按照运算符优先级来看的话: * 先计算:2 && false * 结果是:2 为真 返回 false * 接下来就是: * 0 || false || 3 对比 * 结果:0 为假 返回 false * false || 3 * 结果:false 为假 返回 3 * 最终结果就是:3 * * */ ~~~ ***** 预解析规则: 1,预解析:找东西通过var、function、参数、this 2,逐行解读代码,遇到表达式就会改变原来预解析仓库中的内容,如果遇到函数调用就开了一个新的 作用域,重新开始预解析、逐行解读代码;如果在函数作用于内部没有通过var、function、参数找到任何东西,那么就会返回到父级继续查找,这就是作用域链的过程 58、分析结果 ~~~ console.log(a); // undefined var a = 1; ~~~ 59、分析结果 ~~~ console.log(a); // a is not defined a = 1; ~~~ 60、分析结果 ~~~ alert(a); var a = 1; function fn() { alert(2) }; 解析顺序: 1,找一些东西:var、function、参数(先把需要的东西找齐了才去逐行解读代码) 根据var找到a = 未定义(为什么不去读取呢?我们有理由相信一个偷懒机制,万一a = \[\]或{}那默认上来读取的数据量就比较庞大),所有的变量在正式运行代码之前都会提前赋值:未定义 根据function找到fn = function fn() {alert(2)}(所有函数,在正式运行代码之前都是整个函数块) 我们把这个步骤称为:JS的预解析 2,逐行解读代码 当逐行解读代码的时候,每次都要去仓库里看一眼,看看里面有没有现在遇到的东西:当读取到a的时候不会往下继续读取而是先回到仓库里面看看有没有a,我们仓库中现在有一个a = 未定义,所以alert(a)的结果就是undefined。 ~~~ 61、分析结果 ~~~ console.log(a); // a is not defined a = 1; 解析顺序: 1,整个代码片段一看没有var,没有函数,没有参数,所以以上代码预解析完成后仓库里面什么都没有 2,仓库里面什么都没有的情况下再逐行解读代码就会出现a is not defined ~~~ 62、分析结果 ~~~ // 示例: 逐行解读代码 alert(a); // function a() {alert(4)} var a = 1; alert(a); // 1 function a() { alert(2); }; alert(a); // 1 var a = 3; alert(a); // 3 function a() { alert(4); }; alert(a); // 3 // 解析过程 1,预解析:var function 参数 仓库中的值: a = undefined a = function a() {alert(2)} 注意:在预解析的过程中,如果遇到重名的只会留下一个,一个是a = undefined另一个是a = function a(0 {alert(2)},这个和上下文没有关系,主要是看有没有值,所以现在仓库中的值发生了变化。 仓库中的值: a = function a() {alert(2)} var a = 3;这行的时候又会把a提取出来a = undefined,没有值所以: 仓库中的值: a = function a() {alert(2)} function a() {alert(4)};这行的时候会把a = function a(){alert(4)}提取出来,这个时候仓库中的值为: a = function a() {alert(2)} a = function a() {alert(4)} 这两个PK才会遵循上下文的顺序 仓库中的值: a = function a() {alert(4)} 预解析完成!!! 仓库中的值为: a = function a() {alert(4)} 2,逐行解读代码: 表达式:=+-\*/%++--!,表达式可以修改预解析的值 第一个alert结果:function a() {alert(4)} 表达式是可以修改仓库中的值所以 第二个alert结果:1 函数声明不能修改预解析的值所以 第三个alert结果:1 第四个alert结果:3 第五个alert结果:3 扩展:如果我在本实例最下面添加a();结果是什么? // 示例: 逐行解读代码 alert(a); // function a() {alert(4)} var a = 1; alert(a); // 1 function a() { alert(2); }; alert(a); // 1 var a = 3; alert(a); // 3 function a() { alert(4); }; alert(a); // 3 a(); //a is not defined 解答: alert(typeof a) //number 因为a这时候等于3,相当于3();JS 中有这样的语法吗?没有所以报错了 ~~~ 63、分析结果 ~~~ var a = 1; function fn() { alert(a); // undefined var a = 2; } fn(); alert(a); // 1 1,预解析: a = undefined fn = function fn() {alert(a);var a = 2;} 2,逐行解读代码: 第一行:var a = 1;由于是表达式可以改变值,所以a得值变为了: a = 1; 第二行:function fn() {alert(a);var a = 2;},由于这是函数声明不会改变值,所以不做任何修改,继续往下走 第三行:fn();这个时候遇到了函数调用,那么就会往这个函数里面走,函数呢也是一个域,所以就会执行预解析、逐行解读代码: 1,fn()函数局部预解析: 由于遇到了var a = 2;所以预解析的结果为:a = undefined 2,fn()含数局部逐行解析代码: 第一行:alert(a);由于预解析a = undefined此时和外面的a没有半毛钱关系,所以结果就是undefined 第二行:var a = 2;由于是表达式所以会改变函数作用于中a的值 所以a = 2 fn()函数调用已走完 第四行:alert(a);这里会查找全局a = 1;所以结果就是1 ~~~ 64、分析结果 ~~~ var a = 1; function fn() { alert(a); a = 2; } fn(); alert(a); // 解析过程 1,预解析: a = undefined fn = function fn () {alert(a); a = 2;} 2,逐行解读代码: a = 1; function fn () {alert(a); a = 2;},函数声明不做修改 fn(); 函数调用,掉头执行function fn () {alert(a); a = 2;},函数作用于执行: 1,预解析: 没有找到var function 参数 2,逐行解读代码: alert(a)内部没又找到,查找外层a = 1(作用域链);所以alert(a)结果就是:1 a = 2; 第二个:alert(a); // 2 ~~~ 65、解析过程 ~~~ var a = 1; function fn(a) { alert(a); a = 2; } fn(); alert(a); // 解析过程 1,预解析: a = undefined fn = function fn(a) {alert(a); a = 2} 2,逐行解读代码 a = 1; fn()函数调用开始新的作用域: 1,预解析: 没有var,没有function,找到了参数a,参数本质上就是局部变量 a = undefined; 2,逐行解读代码: 第一个alert(a): undefined a = 2;(修改的是局部的a,并非全局的a) 第二个alert(a): 1 ~~~ 66、解析过程 ~~~ var a = 1; function fn(a) { alert(a); a = 2; } fn(a); alert(a); // 解析过程 1,预解析: a = undefined fn = function fn (a) {aler(a); a= 2}; 2,逐行解读代码 a = 1; fn(a) 函数调用: 1,预解析(var、function、参数): a = undefined 2,逐行解读代码: 传递了实参:a = 1 alert(a) //1 a = 2; alert(a)(全局的a) // 1 ~~~