JS中this的用法解析

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>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值