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