<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>你不知道的javascript(上卷)</title> </head> <body> <script type="text/javascript"> /* //9、this 的全面解析 this的绑定和函数声明位置没有任何关系,之取决于函数的调用方式 在理解this的绑定过程之前,首先理解调用位置:调用位置就是在代码中被调用的位置(问不是声明位置,)。只有仔细分析调用位置才能回答这个问题:这个this到底引用的是什么 通常来说,寻找调用位置就是寻找“函数被调用的位置” 2.1、函数调用位置 function baz(){ console.log("baz"); bar(); //bar的调用位置 } function bar(){ console.log("bar"); foo();//foo的调用位置 } function foo(){ console.log("foo"); } baz();//baz的调用位置 b、函数调用位置2 function foo(){ console.log(this.a); } var a = 2;//2 foo(); function foo(){ "use strict"; console.log(this.a); //报错,严格模式下,this不会默认绑定给window } var a = 2; foo(); function foo(){ console.log(this.a); } var a = 2; (function(){ "use strict"; foo(); //2 })(); //三种情况下,只要函数执行不是执行在严格模式下,默认绑定才会绑定到全局上 2.2、绑定规则 2.2.1默认绑定 function foo(){ console.log(this.a); } var a = 2; foo();//2 //非严格模式下,this默认绑定在window下 function foo(){ "use strict"; console.log(this.a); } var a = 2; foo();// this is not defined; //严格模式下不能将全局对象默认绑定,因此this会绑定到undefined上 function foo(){ console.log(this.a); } var a = 2; (function(){ "use strict"; foo();//2 })() //严格模式下的调用不影响默认绑定 2.2.2隐式绑定 function foo(){ console.log(this.a); } var obj = { a : 2, foo : foo } obj.foo(); //无论是直接在obj中定义还是先定义在添加为引用属性,这个函数严格来说不属于obj对象, //调用位置会使得obj上下文来引用函数,因此你可以说函数被调用时obj对象“拥有”或者“包含”函数 function foo(){ console.log(this.a); } var obj2 = { a : 42, foo : foo } var obj1 = { a : 2, obj2 : obj2 } obj1.obj2.foo();//42 //对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。 //隐式丢失 function foo(){ console.log(this.a); } var obj = { a : 2, foo : foo } var bar = obj.foo;//函数别名 var a = "oops,global"; bar();//"oops,global"; 2.2.3显示绑定 function foo(){ console.log(this.a); } var obj = { a : 2, } foo.call(obj);//2 //通过foo.call(..),我们可以调用foo时强制把他的this绑定到obj上 //硬绑定 function foo(){ console.log(this.a); } var obj = { a : 2, } var bar = function(){ foo.call(obj); } bar();//2 setTimeout(bar,1000);//2 bar.call(window);//2 //硬绑定的bar不可能在修改他的this //创建函数bar(),并且在内部手动调用foo.call(obj), //因此强制把foo的this绑定到obj,无论后面我们如何调用到bar(), //他总会手动在obj上调用foo,这种绑定是一种显性绑定,因此我们称之为硬绑定 //API调用的‘上下文’ function foo(el){ console.log(el,this.id); //1 awesome 2 awesome 3 awesome } var obj = { id : "awesome", } //调用foo()时把this绑定到obj var arr = [1,2,3].forEach(foo,obj); //console.log(arr); new 绑定 使用new调用函数,会执行以下操作 1、创建(或者说构造)一个全新的对象 2、这个新对象会被执行[[Protoptye]]连接 3、这个新的对象会绑定到函数调用的this 4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新的对象 function foo(a){ this.a = a; } var bar = new foo(2); console.log(bar.a); 2.3优先级 默认绑定 < 隐式绑定 < 显式绑定 < new . . . . 2.4被忽略的this function foo(){ console.log(this.a); } var a = 2; foo.call(null);//2 //call 参数为null或者undefined的时候会默认是全局this 柯里化。。。。 */ /* 8、this词法 var foo = a =>{ console.log(a); } foo(2); //2 //箭头函数,是根据为层(函数或者全局)作用域来决定this var foo1 = function(a){ console.log(a); } foo1(2); // 两种声明是等效的 var obj = { id : "awesome", cool:function coolFn(){ console.log(this.id) } }; var id = "not awesome"; obj.cool(); //awesome setTimeout(obj.cool,100); // not awesome // setTimeout 导致 cool()函数丢失了同this之间的绑定 //解决方案 var obj = { count : 0, cool: function coolFn(){ var self = this; if(self.count<1){ setTimeout(function timer(){ self.count++; console.log("awesome"); },100); } } } obj.cool(); // awesome */ /* //7、动态作用域 function foo(){ console.log(a); } function bar(){ var a = 3; foo(); } var a = 2; bar(); //2 输出结果是2,词法作用域让foo()中的a通过RHS引用到了全局 //作用域中的a,因此会输出2 function foo(){ console.log(a); //a is not defined } function bar(){ var a = 3; foo(); } bar(); function foo(){ var a = 3; console.log(a); //引用的是局部变量a的值 } var a = 1; foo();// 3 */ /* //5、闭包作用域 function foo(){ var a = 2; function bar(){ console.log(a); } return bar; } var baz = foo(); baz(); //2 foo() 的返回值是bar()函数,所以通过baz()可以执行这个函数 function foo(){ var a = 2; function baz(){ console.log(a); } bar(baz); } function bar(fn){ fn(); } foo();//2 foo for(var i = 0;i<=5;i++){ (function(){ var j = i; setTimeout(function time(){ console.log(j); // 每100毫秒输出一个数,分别输出0,1,2,3,4 },j*100); })() } for(var i = 0;i<=5;i++){ (function(j){ setTimeout(function time(){ console.log(j); // 每100毫秒输出一个数,分别输出0,1,2,3,4 },j*100); })(i) } for(var i = 1;i<=5;i++){ (function(){ setTimeout(function time(){ console.log(i); // 每100毫秒输出一个数,分别输出6,6,6,6,6 },i*100); })() } */ /* //4、提升 --先编译在执行 a = 2; var a; console.log(a);//a 因为var声明的变量存在提升 console.log(b); //undefined var声明存在提升,赋值不存在提升,所以b存在但是没赋值 var b = 2; foo(); //函数竟然可以执行,说明函数声明也提升了 function foo(){ console.log(a); //undefined , 声明提升了,不存在赋值提升 var a = 2; } foo(); //1 var foo; function foo(){ console.log(1); } foo = function(){ console.log(2) } //引擎编译如下 function foo(){ console.log(1); } foo(); //1 var foo = function(){ console.log(2) } foo(); //3 //var foo 尽管出现在在function之前,但是由于是重复声明,所以被忽略掉 //函数声明会被提升到普通变量之前 function foo(){ console.log(1); } var foo = function(){ console.log(2); } function foo(){ console.log(3); } foo(); // foo is not a function var a = true; if(a){ function foo(){ console.log("A"); } } else{ function foo(){ console.log("B"); } } */ /* //3.4.3、let和var的区别 for(var i = 0;i<10;i++){ } console.log(i);// 10 for(let j=0;j<10;j++){ } console.log(j);// j is not defined //for循环结束后i并没有被销毁,导致全局污染 */ /* //3.3.2、立即执行函数:抱在一对括号内 var a = 2; (function foo(){ var a = 3; console.log(a); //3 })(); console.log(a);// 2 */ /* //块级作用域,函数作用域 //3.2、规避冲突 */ /* function foo(){ function bar(a){ i = 3; console.log(a+i); } for(var i = 0;i<10;i++){ bar(i*2); } } foo();//死循环,bar中的i覆盖了for循环中的i导致死循环 */ /* //2.2.1、eval function foo(str,a){ eval(str); console.log(a,b); //4 } var b = 2; var result = foo("var b = 3;",1); console.log("result:",result); //undefined???默认严格模式? //书上的结果是1,3因为eval(str)== var b = 3; 读取的是局部变量3 function foo(str){ "use strict"; eval(str); console.log(a); } foo("var a = 2");//a si not defined */ </script> </body> </html>