javascript - 解释在 Javascript ES6 中调用对象方法的函数中“this”的使用
问题描述
我正在尝试处理一些 json 并将数据片段写入几个文件。
所以我有一个基于 json 数据和一些文字创建文件夹/文件的功能。文件名由包含不同类别的对象定义:
const folders = {
category1: {
fileName: 'default',
path : '/path',
subpath : () => `/subpath/${this.fileName}${this.uniqueId}`
}
}
这就是你们中的一半人会跳到前面告诉我箭头函数看不到this
它们自己的对象等的地方。我知道这是故意的,因为我稍后会得到必要的数据。
main 函数遵循以下模式:
function readSave() {
//suppose readFileSync would return a string 'filename' and an int 1029
this.fileName = 'filename';
this.uniqueId = 1029;
let filePath = folders.category1.path + folders.category1.subpath();
// I'd go on to write stuff to that file but for now let's just return
return filePath;
}
readSave()
// returns '/path/subpath/undefinedundefined'
// expected '/path/subpath/filename1029'
我也知道我可以作为参数传递fileName
,uniqueId
但这不是重点。这篇文章并不完全是试图找到解决方案,而是要了解它为什么不起作用。
这里的混乱是this
inside的使用readSave
。根据MDN this
内部,常规函数与调用函数的对象相同。由于我在节点上的普通 js 文件上调用它,所以它是global
.
目前一切都很好。如果我检查函数调用中的运行,this
并且global
属性设置没有问题。
问题是 folders.category1.subpath()
评估为undefined
。
调试器显示何时进行评估() => /subpath/${this.fileName}${this.uniqueId}
,this
不再是global
,而是一个空对象。这个文档让我认为箭头函数应该继承this
它被调用的范围,即readSave
,这意味着this
应该是global
。
为了增加我的困惑,在函数之外设置属性可以完美地工作:
function readSave2() {
let filePath = folders.category1.path + folders.category1.subpath();
return filePath;
}
this.fileName = 'filename';
this.uniqueId = 1029;
readSave()
// returns '/path/subpath/filename1029'
检查上面的代码,在进行评估之前,一切都几乎相同() => /subpath/${this.fileName}${this.uniqueId}
。现在,先前为空的 Object 在设置时具有两个属性。
最后,这也与前面的示例完全相同:
const readSave3 = () => {
this.fileName = 'filename';
this.uniqueId = 1029;
let filePath = folders.category1.path + folders.category1.subpath();
return filePath;
}
我已经浏览并阅读了几个小时,但仍然对为什么有些方法有效而有些方法无效感到非常困惑。
提前致谢
更新:原来我对给定文件或函数调用中节点的根对象做了一些错误的假设。并不真正了解模块包装器。
解决方案
这个文档让我认为箭头函数应该继承它被调用的范围的this,即readSave,这意味着它应该是全局的。
箭头函数的nothis
是在创建箭头函数时确定的:
[...] 无论如何, foo's
this
设置为它创建时的状态(在上面的示例中,全局对象)。这同样适用于在其他函数内部创建的箭头函数:它们this
仍然是封闭的词汇上下文[...]。
所以这里this
的箭头函数指的是什么this
:
console.dir(this) // <---- refers to `exports`
const folders = {
category1: {
fileName: 'default'
path : '/path',
subpath : () => `/subpath/${this.fileName}${this.uniqueId}`
}
}
this
出于同样的原因,这两个代码块引用了同一个对象:
console.dir(this) // the `this` in the arrow function below is the same as here
// and `this` refers to `exports`
const readSave3 = () => {
this.fileName = 'filename';
this.uniqueId = 1029;
// ...
}
readSave3()
function readSave() {
// ...
}
this.fileName = 'filename';
this.uniqueId = 1029;
readSave()
加载时节点文件的内容被包装到:(模块包装器)
(function(exports, require, module, __filename, __dirname) {
/*... filecontent ... */
})
然后调用该函数,将相应的值作为参数传递,并在传递给函数What is the root object in Node.js的对象上调用该函数。exports
所以对于/subpath/${this.fileName}${this.uniqueId}
指this
的exports
是readSave3
和你最后的代码一样。对于您的readSave
(第一个),this
指的是全局对象。
因此,对于节点,您的代码将如下所示:
var moduleSetup = function(exports, require, module, __filename, __dirname) {
// here `this` is exports
const folders = {
category1: {
fileName: 'default',
path: '/path',
// `this`referes to exports due to arrow function
subpath: () => `/subpath/${this.fileName}${this.uniqueId}`
}
}
function readSave1() {
// here `this` refers to `global` because `readSave1` is not called on an object
//suppose readFileSync would return a string 'filename' and an int 1029
this.fileName = 'filename';
this.uniqueId = 1029;
let filePath = folders.category1.path + folders.category1.subpath();
// I'd go on to write stuff to that file but for now let's just return
return filePath;
}
readSave1()
function readSave2() {
let filePath = folders.category1.path + folders.category1.subpath();
return filePath;
}
// `this` refers to `exports`
this.fileName = 'filename';
this.uniqueId = 1029;
readSave2()
const readSave3 = () => {
// `this` refers to `exports` due to arrow function
this.fileName = 'filename';
this.uniqueId = 1029;
let filePath = folders.category1.path + folders.category1.subpath();
return filePath;
}
readSave3()
}
// and that's roughly how node would invoce that function:
var module = {
exports: {}
}
moduleSetup.call(module.exports, // moduleSetup called on module.exports
// with these arguments:
module.exports, require, module, theFileName, theDirname)
推荐阅读
- java - 在java中打印一个矩形
- command-line-interface - 回滚纱线命令
- kubernetes - 气流调度程序无法与 kubernetes 执行程序一起启动
- c++ - 在学习数据结构之前先了解一下 STL 是不是很好?
- mysql - mySQL如何从第一个表中选择不同的(1列:pruduct_Type)并从第二个表中选择2列
- typescript - 看不到 RXJS 如何确定在多个订阅上重复共享操作的哪一部分
- prolog - `is`和`=`之间的区别?
- c++ - 如何忽略输入流中的某些字符?
- sql-server - 使用 SSIS 导入具有不同结构的 XML 文件
- codenameone - 代号一焦点和删除组件