读you don't konw js 有感之this全面解析

本文默认读者已经对this有了一定了解,如果读者觉得自己对this定义或者this的简单指向还有疑问,请先看我的另一篇文章

this直译过来就是这个,它会被自动定义到所有函数的作用域中,不过这也造成了很多困扰,因为经常很难分辨它到底代表着什么,但由于它自身的功能确实可以提供很多便利,所以程序员不管能不能掌握this,不管会经常报错,还是会乐此不彼的使用它。

那么this到底是什么?其实它是函数在运行时根据一定规则绑定到变量上的,在函数被调动时,会创建一个调用的记录,也被称为执行上下文,这个记录中记录着调用栈(函数的调用次序),函数参数,调用方式等,this是这个记录的一个属性。

简单介绍一下四种绑定机制:默认绑定(函数调用),隐式绑定(方法调用),显式绑定(call,apply调用),new绑定(构造器调用)。这四种机制已经在另一篇文章介绍过了,这里就不再赘述了,我们今天要讨论的是这四种机制的优先级和一些特殊情况。

优先级:

毫无疑问,默认绑定是最低级别的,所以我们就不考虑它了。判断一下显式绑定和隐式绑定

var foo = function foo(){
    console.log(this.a);
}

var obj1 = {
    a: 1,
    foo: foo
};

var obj2 = {
    a:2,
    foo: foo
};

obj1.foo();//1
obj2.foo();//2

obj1.foo().call(obj2);//2
obj2.foo().call(obj1);//1
所以显示绑定比隐式绑定优先级更高。


接下来判断一下new绑定和显示绑定

var foo = function foo( param ){
  this.a = param;
};

var obj1 = {};

var F = foo.bind(obj1);
F(1);
console.log(obj1.a);//1

var f = new F(2);
console.log(obj1.a);//1
console.log(f.a);//2
可以看出在new了一个实例后,其返回的新对象已经被改变了this绑定,但是其实原来的对象并没有被改变。所以最后的结果就是new绑定 显式绑定 隐式绑定 默认绑定。

但是我们的js总是会给我们带来无限惊喜,这些规则也并不是铁律。在使用apply、call时,我们会遇到一种场景:不需要指定this的指向,只是想利用一下它的后面传入的参数,这时我们经常会将null传入作为第一个参数,然而这会导致一个非常不好的后果,这时this其实会指向全局对象,也就是其实会使用默认规则,其实这也好理解,你把它指向设置为null,它不变成null就只能恢复默认设置了。这里我们提供一种解决方案,传入一个空的对象,这样就不会对全局对象产生影响了。

  另外介绍一下软绑定,顾名思义与硬绑定相对,软绑定是指给函数绑定一个非全局变量和undefined的值,保证不会让this恢复默认绑定的同时还给以后灵活改变this绑定留有余地。下面给出软绑定的实现。

        if( !Function.prototype.softBind ){//是否已经存在softBind函数
		Function.prototype.softBind = function(obj){
			//传入要软绑定this的对象以及参数
			var fn = this;//保留原函数的引用,也就是Function
			var curried = [].slice.call( arguments, 1 );//提取出传入参数的非绑定对象部分

			var bound = function(){
				return fn.apply(
					( !this || this === (window || global )) ? obj : this,//如果this对象不为空或者不是全局对象就绑定传入的对象  this由bound的调用方式决定
					curried.concat.apply( curried, arguments )//将初始参数和新参数合并
				);
			};

			bound.prototype = Object.create( fn.prototype );//创建一个继承原函数原型的空对象,并将bound的原型设为它
			return bound;

		}
	}

	function foo(){
		console.log( "name: " + this.name );
	}

	var obj = { name: "obj" },
		obj2 = { name: "obj2" },
		obj3 = { name: "obj3" };

	var fooOBJ = foo.softBind( obj );
	fooOBJ();// obj

	obj2.foo = foo.softBind( obj );
	obj2.foo();//obj2

	fooOBJ.call( obj3 );//obj3

	setTimeout( obj2.foo, 10 );//obj
其实代码思路很简单,就是在call、apply系列函数的基础上加了一个额外的判定功能,判定this是不是默认绑定,如果是默认绑定则启用我们软绑定的对象,其根本目的就是防止this默认绑定,也可以理解为我们手动给this设置了默认绑定。

另外还有一个需要注意的地方是es6中定义的箭头函数,他的this是直接根据外层作用域来决定的。这里就多叙述了,我将在接下来es6专题中讨论它。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值