升入理解this 精华版

写在前面

在前几篇博文中,关于this理解问题,我都有提到。 自认为理解很到位了,结果项目中,经常被this搞蒙头。简单的可能还好,如果函数调用位置不太明确的,只能大概加估计了。再次复习,加深理解。

js的作用域是基于函数的作用域的,在谈到this指向问题时,很多情况下都是结合函数来说的。this到底是什么,是个什么东西。单独的this就是js的一个关键字,无任何函数,函数调用时,他便指向一个对象。在理解问题之前,我们先得理解调用位置,函数调用栈。然后在介绍结合四法则,结合理解this

函数调用位置

调用栈 一种数据结构,可以理解为函数执行时调用的所有函数

调用位置 函数在代码中被调用的地方,包含函数调用的东西,或者说上下文。为什么需要介绍这个,this就是在函数调用时绑定的,需要找到调用位置,才能方便分析出this的指向。

先来看看这段代码

function baz () {
//当前调用栈 : baz
// 当前函数调用位置在 global
	console.log('baz');
	bar();
}
function bar () {
//当前调用栈: bar ---> baz. 从左到右的栈结构
//此时的调用位置在baz中
	console.log( 'bar' );
	foo();
}
function foo () {
//当前调用栈: foo---> bar--->baz
//调用位置: bar
	console.log ('foo');
}

baz() ----> baz调用位置

我这里依照之前的debug结果,注释出结果,就不再细分析。直观的,我们不便分析出调用位置,我们可以结合浏览器或者编辑器debug工具,调试查看函数调用栈。

我们知道this是在函数调用时绑定的(前面已经提过),上面已经理解了调用位置。那么在函数调用时,this是如何绑定的。这里需要重点结合4个法则分析。

四法则 玩转this

默认绑定

函数调用不带任何修饰时,默认绑定到window顶层对象

这个很简单,就是普通函数调用,this一般都是绑定到global.

实列代码

function foo () {
	console.log(this.a)
}
var a = "oop ,blobal!!!";
var obj = {
	a: 'obj'
}
//带修饰的foo调用, 带对象引用的,函数调用
obj.foo()	===> obj
//不带修饰的调用
foo()  ----> global

隐式绑定

函数调用处,是否包含上下文对象。如果函数位置出,拥有上下文对象。那么this则指向它。这里我叫他带修饰的函数调用。如果函数调用位置处带修饰,比如含有对象的引用。此时函数中的this则指向这个对象.

上面实列有说明,结合字面意思的实列很容易理解。重点说说隐式绑定丢失。这个是我这次复习this的主要原因。这个概念主要包括两个小内容。

  1. 赋值时丢失
  2. 隐式赋值丢失

解释:
第一中很常见,就是将对象的方法赋值到对象外面的变量。导致this绑定的对象丢失。应用默认绑定

.......
var a = obj.foo
a() 	// this---> window

第二种发生在函数当作参数传递的时候, 函数当作参数传递,其实也是一个隐式赋值

比如下面这样,传入值得时候,方法内部this得绑定就会丢失去,使用默认绑定 。当然这取决函数是否运行在严格模式下。只有在非严格模式下,才会使用默认邦定。

//定时器传入隐式绑定函数
setTimout(obj.foo)	//这里的foo是obj对象的一个方法,此处省略具体声明

显示绑定

通过js 内置函数 Funtion本身提供的 call , apply 来强行修该 this

实列

var obj = {
	a: '2'
}
foo.call(obj) 

这种方法,用来解决隐式绑定丢失问题。但是目前还没发,需要稍作修改。即使显示绑定变种,

硬绑定

实列

var obj = {
	a: 2
}
// 隐式绑定丢失
var doFoo = obj.foo	//this---> window

//硬绑定
var doFoo = function () {
	foo.call(obj)	// 等价于 ===》 obj.foo
}
doFoo() ----> this--obj

硬绑定: 总的来说是解决了隐式绑定丢失问题,但同时也带来了问题,this固定,不能在修改。

硬绑定使用改进 , bind. 负责接受值,返回

function foo (something) {
	console.log(something, this.a)
	return this.a + something
}

function bind (fn, obj) {
	return function () {
		return fn.apply(obj, arguments);
	} 
}
var obj = {}

var doFoo =  bind(foo,  obj) 
var a= doFoo(3)

es5内置bind
上面的使用方式可以修改一下
var doFoo = foo.bind(obj) 这里只需要传入需要绑定this的对象即可。

new绑定

通常在js opp的时候,会用到。经常会听到程序员调侃,没得对象new一个就完事儿了。说的就是着玩儿。 在js中通过new关键字 构造函数调用,会生成新对象, 这期间构造内的this会指向 新对象

new 构造函数调用发生的事情:
1.创建新对象
2.新对象会链接__proto__
3.新对象会绑定到函数调用的this
4.如果函数调用没有返回对象,那么new 表达式调用会自动返回新对象。

优先级

在前面说的四法则中,在实际的场景中优先级。

顺序是 new 构造-----> call,apply硬绑定 -----> 隐式绑定----> 默认绑定

这里重点说说 硬绑定和 new 构造调用优先级顺序
硬绑定 通过包装函数,包装内层的显示绑定函数调用, 绑定this。这里使用es5的内置bind。他会返回一个含有显示绑定的硬绑定函数。这个外层函数会忽略this的绑定。
new构造 他会修改硬绑定的this指向。以此看来 new优先级高于硬绑定
实列

var obj = {}
function foo(something) {
	this.a = something
}
var doFoo = foo.bind(obj) 
doFoo (3)	//通过硬绑定对obj设置属性
obj.a = 3

//通过new 改变内层this指向
var bar = new doFoo(2) 
//可以看到 obj.a == 3 依旧未变
但是bar.a = 2 说明了new 改变了内部this指向

应用: 分布应用或者柯里化, new 结合bind使用。

绑定列外

null,undefined 在当作call,apply,bind 在作为this绑定对象传入时,会被应用默认绑定

场景
apply,bind在某些情况 下,不需要传入对象作为this绑定。这个时候可以使用null忽略this绑定

function foo (a, b) {
		console.log(a , b)
}
foo.apply(null, [1, 2])
或者
var doFoo = foo.bind(null, 2);
doFoo(3)

使用null作为忽略this。避免不必要this的绑定. 虽然方便,但也存在问题,他会应用默认绑定。导致一系列问题.

安全this
未必免上述情况, 这里介绍一种。更安全忽略this方法

//创建一个空对象,用于this的绑定
var empty = Object.create(null) //这样创建的对象不存在原型关联
//同样是上述面列子,我们可以这样调用foo
foo.apply(empty, [2, 3])
或者
var doFoo = foo.bind(empty, 2)
doFoo(3)

这样应用默认规则

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值