你所不知道的 JS - 模块加载器
You don't know JS - module loader
我正在阅读 github 上的“你不懂 JS”(第 1 版),我正在研究第 5 章中的模块加载器示例。但是有两件事我不知道不明白。我在这里发现了另一个关于同一个模块加载器的问题 (),但它没有回答我的问题。
先上原码:
var MyModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i=0; i<deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply( impl, deps );
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get
};
})();
MyModules.define( "bar", [], function(){
function hello(who) {
return "Let me introduce: " + who;
}
return {
hello: hello
};
} ); // module["bar"] = hello(who) & deps = []
MyModules.define( "foo", ["bar"], function(bar){
var hungry = "hippo";
function awesome() {
console.log( bar.hello( hungry ).toUpperCase() );
}
return {
awesome: awesome
};
} ); // module["foo"] = awesome() & deps = [hello(who)]
var bar = MyModules.get( "bar" );
var foo = MyModules.get( "foo" );
console.log(
bar.hello( "hippo" )
); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO
现在问题:
- 为什么 impl 应用于自身(加上 deps 对象)?我使用 w3schools/apply() 查看 apply() 的工作原理。在第一个示例中,只有一个对象应用于函数,而不是函数本身加上一个对象。
modules[name] = impl.apply( impl, deps );
- 为什么我要在 foo 的定义中将 bar 作为函数参数,因为它在全局范围内(也在严格模式下)?
MyModules.define( "foo", ["bar"], function(bar){
如果我删除 impl 一个 bar 它就像原始代码一样工作。
"use strict";
...
modules[name] = impl.apply( deps );
...
MyModules.define( "foo", ["bar"], function(){
...
这两件事是不必要的还是巧合,代码有效?
Why is impl applied to itself (plus the deps object)? I used w3schools/apply() for looking at how apply() works. There is in the 1st example just an object applied to a function, and not the function itself plus an object.
在 MDN 上查看 Function#apply()
。通常 MDN 有更多的信息,因此值得参考。
当您调用 apply()
时,第一个参数是所执行函数的 this
值。在很多情况下,该值是什么并不重要 - 如果您不关心它,您也可以传递 null
。
const thisValue = {foo: 3};
function usingThis() {
return this.foo + 2;
}
console.log(usingThis.apply(thisValue)); // 3 + 2 = 5
function notUsingThis() {
return 8;
}
console.log(notUsingThis.apply(thisValue)); // 8
console.log(notUsingThis.apply(null)); // 8
但是,这个模块加载器传递了正在加载的模块。这也是一个明智的决定,因为在内部使用了 if this
,它至少是明智的——不是全局对象,也不是 null
这可能会导致错误.如果不使用 this
,则值是什么都没有关系。
还可能值得注意的是,由于 this
的值是函数本身,您在模块内部使用 this.foo = "hello"
,它会附加到函数对象。由于一个模块只会 运行 一次,这很好 - 您可以读写属性而不必担心它们会被践踏。
Why should i give bar as a function argument in the definition of foo, since it is in the global scope (also in strict mode)?
原因是加载的模块和它加载的模块可能不在同一个范围内。事实上,如果模块 可以 安全地相互引用,那么您根本不需要“加载”它们,因为它们已经准备好所有依赖项。
然而,随着时间的推移,这会导致很难追踪依赖关系和脆弱的代码库。从长远来看,不知道每个模块使用什么是一个糟糕的设计。
如果你看看其他模块系统,它们都会声明依赖关系 显式:CommonJS、异步模块定义 (AMD) 以及 ES6 模块都做同样的事情事物。显示的模块加载器做了非常相似的事情。
为了便于说明,该示例已被简化。是的,在那种情况下和只有那种情况,您可以跳过依赖项的显式声明。然而,在真实的代码库中,模块很可能在不同的地方定义,而不能访问相同的范围以防止事情变得混乱。那就是模块加载器的当前设计非常有意义的时候。
我正在阅读 github 上的“你不懂 JS”(第 1 版),我正在研究第 5 章中的模块加载器示例。但是有两件事我不知道不明白。我在这里发现了另一个关于同一个模块加载器的问题 (
先上原码:
var MyModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i=0; i<deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply( impl, deps );
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get
};
})();
MyModules.define( "bar", [], function(){
function hello(who) {
return "Let me introduce: " + who;
}
return {
hello: hello
};
} ); // module["bar"] = hello(who) & deps = []
MyModules.define( "foo", ["bar"], function(bar){
var hungry = "hippo";
function awesome() {
console.log( bar.hello( hungry ).toUpperCase() );
}
return {
awesome: awesome
};
} ); // module["foo"] = awesome() & deps = [hello(who)]
var bar = MyModules.get( "bar" );
var foo = MyModules.get( "foo" );
console.log(
bar.hello( "hippo" )
); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO
现在问题:
- 为什么 impl 应用于自身(加上 deps 对象)?我使用 w3schools/apply() 查看 apply() 的工作原理。在第一个示例中,只有一个对象应用于函数,而不是函数本身加上一个对象。
modules[name] = impl.apply( impl, deps );
- 为什么我要在 foo 的定义中将 bar 作为函数参数,因为它在全局范围内(也在严格模式下)?
MyModules.define( "foo", ["bar"], function(bar){
如果我删除 impl 一个 bar 它就像原始代码一样工作。
"use strict";
...
modules[name] = impl.apply( deps );
...
MyModules.define( "foo", ["bar"], function(){
...
这两件事是不必要的还是巧合,代码有效?
Why is impl applied to itself (plus the deps object)? I used w3schools/apply() for looking at how apply() works. There is in the 1st example just an object applied to a function, and not the function itself plus an object.
在 MDN 上查看 Function#apply()
。通常 MDN 有更多的信息,因此值得参考。
当您调用 apply()
时,第一个参数是所执行函数的 this
值。在很多情况下,该值是什么并不重要 - 如果您不关心它,您也可以传递 null
。
const thisValue = {foo: 3};
function usingThis() {
return this.foo + 2;
}
console.log(usingThis.apply(thisValue)); // 3 + 2 = 5
function notUsingThis() {
return 8;
}
console.log(notUsingThis.apply(thisValue)); // 8
console.log(notUsingThis.apply(null)); // 8
但是,这个模块加载器传递了正在加载的模块。这也是一个明智的决定,因为在内部使用了 if this
,它至少是明智的——不是全局对象,也不是 null
这可能会导致错误.如果不使用 this
,则值是什么都没有关系。
还可能值得注意的是,由于 this
的值是函数本身,您在模块内部使用 this.foo = "hello"
,它会附加到函数对象。由于一个模块只会 运行 一次,这很好 - 您可以读写属性而不必担心它们会被践踏。
Why should i give bar as a function argument in the definition of foo, since it is in the global scope (also in strict mode)?
原因是加载的模块和它加载的模块可能不在同一个范围内。事实上,如果模块 可以 安全地相互引用,那么您根本不需要“加载”它们,因为它们已经准备好所有依赖项。
然而,随着时间的推移,这会导致很难追踪依赖关系和脆弱的代码库。从长远来看,不知道每个模块使用什么是一个糟糕的设计。
如果你看看其他模块系统,它们都会声明依赖关系 显式:CommonJS、异步模块定义 (AMD) 以及 ES6 模块都做同样的事情事物。显示的模块加载器做了非常相似的事情。
为了便于说明,该示例已被简化。是的,在那种情况下和只有那种情况,您可以跳过依赖项的显式声明。然而,在真实的代码库中,模块很可能在不同的地方定义,而不能访问相同的范围以防止事情变得混乱。那就是模块加载器的当前设计非常有意义的时候。