运算符
约 2733 字大约 9 分钟
2025-04-17
一、优先级
以下是运算符的优先级表,从最高到最低:
.
[ ]
!
、~
、+
、-
is
、defined
**
、*
、/
、%
+
、-
...
、..
<=
、>=
、<
、>
in
==
、is
、!=
、is not
、isnt
、is a
&&
、and
、||
、or
?:
=
、:=
、?=
、+=
、-=
、*=
、/=
、%=
not
if
、unless
、else
二、一元运算符
可用的一元运算符包括 !
、not
、-
、+
和 ~
。
!0
// => true
!!0
// => false
!1
// => false
!!5px
// => true
-5px
// => -5px
--5px
// => 5px
not true
// => false
not not true
// => true
逻辑运算符 not
的优先级较低,因此以下代码
a = 0
b = 1
!a and !b
// => false
// parsed as: (!a) and (!b)
可以被替换为:
not a or b
// => false
// parsed as: not (a or b)
三、二元运算符
1、下标 [ ]
下标运算符允许我们通过索引(从零开始)获取表达式内的值。负索引值从表达式中的最后一个元素开始。
list = 1 2 3
list[0]
// => 1
list[-1]
// => 3
括号表达式也可以作为元组(例如 (15px 5px)
、(1 2 3)
)。
下面是一个使用元组进行错误处理的示例(并展示了此构造的多功能性):
add(a, b)
if a is a 'unit' and b is a 'unit'
a + b
else
(error 'a and b must be units!')
body
padding add(1,'5')
// => padding: error "a and b must be units";
padding add(1,'5')[0]
// => padding: error;
padding add(1,'5')[0] == error
// => padding: true;
padding add(1,'5')[1]
// => padding: "a and b must be units";
这是一个更复杂的例子。当标识符(第一个值)等于error
时,我们调用内置的error()
函数并返回错误信息。
if (val = add(1,'5'))[0] == error
error(val[1])
2、范围 ..
、...
提供了包含(..
)和排除(...
)两种范围运算符,它们会将表达式展开为序列:
..
运算符会将序列包含在范围中。...
运算符会将序列排除在范围之外。
1..5
// => 1 2 3 4 5
1...5
// => 1 2 3 4
5..1
// => 5 4 3 2 1
3、加减运算符 +
、-
乘法和加法二元运算符和一般认知是一样的。单位类型属于同一类的会做类型转换,否则默认转换为字面值,例如 5s - 2px
结果是 3s
。
+
运算符会将两个表达式相加。-
运算符会将两个表达式相减。
15px - 5px
// => 10px
5 - 2
// => 3
5in - 50mm
// => 3.031in
5s - 1000ms
// => 4s
20mm + 4in
// => 121.6mm
"foo " + "bar"
// => "foo bar"
"num " + 15
// => "num 15"
4、乘除运算符:/
、\
、*
、%
*
运算符会将两个表达式相乘。/
运算符会将两个表达式相除。\
运算符会将两个表达式相除并取整。%
运算符会将两个表达式相除并取余数。
2000ms + (1s * 2)
// => 4000ms
5s / 2
// => 2.5s
4 % 2
// => 0
在属性值中使用 /
运算符时,必须用括号括起来,否则 /
运算符将被作为字面量(即“斜线”)编译 (以支持 CSS 的 line-height
属性):
font: 15px/1.5; // 等价于 font-size: 15px; line-height: 1.5;
但是,以下属性值等价于 14px
÷ 1.5
:
font: (15px/1.5); // font: 10px
只有 /
操作符需要特殊对待。
5、复合赋值 +=
、-=
、\*=
、/=
、%=
+=
将两个表达式相加并赋值给左侧变量。-=
将两个表达式相减并赋值给左侧变量。*=
将两个表达式相乘并赋值给左侧变量。/=
将两个表达式相除并赋值给左侧变量。%=
将两个表达式相除并取余数并赋值给左侧变量。
简写运算符的行为与其他常见语言类似:
- 对于 列表变量,将使用第一个值执行运算符并覆盖列表,将其转换为单值变量。
- 对于 字符串和节点值,只有+=可用作追加函数。
- 对于 数值类型,所有运算符的行为与普通数学运算完全相同。颜色值的处理也类似。
n = 12
n += 8
// => n = 20
int-list = 12 23 0 32
int-list %= 2
// => 12 % 2 = 0 (mod operator)
// => int-list = 0
mixed-list = node 23 'str'
mixed-list %= 2
// => error
mixed-list = node 23 'str' #2e7
mixed-list += 2
// => mixed-list = node2
s = 'str'
s += 2
// => s = 'str2'
c = #0e0
c -= #0e0
// => c = #000
6、指数运算符 **
**
运算符会将左侧表达式的值作为底数,右侧表达式的值作为指数,计算并返回结果。
指数运算符示例:
2 ** 8
// => 256
7、相等与关系 ==
、!=
、>=
、<=
、>
、<
相等运算符可用于比较单位、颜色、字符串甚至标识符。这是一个强大的特性,即使是任意标识符(如wahoo
)也可以作为原子值使用。函数可以返回yes
或no
而非true
或false
(虽然不建议这样做)。
==
运算符会检查两个表达式是否相等。!=
运算符会检查两个表达式是否不相等。>=
运算符会检查左侧表达式是否大于或等于右侧表达式。<=
运算符会检查左侧表达式是否小于或等于右侧表达式。>
运算符会检查左侧表达式是否大于右侧表达式。<
运算符会检查左侧表达式是否小于右侧表达式。
5 == 5
// => true
10 > 5
// => true
#fff == #fff
// => true
true == false
// => false
wahoo == yay
// => false
wahoo == wahoo
// => true
"test" == "test"
// => true
true is true
// => true
'hey' is not 'bye'
// => true
'hey' isnt 'bye'
// => true
(foo bar) == (foo bar)
// => true
(1 2 3) == (1 2 3)
// => true
(1 2 3) == (1 1 3)
// => false
只有完全相同的值才会匹配。例如,0 == false
和 null == false
的结果都是 false
。
别名
==
:is!=
:is not!=
:isnt
四、真值运算
在 Stylus 中,几乎所有内容都会被解析为true
,包括带有后缀的单位。即使是0%
、0px
等也会解析为true
(因为在 Stylus 中,mixin 或函数通常会接受单位作为有效值)。
然而,从算术角度来看,0
本身是false
。
长度大于 1 的表达式(或"列表")被视为true
。
- 常见的
true
:0%
、0px
、0
、1
、1px
、'hey'
、(0 0 0)
、('' '')
- 常见的
false
:0
、null
、false
、''
1、逻辑运算符 and
、or
、&&
、||
逻辑运算符 &&
和 ||
分别是 and
/ or
的别名,具有相同的优先级。
and
、&&
:运算符会检查两个表达式是否都为真,如果都为真,则返回右侧表达式,否则返回左侧表达式。or
、||
:运算符会检查两个表达式是否至少有一个为真,如果至少有一个为真,则返回右侧表达式,否则返回左侧表达式。
5 && 3
// => 3
0 || 5
// => 5
0 && 5
// => 0
#fff is a 'rgba' and 15 is a 'unit'
// => true
2、存在运算符 in
此运算符用于检查左侧表达式是否存在于右侧表达式中。
简单示例:
nums = 1 2 3
1 in nums
// => true
5 in nums
// => false
一些未定义标识符:
words = foo bar baz
bar in words
// => true
HEY in words
// => false
元组同样适用:
vals = (error 'one') (error 'two')
error in vals
// => false
(error 'one') in vals
// => true
(error 'two') in vals
// => true
(error 'something') in vals
// => false
在混入(mixin)中的使用示例:
pad(types = padding, n = 5px)
if padding in types
padding n
if margin in types
margin n
body
pad()
body
pad(margin)
body
pad(padding margin, 10px)
输出结果:
body {
padding: 5px;
}
body {
margin: 5px;
}
body {
padding: 10px;
margin: 10px;
}
3、条件赋值运算符 ?=
、:=
条件赋值运算符 ?=
(别名 :=
)允许我们定义变量而不删除旧值(如果存在的话)。此运算符可以通过一个三元运算符和一个 is defined
二元运算来表达其等效的逻辑。
?=
运算符会检查左侧变量是否已定义,如果未定义,则将右侧表达式的值赋值给左侧变量。:=
运算符会检查左侧变量是否已定义,如果未定义,则将右侧表达式的值赋值给左侧变量。
例如,以下内容是等效的:
color := white
color ?= white
color = color is defined ? color : white
// => white
当使用普通的 =
时,只是简单地重新赋值:
color = white
color = black
color
// => black
但是使用 ?=
时,第二次的赋值就会失败(因为变量已经被定义过了):
color = white
color ?= black
color
// => white
4、类型检查运算符 is a
Stylus 提供了一个名为 is a
的二元运算符,用于类型检查。
15 is a 'unit'
// => true
#fff is a 'rgba'
// => true
15 is a 'rgba'
// => false
或者,我们也可以使用 type()
内置函数:
type(#fff) == 'rgba'
// => true
注意: color
是唯一的特例,当左侧操作数 是 RGBA
或 HSLA
时,计算结果都是 true
。
5、变量定义检查运算符 is defined
这个伪二元运算符不接受右侧操作数,并且不会计算左侧表达式。它允许我们检查变量是否已被赋值。
foo is defined
// => false
foo = 15px
foo is defined
// => true
#fff is defined
// => 'invalid "is defined" check on non-variable #fff'
或者,也可以使用内置函数lookup(name)
来实现这一功能,或者执行动态查找:
name = 'blue'
lookup('light-' + name)
// => null
light-blue = #80e2e9
lookup('light-' + name)
// => #80e2e9
这个运算符非常重要,因为未定义的标识符仍然会被视为true
。
例如:
body
if ohnoes
padding 5px
未定义时将产生以下 CSS:
body {
padding: 5px;
}
然而,这将是安全的:
body
if ohnoes is defined
padding 5px
五、三元运算符
三元运算符的行为与大多数语言中的预期一致。它是唯一具有三个操作数的运算符(条件表达式
、真值表达式
、假值表达式
)。
num = 15
num ? unit(num, 'px') : 20px
// => 15px
六、类型转换
作为内置函数unit()
的简洁替代方案,可以使用语法(expression) unit
来强制添加单位后缀。
body
n = 5
foo: (n)em
foo: (n)%
foo: (n + 5)%
foo: (n * 5)px
foo: unit(n + 5, '%')
foo: unit(5 + 180 / 2, deg)
七、颜色运算符
颜色运算提供了一种简洁而富有表现力的方式来调整颜色组件。例如,我们可以对每个 RGB 按通道进行操作:
#0e0 + #0e0
// => #0f0
另一个例子是通过增加或减少百分比来调整亮度值。要使颜色变亮,增加百分比;要使颜色变暗,减少百分比。
#888 + 50%
// => #c3c3c3
#888 - 50%
// => #444
也可以通过增 / 减度数来调整色相。例如,在这个红色值上增加50deg
会得到黄色:
#f00 + 50deg
// => #ffd500
数值会被自动限制在合理范围内。例如,我们可以将色相"旋转"180 度,如果当前值是320deg
,结果会变为140deg
。
我们还可以通过rgb()
、rgba()
、hsl()
或hsla()
一次性调整多个值(包括透明度):
#f00 - rgba(100,0,0,0.5)
// => rgba(155,0,0,0.5)
八、Sprintf 格式化输出
类似sprintf
的字符串格式化运算符%
可用于生成字面值,内部通过s()
内置函数传递参数:
'X::Microsoft::Crap(%s)' % #fc0
编译为:
X::Microsoft::Crap(#fc0);
多个参数需要用括号包裹:
'-webkit-gradient(%s, %s, %s)' % (linear (0 0) (0 100%))
编译为:
-webkit-gradient(linear, 0 0, 0 100%);
更新日志
e7112
-1于