查看在线源代码,我在几个源文件的顶部遇到了这个。
var FOO = FOO || {};
FOO.Bar = …;
但我不知道是什么|| {}
。
我知道{}
等于new Object()
,我认为它||
是为了“如果它已经存在,则使用它的值,否则使用新对象。
为什么我会在源文件的顶部看到这个?
查看在线源代码,我在几个源文件的顶部遇到了这个。
var FOO = FOO || {};
FOO.Bar = …;
但我不知道是什么|| {}
。
我知道{}
等于new Object()
,我认为它||
是为了“如果它已经存在,则使用它的值,否则使用新对象。
为什么我会在源文件的顶部看到这个?
你对意图的猜测|| {}
非常接近。
在文件顶部看到的这种特殊模式用于创建命名空间,即命名对象,可以在其下创建函数和变量,而不会过度污染全局对象。
使用它的原因是,如果您有两个(或更多)文件:
var MY_NAMESPACE = MY_NAMESPACE || {};
MY_NAMESPACE.func1 = {
}
和
var MY_NAMESPACE = MY_NAMESPACE || {};
MY_NAMESPACE.func2 = {
}
两者都共享相同的命名空间,然后加载两个文件的顺序无关紧要,您仍然可以在对象中正确获得func1
并func2
正确定义。MY_NAMESPACE
加载的第一个文件将创建初始MY_NAMESPACE
对象,随后加载的任何文件都将扩充该对象。
有用的是,这还允许异步加载共享相同命名空间的脚本,这可以缩短页面加载时间。如果<script>
标签具有defer
属性集,您将无法知道它们将被解释的顺序,因此如上所述,这也解决了该问题。
var AEROTWIST = AEROTWIST || {};
基本上这一行是说将变量设置为AEROTWIST
变量的值AEROTWIST
,或者将其设置为一个空对象。
双管道||
是一个 OR 语句,只有当第一部分返回 false 时才会执行 OR 的第二部分。
因此,如果AEROTWIST
已经有一个值,则将其保留为该值,但如果之前未设置,则将其设置为空对象。
基本上和这样说是一样的:
if(!AEROTWIST) {var AEROTWIST={};}
希望有帮助。
有两个主要部分var FOO = FOO || {};
涵盖。
#1 防止覆盖
想象一下,您将代码拆分为多个文件,并且您的同事也在处理一个名为FOO
. 然后它可能会导致有人已经定义FOO
并为其分配了功能(如skateboard
函数)。然后你会覆盖它,如果你不检查它是否已经存在。
问题案例:
// Definition of co-worker "Bart" in "bart.js"
var FOO = {};
FOO.skateboard = function() {
alert('I like skateboarding!');
};
// Definition of co-worker "Homer" in "homer.js"
var FOO = {};
FOO.donut = function() {
alert('I like donuts!');
};
在这种情况下,如果您在 HTML之后skateboard
加载 JavaScript 文件,该函数将消失,因为 Homer 定义了一个新对象(因此覆盖了 Bart 的现有对象),因此它只知道该函数。homer.js
bart.js
FOO
donut
所以你需要使用var FOO = FOO || {};
这意味着“FOO 将被分配给 FOO(如果它已经存在)或一个新的空白对象(如果 FOO 不存在)。
解决方案:
var FOO = FOO || {};
// Definition of co-worker Bart in bart.js
FOO.skateboard = function() {
alert('I like skateboarding!');
};
// Definition of co-worker Homer in homer.js
var FOO = FOO || {};
FOO.donut = function() {
alert('I like donuts!');
};
因为 Bart 和 Homer 现在FOO
在定义他们的方法之前检查是否存在,所以您可以按任意顺序加载bart.js
,homer.js
而不会覆盖彼此的方法(如果它们有不同的名称)。所以你总是会得到一个FOO
具有方法skateboard
和donut
(耶!)的对象。
#2 定义一个新对象
如果您已经阅读了第一个示例,那么您现在已经知道|| {}
.
因为如果没有现有FOO
对象,那么 OR-case 将变为活动状态并创建一个新对象,因此您可以为其分配功能。像:
var FOO = {};
FOO.skateboard = function() {
alert('I like skateboarding!');
};
|| 的另一个常见用途 也是为未定义的函数参数设置默认值:
function display(a) {
a = a || 'default'; // here we set the default value of a to be 'default'
console.log(a);
}
// we call display without providing a parameter
display(); // this will log 'default'
display('test'); // this will log 'test' to the console
其他编程中的等价物通常是:
function display(a = 'default') {
// ...
}
如果 AEROTWIST 中没有值或者它为 null 或未定义,则分配给新 AEROTWIST 的值将是 {}(一个空白对象)
运算符有||
两个值:
a || b
如果 a 是真的,它会返回a
。否则,它将返回b
。
虚假值为null
、undefined
、0
、""
和。真实的价值观就是其他一切。NaN
false
因此,如果a
尚未设置(是它undefined
)它将返回b
。
对于 || 操作,JS 将返回它找到的第一个“真实”值(从左到右读取):
var bob = false || undefined || 0 || null || "hi"
// ^ ^ ^ ^ ^
// nope nope nope nope yip
//
// => bob = "hi"
// --------------
// breaking
// --------------
var bob = false || "hi" || 0 || null || undefined
// ^ ^
// nope yip <-- stops here
// the rest are ignored
//
// => bob = "hi"
另一个技巧是使用 && (and) 来确保在访问之前存在某些内容。
对于 && 操作,JS 将返回它找到的最后一个“真实”值(从左到右读取)。
例如,如果您尝试从多级对象中读取属性。
var obj = {
foo : {
bar : "hi"
}
}
var bob = obj && obj.foo && obj.foo.bar;
// ^ ^ ^
// yip yip use this
//
// => bob = "hi"
// --------------
// breaking:
// --------------
var bob = obj && obj.foo && obj.foo.sally && obj.foo.bar;
// ^ ^ ^
// yip yip nope <-- stops here,
// ^ and returns the last "yip"
// | the rest are ignored |
// '-----------------------------------------------'
//
// => bob = obj.foo
两者|| 和 && 操作是从左到右读取的......所以你可以拥有通常可能会向右侧抛出错误的东西,因为一旦 JS 检测到真/假值,它就会停止从左到右读取。
请注意,在某些版本的 IE 中,此代码不会按预期工作。因为var
, 变量被重新定义和赋值,所以——如果我没记错的话——你最终会得到一个新对象。那应该可以解决问题:
var AEROTWIST;
AEROTWIST = AEROTWIST || {};