因为 执行foo.count =0 时,确实向函数对象foo中添加了个属性count,但是函数内部的代码this.count中的this并不是指向那个函数对象,若要强制使this指向函数本身,可将foo(i) 改为 foo.call(foo,i) 后面会解释具体原理
第二种:this 指向函数的作用域 这个说法在某种情况下是正确的,在某种情况下是错误的,但是需要明确的是,this在任何情况下都不指向函数的词法作用域
3.this 到底是什么
当一个函数调用时,会创建一个活动记录(执行上下文)。这个记录包含函数在哪里被调用(调用栈),函数的调用方式,传入的参数等信息。this就是这个记录的一个属性,会在函数执行时用到。
this是在运行时绑定的,并不是在编写时绑定的,它的上下文取决于函数调用时的各种条件,this的绑定和函数声明的位置没有任何的关系,只取决于函数的调用方式。
this实际上就是在函数调用时发生的绑定,他指向什么完全取决于函数在哪里被调用
调用位置: 函数在代码中被调用稍微位置(并不是声明的位置),需要分析调用栈(就是为了到达当前执行位置所调用的所有函数)
function baz() { //当前的调用栈是baz //因此当前的调用位置是全局作用域 conlose.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的调用位置我们可以看到this.a被解析成了全局变量a,在本例中,函数调用应用了this的默认绑定,因此this指向全局变量,通过分析调用位置来看foo()是如何调用的,在代码中,foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他的规则
在严格模式下,则不能将全局对象用于默认的绑定,因此this会绑定到undefined
是要函数体处于严格模式,而不是调用位置处于严格模式
要考虑调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含
function foo() { console.log(this.a): } var obj = { a : 2, foo: foo }; obj.foo();//2 //调用位置会使用obj上下文来引用函数,因此可以说函数被调用时,obj对象“拥有”或“包含”函数引用当函数有上下文对象时,隐形绑定规则会把函数调用中的this绑定到这个上下文对象中。因为调用foo()时,this被绑定到obj中,因此this.a和obj.a是一样的
对象属性引用链中只有上一层或者最后一层在调用位置起作用 如:
function foo() { console.log(this.a); } var obj2 = { a:42, foo:foo }; var obj1 = { a:2, obj2:obj2 }; obj1.obj2.foo();//42隐式丢失 :一个常见的this 的绑定问题就是 被隐式绑定的函数会丢失绑定对象,也就是说,他会应用默认绑定,从而把this 绑定到全局对象上或者undefined,取决于是否在严格模式 如:
function foo(){ console.log(this.a); } var obj = { a :2, foo:foo }; var bar = obj.foo;//函数别名 var a = "global"; bar();//"global"虽然bar 是obj 的引用 ,但实际上,它引用的是foo函数本身,因为此时,bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
传入回调函数时,会发生一种更意外的情况 如:
function foo() { console.log(this.a); } function doFoo(fn){ fn(); } var obj = { a :2, foo:foo }; var a = "global"; doFoo(obj.foo);//"global" //参数传递其实就是一种隐式传递,因此我们传入的参数会被隐式赋值,所以结果和上一个一样将参数传入语言内置函数和自己声明的函数 结果一样
call() 和 apply() 方法 : 第一个参数是一个对象,是给this准备,接着在调用函数时,将其绑定到this中,你可以直接指定this 的绑定对象,因此称之为 显示绑定
function foo() { console.log(this.a); } var obj = { a:2 }; foo.call(obj);//2 //通过foo.call() 我们可以在调用foo时强制把它的this 绑定到obj 上call() 与 apply(功能一样) 传参不一样
硬绑定 如:
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); //2 3 console.log(b);//5bind(…)会返回一个硬编码的函数,他会把你指定的参数设置为this 的上下文对象并调用原始函数
先来了解下new操作 用new来调用函数时 会自动执行下面的操作
创建 和 构造 一个全新的对象这个对象会被执行[Prototype] 连接这个对象会被被绑定到函数调用的this如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象 function foo(a) { this.a = a; } var bar = new foo(2); console.log(bar.a);//2 //使用 new 来调用foo(..)时,我们会构造一个新对象并把它绑定到 //foo(..)调用中的this上new绑定 -》 显示绑定 -》 隐式绑定 -》 默认绑定
1.函数是否new绑定,若是,this绑定的是新创建的对象 2.函数是否为显示绑定,若是,this绑定指定的对象 3.函数是否在某个上下文对象中调用隐式绑定?若是,则绑定那个上下文对象 4.都不是的话 默认绑定 严格模式 绑定undefined 非严格模式 绑定到全局对象
当你把null 或者undefine 作为this的绑定对象传入call、apply或者bind,这些值在调用时就会被忽略,实际应用的是默认绑定的规则。
但是如果某个函数确实使用了this,那默认绑定规则会将this绑定到全局对象,这将会导致修改全局对象。
若希望this 指向为空 可以考虑 :
function (a,b) { console.log("a:"+a+",b:" +b); } //我们的DMZ空对象 var k = Object.create(null); //把数组展开成参数 foo.apply(k,[2,3]);//a:2,b:3 //使用bind(..)进行柯里化 var bar = foo.bind(k,2); bar(3); // a:2,b:3有可能无意中创建一个函数的间接引用,这时,调用这个函数会应用默认绑定 如:
function foo() { console.log(this.a); } var a = 2; var o = {a:3,foo:foo}; var p = {a:4}; o.foo();//3 (p.foo = o.foo)();//2 //此赋值表达式的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo //或者o.foo硬绑定会大大降低函数的灵活性,使用硬绑定后就无法使用隐式绑定或者显示绑定来修改this
如果给默认绑定指定一个全局对象和undefined以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式和显示修改this的能力
if(!Function.prototype.softBind){ Function.prototype.softBind = function(obj) { var fn = this; //捕获所有的curried参数 var curried = [].slice.call(arguments,1); var bound = function() { return fn.apply( ( !this || === (window || global))? obj:this, curried.concat.apply(curried,arguments) ); }; bound.prototype = Object.create(fn.prototype); }; }箭头函数 不适用前面所说的this的四条标准规则,而是根据外层(函数或者全局)作用域来决定this。具体来说,箭头函数继承外层函数调用的this绑定(无论this绑定到啥)
function foo() { //返回一个箭头函数 return (a) => { //this 继承来自foo() console.log(this.a); }; } var obj1 = { a:2 }; var obj2 = { a:3 }; var bar = foo.call(obj1); bar.call(obj2);//是2 不是3foo()内部创建的箭头函数会捕获调用时foo()的this,由于foo()的this 绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改(new也不行)