现在我把闭包跟 this 分开来看了,又遇到了些问题,望各位解答

2017-05-22 12:47:47 +08:00
 cheroky

谢谢上一个帖子各位大牛细心解答,是应该把 this 分开看。然后我看 you dont know js,先把 this 搞懂,里面说的 this 的会突然指向 window 的变量,结合楼内大神的答案,其实可以用 window 解释,然后我遇到了回调函数 this 的下面问题:

var name = "global";
var obj = {
	name: "Yes",
	getName: function() {
		console.log(this.name);
	}
}

function doo(f) {
	f(); 
}

doo(obj.getName) //"global"

至少这个例子我可以理解为 obj.getName 的调用点再 doo 函数里,所以 this 应该指向这个,而调用这个函数可以解释为 window.doo(obj.getName),doo 的 this 指向 window,所以最终的 this 指向 window。那如果我用一个有 this 的对象来调用呢。代码如下:

var name = "global";
var obj = {
	name: "Yes",
	getName: function() {
		console.log(this.name);
	}
}

var obj2 = {
	name: "Yes2",
	getName: function(f) {
		f();
	}
}

obj2.getName(obj.getName) //"global"

发现这个 this 还是指向 window,有点想不通。。。

3420 次点击
所在节点    JavaScript
26 条回复
UncleRiver
2017-05-22 12:57:03 +08:00
参考一下这个:
http://stackoverflow.com/questions/7043509/this-inside-object

> Javascript has no block scope, only function scope
whimsySun
2017-05-22 13:08:43 +08:00
函数被传递时没有通过 bind 方法指定 this,this 指向其调用方的上下文,调用方你可以理解为 ${invoke}.${function}. 如果 invoke 没有指定,就是 this 就指向 global。
上面的例子:
调用 obj2.getName 时候 getName 的调用方就是 obj2,this 既 obj2. obj.getName 本传递后,仅做为一个函数传递,使用的时候也没有指定调用方,既 this 为 global

如果你需要 obj2.getName(obj.getName) 输出 “ Yes ”, 通过 obj2.getName(obj.getName.bind(obj))

如果你需要 obj2.getName(obj.getName) 输出 ‘ Yes2 ’, 修改 obj2,`obj2.getName = function(f) { f.apply(this) }`
whimsySun
2017-05-22 13:10:53 +08:00
@UncleRiver 和楼主的例子是两种情况
ericls
2017-05-22 13:11:18 +08:00
@UncleRiver `let` and `const` are block scoped
otakustay
2017-05-22 13:27:09 +08:00
只看调用不看怎么定义和怎么传参,你只要看到 doo 里是 f()(函数调用),那么这个 f 只要没 bind,无论它怎么定义的怎么传进来的,它的 this 都是 global,和 doo 的 this 是啥以及这个 f 怎么来都没关系
cheroky
2017-05-22 13:29:57 +08:00
@whimsySun 感谢热心回答。但是我还有问题是这个函数作为对象的方法被调用,不是应该指向该对象么?而且这个函数既不是全局函数。而且用 obj2 调用,为什么不指向 obj2?
lijsh
2017-05-22 13:33:08 +08:00
@cheroky #6 你只是传 obj.getName 这个方法进去,等价于传 function() {
console.log(this.name);
},传进去就变闭包了,调用的时候就指向全局。
必须 obj.getName() 才是 obj 调用
wodewone
2017-05-22 13:42:24 +08:00
js 执行要看它的运行环境而不是它的初始环境
xilixjd
2017-05-22 13:43:58 +08:00
你确定你看完了 u don't know 吗。。
this 的指向是看在哪调用的,好像有 4 种调用形式
你这个例子跟第一种是一样的
sensui7
2017-05-22 14:11:06 +08:00
> 至少这个例子我可以理解为 obj.getName 的调用点再 doo 函数里...

你理解错了, 这里不存在 obj.getName 的调用, 你只不过是把  obj.getName 赋值给了形式参数f,你调用的是形参f. 形参f是什么, 是函数调用阿.
ChefIsAwesome
2017-05-22 14:13:56 +08:00
别想了,你不用 this 就完事了。
cheroky
2017-05-22 14:51:31 +08:00
@lijsh 试了一下,不知道是不是等同于下面的代码:
var name = "global";

var obj2 = {
name: "Yes2",
getName: function() {
(function() {
console.log(this.name);
})();
}
}
obj2.getName() //"global"
结果是一样的不知道是不是同个原理
bramblex
2017-05-22 15:16:48 +08:00
const f = function () {console.log(this)}

f() // => window / global

const aaa = {}

aaa.f = f

aaa.f() // => aaa

这个你能理解嘛?

f() 等效于 f.apply(window/global)
bramblex
2017-05-22 15:18:10 +08:00
上面的要是你都理解,把他反过来你就不会了?

const aaa = {f: function() {console.log(this)}}

const f = aaa.f

f()
cheroky
2017-05-22 15:32:37 +08:00
@bramblex 这个我能理解,我的理解是
f=aaa.f 相当于 window.f=aaa.f 是吧,调用 window.f(),this 指向 window。问题是我的例子我把函数直接调用,和作为参数传出去再调用,我输出了 this 之后,发现函数作为参数传过去为什么 this 指向了 window,中间有涉及到 window 吗?
whimsySun
2017-05-22 15:41:31 +08:00
@cheroky 不知道你之前是不是学过一些面向对象的语言,收到了影响。js 里面,你可以把函数当成一个单一个体,只是在调用的使用会有归属,不知道这个你能不能理解
Exin
2017-05-22 15:49:54 +08:00
建议楼主把 You dont know js 中的相关章节先仔细完整地阅读,讲的还是蛮清楚的
bramblex
2017-05-22 15:53:25 +08:00
@cheroky

const aaa = {f: function(){console.log(this)}}

下面两条有什么区别吗?

xxx(aaa.f)

===
const f = aaa.f
xxx(f)

如果你觉得上面两条没区别吗? f 只在调用的时候才会去找调用它的对象,传参的时候又没调用。

最后 f = aaa.f 和 window.f = aaa.f 有本质区别的。
Biwood
2017-05-22 15:54:02 +08:00
你这个问题问的挺好的,以后再项目中你会遇到很多次这种情况。

先说原因,obj2.getName(obj.getName) 等价于 obj2.getName(匿名函数),这个 obj.getName 引用的是一个匿名函数,传参的时候,匿名函数被赋值给了形参 f,这一步是关键,形参 f 默认没有绑定任何 Context,你觉得 f 的 Context 是 obj2 吗? f 不是 obj2 的属性,所以显然不是。对于没有手动绑定 Context 的函数,默认 Context 为全局对象,浏览器里面就是 window 对象,严格模式下面会是 undefined。

配合 ES5 里面的 Function.prototype.bind() 的用法,理解起来会容易许多。你可以在 Chrome 浏览器调试栏里面执行如下代码:

var name = "global";
var obj = {
name: "Yes",
getName: function() {
console.log(this.name);
}
}

var obj2 = {
name: "Yes2",
getName: function(f) {
console.dir(f);
f();
}
}

obj2.getName(obj.getName.bind(obj2))

对比一下跟你原来的写法有什么不同,同时看看打印出来的函数对象的具体结构,大概就能理解了
bramblex
2017-05-22 15:56:29 +08:00
@cheroky

楼主你的问题在于,你根本不理解在 js 函数和其他变量是一样的东西,一个函数里面的 this 是不会绑定一个特定的对象的,而是会在 [函数调用的那一刻] 寻找所调用它的对象。

所以你上面的例子,只有到 f() 这个执行,才会去找调用 f 的对象。但是现在没人在调用 f 啊,所以就默认是 window 或者 global

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/362930

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX