你不知道的javaScript笔记(2)
this和对象原型
this是一个很特别的关键字,被自动定义在所有函数的作用域中
// foo.count 是0,字面理解是错误的
function foo(num) { console.log("foo:"+ num); this.count++; } foo.count = 0; var i; for(i=0;i<10;i++){ if(i>5){ foo(i) } } console.log(foo.count) //0
// 使用词法作用域解决问题
function foo(num) { console.log("foo:"+ num); data.count++; } var data = { count:0 }; var i; for(i=0;i<10;i++){ if(i>5){ foo(i) } } console.log(data.count); // 4
// 用foo标识符来替代this来引用函数对象,回避了this 的问题,完全依赖于变量foo的词法作用域。
function foo(num) { console.log("foo:"+ num); foo.count++; } foo.count = 0 var i; for(i=0;i<10;i++){ if(i>5){ foo(i) } } console.log(foo.count) //4
//强制this 指向foo函数对象
function foo(num) { console.log("foo:"+num); this.count++ } foo.count = 0; var i; for(i=0; i< 10; i++){ if(i>5){ foo.call(foo,i); } } console.log(foo.count) //4
this是在运行是 绑定的,并不是在编写时绑定的,它的上下文取决于函数调用时的各种条件,this的绑定和和函数声明的位置没有任何关系,只取决于函数调用的方式。
this全面解析
调用栈与调用位置
function baz(){ //当前调用栈是:baz // 因此,当前的调用中位置是全局作用域 console.log("baz"); bar(); // <--bar的调用位置 } function bar(){ //当前的调用栈是: baz-> bar // 因此,当前调用位置在baz console.log("bar); foo(); // <-- foo 的调用位置 } function foo(){ //当前的调用栈是: baz-> bar->foo // 因此,当前调用位置在bar console.log("foo"); } baz(); // <-- baz 的调用位置
只有运行在非strict mode 下,默认绑定才能绑定到全局对象。
对象属性引用链中只有最顶层或者说最后一层灰影响调用位置。
function foo() { console.log(this.a); } var obj2 = { a: 42, foo:foo }; var obj1 = { a:2, obj2: obj2 }; obj1.obj2.foo(); // 42
硬绑定的典型应用场景就是创建一个包裹函数,传入所有的函数并返回接收到的所有的值。
function foo(something){ console.log(this.a,something); return this.a + something; }; var obj = { a:2 }; var bar = function() { return foo.apply(obj,arguments); }; var b = bar(3) ; // 2 3 console.log(b) // 5
另一种方法是创建一个i可以重复使用的辅助函数
function foo(something){ console.log(this.a, something); return this.a + something; } // 简单的辅助绑定函数 function bind(fn,obj){ return function(){ return fn.apply(obj,arguments); }; } var obj = { a:2 } var bar = bind(foo,obj); var b = bar(3); // 2 3 console.log(b) // 5
ES5 中提供了内置的方法 Function.prototype.bind, bind(..) 会返回一个硬编码的新函数,它会
把参数设置为this的上下文并调用原始函数。
function foo(something){ console.log(this.a, something); return this.a + something; } var obj = { a:2 } var bar = foo.bind(obj); var b = bar(3); // 3 5 console.log(b) // 5
API 调用的 上下文
function foo(el){ console.log(el,this.id); } var obj = { id: "awesome' } // 调用 foo(..)时把this 绑定到obj [1,2,3].forEach(foo,obj); // 1 awesome 2 awesome 3 awesome
new可以影响函数调用时this 绑定行为的方法。
function foo(a){ this.a = a; } var bar = new foo(2); console.log(bar.a); // 2
判断this
1.函数是否在new 中调用(new 绑定)? 如果是的话this 绑定的是新创建的对象。
var bar = new foo();
2.函数是否通过call , apply (显示绑定) 或者硬绑定调用? 如果是的话,this的绑定时指定的对象。
va bar = foo.call(obj2)
3.函数是否在某个上下文对象中调用(隐式绑定) ? 如果是的话,this 的绑定时在那个上下文。
var bar = obj1.foo()
4.如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象上。
var bar = foo();
软绑定
function foo(){ console.log("name:" + this.name); } var obj = {name: "obj"}, obj2 = {name: "obj2"}, obj3 = {name: "obj3"}, obj3 = {name: "obj3"}; var foo0BJ = foo.softBind(obj); foo0BJ(); // name:obj obj2.foo = foo.softBind(obj); obj2.foo(); // name:obj3 <--看! setTimeout(obj2.foo,10); // name:obj <--- 应用了软绑定