这套题还不错,感兴趣的猿可以试一试:前端开发工程师

目录

  1. void 是什么?
  2. void 的作用
  3. void 优先级
  4. void 的执行
  5. 为什么要用 void ?

前言

前两天在开发项目的公共基础库时,无意间在看 moment.js 源代码的时候发现这样一个 util 函数。这个函数作用无非就是判断输入否是 undefined ,这个不是关键,关键是 void 这个引起了我的注意,这里为什么不直接使用 input === undefined ?而是使用这种形式来判断了。带着疑问我们今天就一起来看看 JavaScript void 运算符。

export default function isUndefined(input) {
    return input === void 0;
}
 

1. void 是什么?

void 运算符 对给定的表达式进行求值,然后返回 undefined。语法就是 void expression 。void 是一个一元运算符,它可以出现在任意类型的操作数之前执行操作数,却忽略操作数的返回值,返回一个 undefined。void 在表达式的左边,void 右边的表达式可以是带括号形式(例如:void(0)),也可以是不带括号的形式(例如:void 0)。

2. void 的作用

1. JavaScript URIs

如果有印象的同学是不是会发现,我们在阻止 a 标签的默认事件是会用到这个。

<a href="javascript:void(0);">
<a href="javascript:void(0);">
  这个链接点击之后不会做任何事情,如果去掉 void(),
  点击之后整个页面会被替换成一个字符 0。
</a>
<p> 
  chrome中即使<a href="javascript:0;">也没变化,firefox中会变成一个字符串0 
</p>
<a href="javascript:void(document.body.style.backgroundColor='green');">
  点击这个链接会让页面背景变成绿色。
</a>
 

注意,虽然这么做是可行的,但利用 javascript: 伪协议来执行 JavaScript 代码是不推荐的,推荐的做法是为链接元素绑定事件。

2. 立即调用的函数表达式

在使用立即执行的函数表达式时,可以利用 void 运算符让 JavaScript 引擎把一个function关键字识别成函数表达式而不是函数声明(语句)。

void function iife() {
    var bar = function () {};
    var baz = function () {};
    var foo = function () {
        bar();
        baz();
     };
    var biz = function () {};
    foo();
    biz();
}();
 

3. 在箭头函数中避免泄漏

箭头函数标准中,允许在函数体不使用括号来直接返回值。 如果右侧调用了一个原本没有返回值的函数,其返回值改变后,则会导致非预期的副作用。 安全起见,当函数返回值是一个不会被使用到的时候,应该使用 void 运算符,来确保返回 undefined(如下方示例),这样,当 API 改变时,并不会影响箭头函数的行为。

button.onclick = () => void doSomething();
 

确保了当 doSomething 的返回值从 undefined 变为 true 的时候,不会改变函数的行为。

3. void 优先级

由于 void 运算符的优先级比较高(14),高于普通运算符的优先级,所以在使用时应该使用小括号明确 void 运算符操作的操作数,避免引发错误。 在下面的示例代码中,由于第一行没有使用小括号,void 运算符优先计算,恒等于( void 2)- 1,也就是 undefined - 1,结果为 NaN,在第二行代码中用括号包含了 (2-1),所以优先于是先计算2-1,在处理 void,恒等于 void (1),结果也就是 undefined。

console.log(void 2 - 1);  //返回NaN
console.log(void (2 - 1));  //返回undefined
 

4. void 的执行

在 262 ECMA 中,对 void 有一个执行步骤的解释,大致的解读一下:

  1. 执行 UnaryExpression 并把返回值赋值给 expr
  2. 调用 GetValue(expr)
  3. 返回 undefined

注意最后的一句话,GetValue一定要调用,即使它的值不会被用到,但是这个表达式可能有副作用。其实这个副作用是什么,并没有解释。既然void xx === undefined,我们回到最开始的问题,哪 void 0 、void 100、void xx()、void 不管什么操作,返回的都是 undefined 。哪为什么在 moment.js 中不直接写 input === undefined。

5. 为什么要用 void ?

我们知道 undefined 在 JavaScript 中是一个保留字。哪既然他是一个保留字,我们就可以为它赋值。

function test() {
    var undefined = "不爱吃猫的鱼er";
    console.log(undefined); // 不爱吃猫的鱼er
}
test();
console.log(undefined); // undefined
 

赋值之后你提取到的 undefined 就不等于 undefined了,当然如果我们使用的是 window 对象上的 undefined,也可能被赋值 。所以你直接使用的 undefined ,不一定是100%可靠。于是在很多框架或者基础 JS 库中,采用void方式获取 undefined 便成了通用准则。例如 moment.js、Backbone.js、underscore.js,在他们的源码中对 undefined 的使用都是使用 void 0 代替。 • moment.js

export default function isUndefined(input) {
    return input === void 0;
}
 

• Backbone.js

if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
 

• underscore.js

_.isUndefined = function(obj) {
    return obj === void 0;
}
 

除了采用void能保证取到 undefined 值以外,AngularJS 的源码里通过函数调用不传参数,确保了 undefined 参数的值是一个 undefined。

(function(window, document, undefined) {
    //.....
})(window, document);
 

总结

通过 void xx 这种方式来获取 undefined,比直接使用 undefined 来的更加可靠安全,所以在很多的库、框架中都使用 void xx 这种方式来处理undefined ,保证使用到的是安全可靠的 undefined。这种方式我们在日常的项目开发或者的封装也可以借鉴起来。