lessfish / underscore-analysis

【NO LONGER UPDATE】underscore-1.8.3.js 源码解读 & 系列文章(完)
MIT License
3.96k stars 644 forks source link

为什么用「void 0」代替「undefined」 #1

Open lessfish opened 8 years ago

lessfish commented 8 years ago

Why underscore

最近开始看 underscore源码,并将 underscore源码解读 放在了我的 2016计划 中。

阅读一些著名框架类库的源码,就好像和一个个大师对话,你会学到很多。为什么是 underscore?最主要的原因是 underscore 简短精悍(约 1.5k 行),封装了 100 多个有用的方法,耦合度低,非常适合逐个方法阅读,适合楼主这样的 JavaScript 初学者。从中,你不仅可以学到用 void 0 代替 undefined 避免 undefined 被重写等一些小技巧 ,也可以学到变量类型判断、函数节流&函数去抖等常用的方法,还可以学到很多浏览器兼容的 hack,更可以学到作者的整体设计思路以及 API 设计的原理(向后兼容)。

之后楼主会写一系列的文章跟大家分享在源码阅读中学习到的知识。

欢迎围观~ (如果有兴趣,欢迎 star & watch~)您的关注是楼主继续写作的动力

Why does void 0 replace undefined

说来惭愧,underscore 源码解读这个 Repo 放在 Github 都已经 20 天没有更新了,要不是今天 "不小心" 注意到,我居然都快忘了(是不是 lu 多了),所以今晚无论如何都要 lu 出第一篇(毕竟万事开头难)。相对于其他源码解读的文章,基本都会从整体设计开始讲起,楼主觉得 underscore 这个库有点特殊,so 决定按照自己的思路,从用 void 0 代替 undefined 说起。

underscore 源码没有出现 undefined(注意,其实有出现一处,是为 "undefined",而不是 undefined),而用 void 0 代替之。为什么要这么做?我们可以从两部分解读,其一是 undefined 哪里不好了,你非得找个替代品?其二就是替代品为毛要找 void 0?

我们先看第一点,答案很简单,undefined 并不是保留词(reserved word),它只是全局对象的一个属性,在低版本 IE 中能被重写。

var undefined = 10;

// undefined -- chrome
// 10 -- IE 8
alert(undefined);

事实上,undefined 在 ES5 中已经是全局对象的一个只读(read-only)属性了,它不能被重写。但是在局部作用域中,还是可以被重写的。

(function() {
  var undefined = 10;

  // 10 -- chrome
  alert(undefined);
})();

(function() {
  undefined = 10;

  // undefined -- chrome
  alert(undefined);
})();

接下来思考第二个问题,为毛找的替代品是 void 0?

我们来看看 MDN 的解释:

The void operator evaluates the given expression and then returns undefined.

意思是说 void 运算符能对给定的表达式进行求值,然后返回 undefined。也就是说,void 后面你随便跟上一个表达式,返回的都是 undefined,都能完美代替 undefined!那么,这其中最短的是什么呢?毫无疑问就是 void 0 了。其实用 void 1,void (1+1),void (0) 或者 void "hello",void (new Date()) 等等,都是一样的效果。更重要的前提是,void 是不能被重写的(cannot be overidden)。

那么,ES5 大环境下,void 0 就没有用武之地了吗?答案是否定的,用 void 0 代替 undefined 能节省不少字节的大小,事实上,不少 JavaScript 压缩工具在压缩过程中,正是将 undefined 用 void 0 代替掉了。

一篇不长的文章写了两个小时,心累,不点个赞、不关注下楼主的 Repo 你觉得好意思吗?https://github.com/hanzichi/underscore-analysis

Read More

itsmatthu commented 8 years ago

不小心走进了楼主的博客,我很欣赏楼主对于库的解读和学习。我自己也在学习和使用过程中。这篇文章显得有些虎头蛇尾了,较大的篇幅解读了undefined是可以被重写,但是对于为什么能减少字节,压缩工具的实现,居然一句话带过了,这样显得结尾有些草率和仓促了,继续努力

lessfish commented 8 years ago

谢谢。 可能我们的重点不同吧,至于压缩工具的实现,这似乎有点 "偏题" 了,反正我是这么认为的~

idda commented 8 years ago

蛮好,加油

juemin90 commented 8 years ago

已关注,加油po主

lessfish commented 8 years ago

@juemin90 多谢支持!

jiapeiyang commented 8 years ago

长见识了,谢谢楼主

lessfish commented 8 years ago

@jiapeiyang 感谢支持~

Wangbaogang commented 8 years ago

支持支持!最近在读underscore源码,楼主的注释版读起来很棒!

lessfish commented 8 years ago

@Wangbaogang thanks a lot!

lcoder commented 8 years ago

lz棒棒哒

git-lt commented 7 years ago

写的不错

zhw2590582 commented 7 years ago

这个系列,我看定了

Melody12ab commented 7 years ago

已start,楼主加油!!!

snayan commented 7 years ago

已start,楼主加油!!!

cleartime commented 7 years ago

已start,楼主加油!!!

waitinghope commented 7 years ago

你不知道的js中卷,2.4.2也谈到了void 0 同时举的例子我觉得不太好。

lessfish commented 7 years ago

@waitinghope 谢谢指出。没看过「你不知道的js」,不介意的话举个更好的例子?我可以补充或者修改下

waitinghope commented 7 years ago

@hanzichi 给你的outlook发了封信.

lessfish commented 7 years ago

@waitinghope 看完了,其实他的意思就是「void + 任何数字」返回的都是 undefined。如果在一个函数中又要执行一个定时器,又要 return,写成 return void setTimeout(...) 的形式,不失为一种 hack

https://book.douban.com/review/8163708/ 他说的不对吧,如果 App.ready 了,那么就会返回 result 值了,如果 !App.ready ,那么返回的是 undefined

waitinghope commented 7 years ago

我觉得他的意思是:var t; 然后return t; 这个时候if(t)是判断不出来啥的.

lessfish commented 7 years ago

@waitinghope 理解有出入吧 ...

xyyie commented 7 years ago

来看看分析的,还是赞下。

LastKing commented 7 years ago

0.0 重点是写出来了,但是 感觉好头重尾轻。。。不过还是 理解。。!~!~!点个赞赞!~!~!~!~

MontageD commented 7 years ago

看出分析很用心,值得学习。

Sy-52 commented 7 years ago

楼主你好,首先向你的分享行为与辛勤劳作表示敬意。 我在读完本文后发现其中一点写的有点问题,在此提出,如果理解的有问题,希望楼主赐教。

(function() { var undefined = 10;

// 10 -- chrome alert(undefined); })();

第一,这一句中undefined在其中起的是变量标识符的作用,我觉得这种情况下说 ' 用void 0 替代undefined去做变量名更好"没有任何意义。 第二、我感觉楼主文章中,这段想表达的意思是这种情况是个Bug,但我认为这种情况是标识符解析的规则导致。如果你想在函数作用域中正确返回undefined的话,避免使用undefined做变量标识符不是更好?或者你也可以返回window.undefined。

lessfish commented 7 years ago

@Sy-52 因为你知道 避免使用 undefined,但是别人可能不知道(虽然我们一般不会这么做),但是确实在低版本 IE 下 undefined 能被作为变量标识符而被重写但不报错

Sy-52 commented 7 years ago

@hanzichi 看了你的回答,又梳理了一下逻辑。 我认为var undefined = 10;这条语句意在创建一个语义为'未定义'的变量,给它赋值整数10,此时alert(undefined);弹出undefined变量的值10不是理所应当的事吗?在此种情况下,你不会想返回的是系统属性undefined吧。所以我还是没搞懂你在这段到底想表达什么意思。 如果可能的话,希望楼主能提供一些在underscore.js中用void 0替代undefined的实际例子或能够指示一下例子所在的地方,thx。

lessfish commented 7 years ago

@Sy-52 你说的这两行代码就已经是很好的例子了啊:

var undefined = 10;

// undefined -- chrome
// 10 -- IE 8
alert(undefined);

弹出undefined变量的值10不是理所应当的事吗?

理所应当?但是 chrome 弹出 undefined 啊?

LastKing commented 7 years ago

@Sy-52 .....这个不是理所应当。。。按照其他java c++的语言规范,undefined 应该改是 语言保留字,但是不幸的js 丢下了这么坑,undefined可以声明为变量的,如果它能为变量时,你就不清楚 到底是变量还是 undefined..... if(xxx ==undefined) 他是比对 变量还是 比对 js的本身的数据类型undefined 勒?。。。

Sy-52 commented 7 years ago

@LastKing 多谢指教,对java等语言不是很熟悉,没研究过,很长姿势。对于你说的第二点:

if(xxx ==undefined) 他是比对 变量还是 比对 js的本身的数据类型undefined 勒?。。。

经我测试,xxx对比的是函数作用域中的变量undefined,这也符合标识符解析规则。

@hanzichi 又想了一下,我可能get到你的意思了。

(function() {
var undefined = 10;

// 10 -- chrome
alert(undefined);
})();

一、你的意思可能是:如果你想在函数作用域中返回或操作系统属性undefined,但如果你在函数作用域中定义有名为undefined变量,你就无法如愿,所以此时用void 0替代undefined更好:

(function() {
var undefined = 10;

alert(void 0);
})();

二、但在全局作用域中:

var undefined = 10;

// undefined -- chrome
// 10 -- IE 8
alert(undefined);

你本来在全局作用域中定义一个名为undefined的变量赋值为10,但在操作它时却被系统属性undefined覆盖,这个才是不符初衷吧,这里用void 0替换undefined没有意义。(另外我测试IE8下alert(undefined)显示为undefined)

综上,由于其他语言中undefined是保留字@LastKing,那么这里提示大家养成不使用undefined做变量名的习惯不是比用void 0替换undefined更好?(因为以后undefined可能会成为保留字)

waitinghope commented 7 years ago

@Sy-52 https://book.douban.com/subject/26606848/ 最关键的是你的code运行在一个不知道undefined是不是真正的undefined的环境中。 这一点对于underscore特别重要。 举个类似的例子,你怎么确定自己拿到的$就是jQuery的$?如果不是,那你所有依赖$的code还能用吗?

lessfish commented 7 years ago

@Sy-52 我觉得你有点钻牛角尖了,私以为 get 到「undefined 在低版本 IE 下能被改写所以采用 void 0 代替」这点就可以了,这也是我的初衷,毕竟 IE8 也即将退出历史舞台。当然,实际开发中,一般不会用 undefined 去做变量名,但是这和知道本文中心点不冲突。

Sy-52 commented 7 years ago

@hanzichi ok,捋顺了,感谢你愿意花时间解惑 ^_^

w-y commented 7 years ago

0.._比void 0更短

lessfish commented 7 years ago

@w-y 还真是,测了下 IE8 也没有问题,请教下这是什么原理?

w-y commented 7 years ago

@hanzichi 类似 这种 0['x'] 但0.x会语法错误 4个字符的话应该还有几种写法

lessfish commented 7 years ago

@w-y 感谢,学习了~

lcoder commented 7 years ago

@w-y 为什么我chrome测试:console.log( 0.. ) 报错?

lessfish commented 7 years ago

@lcoder 后面还跟了个 _ ,意思就是 0['_']

195286381 commented 7 years ago

楼主分析的不错 学到新知识

pageliu16 commented 7 years ago

谢谢楼主,正在研究中

wave1992 commented 7 years ago

~👍

district10 commented 6 years ago

我记得 lodash 里面是用 var undefined 来定义 undefined... 也是奇妙. 自己定义 undefined, 然后不 define 它... 于是就是 undefined.

WalkerKing commented 6 years ago

非常感谢分享,正在看您的注释版的源码

fxk01 commented 6 years ago

写的很好

shangguanhonglei commented 6 years ago

undefined不是js的保留关键字,所以undefined可以作为变量被赋值,至于为什么使用void 0 ,楼主的解析还是不错的

sighWang commented 6 years ago

建议楼主简练下语言,删掉「说来惭愧...」等。这样子读起文章来会更专注

pq1949 commented 6 years ago

为什么不用 null 代替 void 0

delayk commented 6 years ago

@pq1949 null与undefined等价吗?

eventhorizon-cli commented 6 years ago

@delayk null == undefined null !== undefined