You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
图中可以看到,其实数组的每一个下标也是在栈里进行声明的。和用let x 进行声明的操作是一样的。let x的声明如下图:
所以是不是发现其实undefined也就那么回事吧。一般来说,如果某一个知识点越绕人,那我们就应该从更底层的角度去看清这个知识点。只要你真的是从一个更加深刻和底层的角度去看待undefined,其实 just so so 啦。对了,null我也顺带解释了,只不过没有重点关注,但是整篇下来,其实null是什么,也差不多一清二楚了。总之null和undefined就是完全不同的两码事。
写在最前
数据类型可以说是编程语言的基石,重要性不言而喻。那么现在就从数据类型开始,打破你的思维认知,做一个充满想象力的
FEE
。针对上篇的一些偏激评论,我想强调的一点是:我写的文章,并不是给那些偏激到说脏话的人看的,请尊重每一位为前端贡献微薄力量的Blogger
。多说一句
这篇可以算是 前端猎奇系列中的 探索 Python 来反补 JavaScript 的中篇。 如果没有看过上篇文章的,可以去我的专栏里读读上篇,在知识上没有啥关联的地方,相对独立。基本是我在学习
PY
的时候,学到某一个地方,突然会想到JS
在这一方面是如何表现的,然后随着思考,真的会有不少收获吧。关于数据类型
有句话说的好,掌握数据类型是学习一门语言的基础。我们从这句话中可以看出,掌握好数据类型是有多么重要。你曾经是不是有想过
JS
的数据类型为什么会是这样,为什么要有null
、undefined
。也许你有过疑问,但是疑问触发后,简简单单的探寻后,就把疑问扔到回调函数里了,这一扔就是到如今。现在我将从PY
的角度来反补JS
,通过PY
去看清JS
的数据类型,看清编程语言的一些规律。now go!JS
的数据类型分为值类型和引用类型:PY
的数据类型分为数值类型、序列类型、Set类型、字典类型:JS
的Set相同)JS
的Map相同)现在我们看一下
PY
和JS
的数据类型,这里我不阐述具体是什么,我只是总结一下,当我学习到这的时候,我对JS的数据类型有了什么样新的理解。现在,你会发现几个很有趣的地方,请看如下:关于 Set 和 Map
这和
PY
的Set
、Dictionary
不谋而合,但是ES6
规范的制定者,没有选择使用Dictionary
作为键值对的类名称,而选择了使用Map
作为键值对的类名称。而Map
正是Java
的键值对的类名称。所以给我的感觉就是,JS在吸收很多语言的优秀的特性,我个人认为,命名成Map
要比Dictionary
好,毕竟少写7
个字符呢😂。关于 Array 和 List
就这样就结束了吗?No,我们再看上面两种类型,首先注意
PY
的List
和JS
的Array
是相同的,都是可以动态进行修改的。但是很多FEE
,因为掌握的知识不够宽泛,导致了对很多事情不能理解的很透彻。比如,我们的思维中就是这样一种固定的模式:数组是可以动态修改的,数组就是数组类型。。我个人建议,FEE
一定不能将自己的思维束缚在某个维度里。这真的会阻碍你 开启那种瞬间顿悟的能力。如果你了解了
PY
或者其他语言,你会发现其实JS
的数组,其在编程语言里面,只能算是List
类型,属于序列类型的一种。而且很重要的是,JS
的Array
是动态的,长度不固定,了解过Java
的同学应该知道,在Java
中,数组是分为Array
和ArrayList
,Aarry
是长度固定的,ArrayList
是长度可以动态扩展的。所以JS
的Array
其实只是编程语言 的Array
中的一种。如果你知道这些,我觉得这对去深刻理解JS
的数据类型将有很大的帮助。虽然JS
对一些知识点进行了简化,但是作为一个合格的计算机工程师,我们不能习惯的接受简化的知识点,一定要去多维度理解和 掌握简化的知识点。了解其背后的世界,也是非常异彩纷呈的。关于 JS 的 String 和 PY 的 String
你会发现
JS
的String
是被归类为数值类型,而PY
的String
是被归类为序列类型。其实我个人更倾向于把JS
的String
归为序列类型,为什么这么说呢,因为JS
的字符串本身就带有很多属性和方法,既然有方法和属性,也就意味着至少是个对象吧,也就是隐式执行了new String
。字符串对象可以通过方法和属性来操作自己的字符序列。所以这被归类为数值类型的话,我个人认为是不科学的,而PY
就分的很清楚。关于 null 和 undefined
null 和 undefined 的争论就在此结束吧。
可能一开始会对
null
和undefined
比较陌生,可能会有那么一刻,你怀疑过JS
的null
和undfined
为什么会被单独作为数据类型,但是过了那一刻,你就默许其是一个语言设计规则了。但是我想说的是,语言设计规则也是人设计的,是人设计的就应该多一份怀疑,不必把设计语言的人看成神一样。编程语言那么多,哪有那么多神。网上有很多好文章介绍JS
的undefined
和null
的,也都说了有多坑。想深入理解有多坑的可以自行百度谷歌。我也不重复造解释了,比如,undefined
居然不是保留字,也是够神奇的,看了下博客,有篇解释的很不错,可以瞅瞅为什么undefined可以被赋值,而null不可以?。写博客的时候,并不是一味的写自己的东西,有时候别人总结好的东西,在我写博客过程中,也能带给我很多灵感和收获。这也是算是和站在巨人的肩膀上是一个道理吧。不过我还是有点个人独特的看法的。而且我认为我的看法要比网上绝大多数的见解要更加深刻(不要脸)。我不想说
undefined
有多坑,我只想去探究和理解undefined
的本质。掌握了本质后,坑不坑也就不重要了。我个人认为,JS
的undefined
是一种为了处理其他问题而强行做出的一种折中方案,且听我娓娓道来。既然
PY
和JS
都是解释性语言,那么为什么PY
可以不依赖undefined
,只需要使用None
就可以了呢? 我写一个简单的例子,可以从我下面的分析中,找到一些更深层的真相,找到设计undefined
真正的原因。代码如下:我们来看运行结果:
从图中会发现,
JS
没有报错,但是PY
报错了,究竟是什么原因? 这里中断一下,我们来看下面这个截图,是java的一段代码的运行结果:图中可以看出,在Java中,可以声明变量,但不赋值,然后不去调用此变量,程序是不报错的,但是在PY中,请看下面截图:
我们发现,我们声明了,也没有去调用它,程序还是报错了。是为什么呢?
为什么在
Java
,C++
,C
语言中,可以声明变量,而不用赋值,并且不会报错。而在PY
中会报错呢,而在JS
中是undefined
呢?其实仔细一想,会恍然大悟,一个非常关键的一点就是:Java
、C++
,C
是强类型语言,在声明的时候,就已经确定了数据类型。所以就算不去赋值,Java
、C++
等也会根据声明的数据类型,设置一个默认的数据类型的值。但是这里注意一点,如果整个程序执行完,在只声明,却没有赋值的情况下,去输出或者调用该变量,程序会报错的。为什么会报错呢,是因为此变量的地址是系统随机生成的,并不在此程序内的地址范围内,也就是说此变量的地址可能是指向其他程序的地址,在这种情况下,如果去调用该地址,那么可能会出现很大的危险性,比如你调用了其它很重要的东西。这里我觉得可以把它理解为游离的指针,虽然这样形容不好,但是很形象,游离的指针是很危险的东西。有多危险,哈哈哈,自己体会✧(≖ ◡ ≖✿)。中断结束,继续
PS
,从上面的叙述知道了Java
等语言是强类型语言。但是我们知道而PY
和JS
是脚本语言,属于弱类型语言,而弱类型语言的特点就是:在声明变量的时候,不需要指定数据类型,这样的好处就是一个变量可以指向万物,缺点是性能差一些,需要让编译器在赋值的时候自己去做判断。请紧跟着我的脚步,我们来看下面这段代码:可以看到,
x
是JS
声明的变量,由于脚本语言是动态的,所以这个变量x
可以指向万物,那么如果直接使用x
,而不让其报错的话,该怎么做呢。一个原则一定不能忘,就是不赋值的话,调用一定会报错,OK,那就赋值,给一个默认值,那么这个默认值用什么来表示呢,指向万物的话,那这类型的可能性有好几种,如果使用
null
来表示的话,由于null
代表空对象,这里说一个很关键的点,就是,为什么其他语言中比如Java
,C++
,他们对于空,都是使用null
来代表一个空对象的?其实最本质的原因还是因为他们是强类型语言,必须在变量前面声明数据类型,对于值类型,他们系统可以自动给一个默认值。所以在强类型语言中的
null
,其作用只是给引用类型用的。而到了弱类型语言中,比如PY
,JS
,我们看PY
,由于PY
老哥不想使用undefied
,也只想用一个null
。那么自然而然的结果就是:直接不允许在未赋值之前,直接调用声明的变量
,只要调直接提示报错,那么你可能会有疑问了,为什么PY语言中,连只声明变量,不去调用它,程序都会报错呢。其实我个人觉得原因是因为弱类型语言的数据类型不确定导致的,编译器无法去给一个默认值,也就意味着不确定因素增加,既然不确定,那PY
的做法就是直接使其报错。通过编译器报错来显式让开发者去遵循编码规则。而小可爱
JS
就不一样了,由于设计者就是不想使其报错,想允许声明,并且可以在未赋值的时候还可以直接调用而不报错。所以也就意味着他要给声明的变量赋一个默认值,怎么赋值呢?这估计也是困扰了设计者良久,下面我举一个很简单易懂的例子,请看下面代码:从代码可以看出,如果想不报错,有几种可能:
第一种: 按照其他语言的规范,只保留一个空值
null
,ok,继续往下推导,由于JS
是弱类型,变量指向万物,所以肯定只能给所有声明但未赋值的变量设置null
为默认值了。但是这样的话,问题来了。看第三行代码,其实
y[3]
也是声明未赋值的变量
,是不是有点不相信,觉得超出认知了。没事,先继续往下看,既然y[3]
也是未赋值的变量
,那把y[3]
的默认值也设置为null
吗?很明显,不合理。因为
y[3]
可能是各种类型,如果直接都设置为null
。那用户直接打印y[3]
,然后蹦出来一个null
,还是object
类型,岂不要炸?所以到这里,我会慢慢发现,其实JS
中的null
和undefined
是完全不同的两码事,很容易去区分。综上,我猜一下
JS
作者的脑洞应该是这样的,既然我想让调用声明未赋值的变量不报错,那ojbk
。不是弱语言么,不是指向万物吗?那要来就来刺激点,我就单独设置一个数据类型,名为undefined
。专门用来counter
指向万物的声明却未赋值的变量。哈哈哈哈,是不是很刺激😂。解决最后一公里的疑惑
看下面代码
你会发现
x
和y[3]
都是undefined
。我们来透过现象看本质,本质上就是声明了,但是未赋值。为什么可以这么说,难道y[3]
,也是声明了,但未赋值吗?我可以明确告诉你,是的,没毛病。你可能不相信我说的话,下面我在白板上画一个图就顿悟了。。请看图:图中可以看到,其实数组的每一个下标也是在栈里进行声明的。和用
let x
进行声明的操作是一样的。let x
的声明如下图:所以是不是发现其实
undefined
也就那么回事吧。一般来说,如果某一个知识点越绕人,那我们就应该从更底层的角度去看清这个知识点。只要你真的是从一个更加深刻和底层的角度去看待undefined
,其实 just so so 啦。对了,null
我也顺带解释了,只不过没有重点关注,但是整篇下来,其实null
是什么,也差不多一清二楚了。总之null
和undefined
就是完全不同的两码事。总结
从
JS
和PY
的数据类型,我们可以看出,PY
在设计数据类型的时候,明显考虑的很多,或者说,PY语言在被创造的时候,其数据类型的设计是比较规范的。而我们去看JS
,会发现,有很多坑,感觉是当初为了简化知识点难度,而留下了很多坑。虽然我没有经历过IE
时代的前端,但现在也能深刻体会到前端工程师的不容易。以前还有同行说前端很简单啊,现在也有,我都遇到过好几次这种人了:我:我是前端开发。
人家:噢,我知道了,就是写网页的对吧。。。
我心里os:对你个锤子。。
FEE
们都是从坑里一步步爬上来的,真的不容易。总之,现在的前端正在一步步走上规范,走上体面。。。文末彩蛋一,动态参数
PY
中如何处理动态参数的呢,其实PY
是通过元组或者字典来处理动态参数的,代码如下,这里只写使用 元组 实现动态参数的代码执行结果图如下:
我们再看
JS
是如何实现的执行结果图如下:
看上面两种方式,看完你应该就明白了,
ES6
增加展开符的原因是什么,以及为什么要设计成这个样子。使用...
作为标记。同时为什么要将所有可变参数放在一个数组里面。其实语言都是有相同性的,尤其对于
JS
语言来说,采纳了很多语言的优点,这对于我们前端来说,是一个很大的优势,如果平时善于去这样比较和反补,我个人觉得,FEE
去承担其他开发岗位,也是完全能Hold
住的。番外二,深夜写博客时的意外惊喜(意不意外,刺不刺激)
当我写下这段代码:
第一种情况:我在
node.js
环境运行:结果如图所示:第二种情况:我在
chrome
浏览器下执行这段代码,结果如图所示:第三种情况:我在
IE
浏览器下执行这段代码,结果如图所示:上面第二种情况,你会发现在
chrome
浏览器下,输出的结果形式为:我靠,什么鬼。居然把
arguments
写成了数组的形式:[1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
但是
__proto__
还是Object
。吓的我赶紧试了下面这段代码,代码如图所示:靠,还果真返回了长度。。。但是为什么
__proto__
是Object
。。。。不行,我又看了IE浏览器和
node.js
环境下的结果,都是相同的结果,使用{}
表示类对象数组我陷入了沉思。。。。
不知道是
chrome
开发者故意这样设计的,还是写错了。。小老弟,你怎么回事?chrome
会弄错? 本着上帝也不是万能的理念,我打开了我的脑洞。chrome
浏览器既然不按照{}
这种写法,直接将arguments
写成[]
,使其直接支持数组操作,同时,其原型又继续是对象原型。仔细看会发现又加了一行Symbol(Symbol.iterator): ƒ values()
。这样做的目的是什么,为什么要这样设计?搜了
blog
,然而没搜到。。。这一连串的疑问,让我再次陷入了沉思。。。思考了一会,动笔画了画,发现好像可以找到理由解释了。我觉得可以这么解释:
chrome
想让类数组对象这种不三不四的东西从谷歌浏览器中消失。所以下面这种输出结果就一去不复返了,那么如果不这样写,用什么方法去替代它呢。答案就是写一个原型链继承为对象类型的数组,同时给继承对象类型的数组(其还是对象,不是数组) 增加
Symbol.iterator
属性,使其可以for of
。为什么要这样做呢,因为一些内置类型自带迭代器行为,比如
String
、Array
、Set
、Map
,但是Object
是不带迭代器的,也就意味着我们可以推断出,如果从chrome
浏览器的那种写法的表面上分析,假定arguments
是Array
,那么就完全没必要增加Symbol.iterator
,所以矛盾,所以可以得出,arguments
还是对象,而对象是不带迭代器的。所以要给形式为[]
的arguments
增加Symbol.iterator
。使其具有迭代器功能。从而可以使用for of
。从而完成了[1,2,3]
到{'0':1, '1':2, '2':3}
的转变所以:上述答案被证明为正确。
当然,也可能是:
有理有据的胡诌。。。
备注:
文末的可爱声明: 如果转发或者引用,请贴上原文链接,尊重一下劳动成果😂。文章可能 (肯定) 有一些错误,欢迎评论指出,也欢迎一起讨论。文章可能写的不够好,还请多多包涵。人生苦短,我学前端,多一点贡献,多一分开心,欢迎关注,后续更加精彩哦~
小伙伴觉得我写得还不错的话,就点个赞 以兹鼓励 一下吧😊。
The text was updated successfully, but these errors were encountered: