this的用法
- 需要关注的是,不同调用位置上的调用形式,决定了this的指向;
独立调用
-
默认绑定规则:this绑定给window;
-
在严格模式下,默认绑定规则会把this绑定undefined上;
function foo() { console.log( this.a ); } var a = 2; (function(){ "use strict"; foo(); //2 })();
-
这里有一个微妙但是非常重要的细节,虽然 this 的绑定规则完全取决于调用位置,但是只有 foo()运行在非 strict mode 下时,默认绑定才能绑定到全局对象; 严格模式下调用foo()不会影响默认绑定规则;
function foo() { "use strict"; console.log( this.a ); } var a = 2; foo(); //undefined
-
无论函数是在哪个作用域中被调用,只要是独立调用则就会按默认绑定规则被绑定到全局对象或者undefined上
隐式调用:(使用对象的属性调用)
隐式绑定
- 隐式绑定的规则:this给离函数最近的对象;是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含;
// 隐式绑定的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含
// 当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象
function foo() {
console.log( this.a );//2
}
var obj = {
a: 2,
foo: foo
};
obj.foo();
- 当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象;
- 对象属性引用链中只有最顶层或者说最后一层会影响调用位置;
//对象属性引用链中只有最顶层或者说最后一层会影响调用位置
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 );//oops, global
}
var a = "oops, global";
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; //把obj.foo赋予别名bar,造成了隐式丢失,因为只是把foo()函数赋给了bar,而bar与obj对象则毫无关系
bar();
//等价于
var a = "oops, global";
var bar = function foo(){
console.log( this.a );
}
bar();//oops, global
- 将函数通过隐式调用的形式进行传参;
var a = 0;
function foo(){
console.log(this.a);
};
function bar(fn){
fn();
}
var obj = {
a : 2,
foo:foo
}
//把obj.foo当作参数传递给bar函数时,有隐式的函数赋值fn=obj.foo。与上例类似,只是把foo函数赋给了fn,而fn与obj对象则毫无关系。
bar(obj.foo);//0
//等价于
var a = 0;
function bar(fn){
fn();
}
bar(function foo(){
console.log(this.a);
});
- 内置函数:内置函数与上例类似,也会造成隐式丢失
var a = 0;
function foo(){
console.log(this.a);
};
var obj = {
a : 2,
foo:foo
}
setTimeout(obj.foo,100);//0
//等价于
var a = 0;
setTimeout(function foo(){
console.log(this.a);
},100);//0
显式绑定
- 通过call()、apply()、bind()方法把对象绑定到this上,叫做显式绑定。
//普通对象的属性查找
function foo(a,b) {
console.log( this.a,a,b );
}
var obj = {
a:2
};
foo.call( obj,"a","b"); //2 a b
foo.apply(obj,["a","b"])//2 a b
- 显式绑定规则:
- 硬绑定:硬绑定是显式绑定的一个变种,使this不能再被修改。
// 我们来看看这个显式绑定变种到底是怎样工作的。我们创建了函数 bar() ,并在它的内部手动调用了 foo.call(obj) ,因此强制把 foo 的 this 绑定到了 obj 。无论之后如何调用函数 bar ,它总会手动在 obj 上调用 foo 。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定。
function foo() {
console.log( this.a );/
}
var a =1;
var obj = {a:2};
var obj_test = {a:"test"};
var bar = function() {
console.log( this.a );
foo.call( obj );};
bar(); // 1 2
setTimeout( bar, 1000 ); // 1 2
bar.call( obj_test ); //test 2
//硬绑定的bar不可能再修改它的this(指的是foo中的this)
// 硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回接收到的所有值
function foo(arg1,arg2) {
console.log( this.a,arg1,arg2);
return this.a + arg1;
}
var obj = {a:2};
var bar = function() {
return foo.apply( obj, arguments);
};
var b = bar(3,2); // 2 3 2
console.log( b ); // 5
new绑定
//3. 这个新对象会绑定到函数调用的 this 。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
//使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this 上。 new 是最
//后一种可以影响函数调用时 this 绑定行为的方法,我们称之为 new 绑定。
绑定例外
- 箭头函数:this的绑定和作用域有关。如果在当前的箭头函数作用域中找不到变量,就像上一级作用域里去找。
function foo() {
setTimeout(() => {
console.log('id:', this.id); //id: 42
}, 100);
}
var id = 21;
foo.call({ id: 42 })
- 被忽略的this:当被绑定的是null,则使用的是默认绑定规则;
// 如果你把 null 或者 undefined 作为 this 的绑定对象传入 call 、 apply 或者 bind ,这些值在调用时会被忽略,实际应用的是默认绑定规则;
function foo() {
console.log( this.a );
}
var a = 2222;
foo.call( null ); // 2
- 柯里化
function foo(a,b) {
console.log( "a:" + a + ", b:" + b );
}
// 把数组“展开”成参数
foo.apply( null, [2, 3] ); // a:2, b:3
// 使用 bind(..) 进行柯里化
var bar = foo.bind( null, [2] );
bar( 3 ); // a:2, b:3
//升级版:不会污染全局
function foo(a,b) {
this
console.log( "a:" + a + ", b:" + b );
}
// 我们的DMZ空对象,“DMZ”(demilitarized zone,非军事区)
var ø = Object.create( null );//{}
// 把数组展开成参数
foo.apply( ø, [2, 3] ); // a:2, b:3
// 使用bind(..)进行柯里化
var bar = foo.bind( ø, 2 );
bar( 3 ); // a:2, b:3
总结
-
this是函数执行的上下文对象;
-
根据函数调用的方式不同this的值也不同:
- 1.以函数的形式调用,this是window
- 2.以方法的形式调用,this是调用方法的对象
- 3.以构造函数的形式调用,this是新创建的那个对象
- 4.使用call和apply调用的函数,第一个参数就是this
- 5.在全局作用域中,this是window
- 6.在响应函数中,给谁绑定的事件this就是谁。
-
在事件处理函数中:获取触发当前事件的元素;
-
在普通函数中(直接调用):获取的是window对象;
-
作为对象的方法中:获取的是当前对象。
-
在全局作用域中:this指代window对象。
-
构造函数中的this:指代创建的对象。
-
注意:
- 当调用方式不同时,this指代的含义不同,得到的结果也不同。
- this本身不具备任何含义。
总结代码示例
<script>
// TODO 一、以全局&调用普通的函数的形式调用,this是window.
function fn(){
console.log(this);
}
fn();
//二、构造函数
//如果函数作为构造函数使用,那么其中的this就代表即将new出来的对象
function Objfn(){
this.a = 10;
console.log(this);//此时输出的是对象 Objfn {a: 10}
}
var objfn = new Objfn();
console.log('objfn.a='+objfn.a);//objfn.a=10
//但是如果直接调用Objfn1()函数,而不是new Objfn1(),那么情况就变成了Objfn()是普通函数
function Objfn1(){
this.a = 10;
console.log(this);//此时输出的是对象 Window {stop: function, open: function, alert: function, confirm: function, prompt: function…}
}
var objfn1 = Objfn1();
//console.log('objfn.a='+objfn1.a);//错误,Cannot read property 'a' of undefined
//三、对象方法
//如果函数作为对象的方法,方法中的this指向该对象
var obj={
a:10,
foo:function () {
console.log(this);//Object {a: 10, foo: function}
console.log(this.a);//10
}
}
obj.foo();
//注意,要是此时在对象方法中定义函数,那么情况就不同了
//此时的函数fn虽然是在 obj1.foo1内部定义的,但它仍然属于一个普通的函数,this仍然指向window.
var obj1={
a1:10,
foo1:function () {
function fn(){
console.log(this);//Window {stop: function, open: function, alert: function, confirm: function, prompt: function…}
console.log(this.a1);//undefined
}
fn();
}
}
obj1.foo1();
//另外,如果此时foo2不作为对象方法被调用,则
var obj2 = {
x: 10,
foo2: function () {
console.log(this); //Window
console.log(this.x); //undefined
}
};
var fn2 = obj2.foo2;
//等价于fn2 = function () {
//console.log(this); //Window
//console.log(this.x); //undefined
//}
fn2();//此时又是在全局里执行的普通函数。
//四、构造函数的prototype属性
//在 Foof.prototype.getX函数中,this 指向的 Foof对象。不仅仅如此,即便是在整个原型链中,this 代表的也是当前Foof对象的值。
function Foof(){
this.x = 10;
}
Foof.prototype.getX = function () {
console.log(this); //Foof {x: 10}
console.log(this.x); //10
}
var foof = new Foof();
foof.getX();
//五、函数用call、apply或者bind调用
var obja = {
x: 10
}
function fooa(){
console.log(this); //Object {x: 10}
console.log(this.x); //10
}
fooa.call(obja);
fooa.apply(obja);
fooa.bind(obja)();
</script>