一图胜千言,今天我们要学习的就是下面的这张图:
话说蹩脚的Javascript小镇坐落在一个不知名的山脚下。
镇长
镇长就是大名鼎鼎的object:
其实这家伙也就会那么几个蹩脚的技能,大部分时间就是悠闲地混日子,却安安稳稳的混了快一辈子。他声称:“整个小镇全是他的,Javascript就是彻底面向对象的”。其实他的狂妄也是有道理的,整个小镇里大部分人都是他的部下,除了那几个特殊的家伙。好了,镇长大人长得就是下面的样子:
var obj = { name : "镇长", age : 88 }; assert(obj.name === "镇长" && obj.age === 88, "Object");
女强人
小镇里最出名的就是女强人function了:
我实在不知道怎么来形容这个人,精明干练、诡计多端、冷若冰霜、风骚入骨等等。但是我不得不承认,正是因为她的存在才使得整个小镇充满到了生机,她的一颦一笑都会牵动整个小镇的神经:
function smile() { return "笑"; } var cry = function() { return "哭"; } assert(smile() === "笑", "Function"); assert(cry() === "哭", "Function");
三个熊孩子
小镇里有三个调皮捣蛋的熊孩子:
一个叫boolean,理想是长大了当一个法官,因为他对是非最感兴趣;
一个叫number,理想是长大了当一个数学家,因为他对数字最感兴趣;
一个叫string,理想是长大了当一个文学家,因为他对文字最感兴趣;
boolean最小,也最单纯,她最常用的口头禅就是,如果我扔的硬币是正面,我今天就和number玩;如果是反面,我今天就和string玩。
assert(true, "true"); assert(!false, "false");
number是个小小的数学家,他会用整数数数,会用浮点数测量,还有用一些特殊数解决一些稀奇古怪的问题:
test("整数字面量", function() { assert(12 === 12, "十进制"); assert(012 === 10, "八进制"); assert(0x12 === 18, "十六进制"); }); test("浮点数字面量", function() { assert(_floatEqual(3.14, 3.1400000001), "浮点数"); assert(_floatEqual(3.1415e2, 314.1500000001), "科学计数法"); assert(_floatEqual(.42, 0.42), ".42不推荐"); assert(_floatEqual(42., 42.0), "42.不推荐"); }); test("特殊数字", function() { assert(NaN !== NaN, "NaN"); assert(Infinity === Infinity, "Infinity"); assert(-Infinity === -Infinity, "-Infinity"); });
string是个小小的文学家,他会用文字写文章,还有一些特殊的符号来写自己的日记:
test("字符串字面量", function() { assert("abc" === "abc", "'abc'"); assert('abc' === "abc", "'abc'"); }); test("转义字符", function() { assert("a\nb" === "a\nb", "\\n"); });
是的,这三个家伙就是镇长管不了的,他们整天神出鬼没,四处闯祸,搞得镇长不知道吹胡子瞪眼了多少次,最后镇长大人终于想到一个好办法,那就是:把三个熊孩子的妈妈招聘到镇长办公室。
三个好妈妈
这三个妈妈其实没啥好介绍的,因为他们的孩子和她们长得非常象:
自从三个妈妈上岗了以后,三个熊孩子闯的祸就明显少了很多,因为很多时候,熊孩子们的行为会被妈妈们自动接管过去。
一对老夫妻
镇子里住着一对老夫妻,或许小镇还不存在的时候他们就已经在这里了。是的,他们也不归镇长管。
老奶奶undefined是个接生婆,只要报上一个名字,她就可以告诉你这个人是不是镇子里的出生的:
var p; assert(p === undefined, "undefined"); //ReferenceError: q is not defined assert(q !== undefined, "undeclared");
老爷爷null是个老中医,只要得到了他的假条,你就可以什么都不用干了:
var obj = null; assert(obj === null, "null"); var work = function() { } work = null; assert(work === null, "null");
其他职员
镇长办公室里还有其他几个职员:
Array负责组织排队
Date负责时间管理;
RegExp负责复杂的文档处理;
Error负责错误处理
初次见面,先打个招呼吧,以后你会慢慢熟悉的:
var arr = [1, 2, 3]; assert(arr.length === 3, "Array"); var reg = /abc/i; assert("Abc".replace(reg, "xyz") === "xyz", "RegExp"); var date = new Date("2016-12-17"); assert(date.getYear() === 2016 - 1900 && date.getMonth() === 11 && date.getDate() === 17, "Date"); var error = new Error("错误"); assert(error.message === "错误", "Error");
新来的家伙
有一天,小镇里发生一件影响深远的的大事,一个臭流氓花了一元买了一张车票,然后他就拿这张票看了一场票价100的电影:
let rogue = { name : "张三", age : 25, }; function buy_bus_ticket(person) { person.is_ticket = true; } function go_bus(person) { if (person.is_ticket) { console.log("发车啦"); person.is_ticket = false; } } function go_cinema(person) { if (person.is_ticket) { console.log("看电影"); person.is_ticket = false; } } buy_bus_ticket(rogue); go_cinema(rogue);
要不是票还要收回去,这家伙可以凭这一张票玩遍整个小镇。镇长大人雷霆震怒,下令彻查,但是结果却是:代码没有错误。
看来解决问题的办法只能是让is_ticket变成is_bus_ticket,is_cinema_ticket。但是Javascript是一门动态语言,天知道以后还要裂变出来多少的is_xxx_ticket。
镇长大人没招了,只好向上级单位(Javascript标准委员会),上级单位很够意思,没过多久就派来了一个新的家伙symbol,专门解决这个问题:
symbol有什么本事?据说只有一个,就是会变脸,底下人每次求他办事他都会变个面孔,但是面对编译器大人,他又永远只有一个面孔:
let id = Symbol("name"); assert(id.toString() === "Symbol(name)", "display"); let id_ = Symbol("name"); assert(id !== id_, "unique");
symbol很好的解决了属性名冲突的问题:
let isTicket = Symbol("isTicket"); let isEmployee = Symbol("isEmployee"); let isVip = Symbol("isEmployee"); let me = { name : "张三", age : 25, isTicket : true, isEmployee : true, }; me.isVip = false; assert(me.isTicket && me.isEmployee && !me.isVip, "object");
现在各个商家只要先请教symbol,就可以随意的给顾客打标记了,绝对不用担心属性名会重复。至于symbol实际上是怎么实现的,是用uuid还是其它什么能够保证不重复的东西,我们就不需要关心了。
但是每次请教symbol都会变脸,这在有时候是不利于共享的,这时候就要用到全局symbol了:
let me = Symbol.for("me"); assert(me.toString() === "Symbol(me)", "display"); let me_ = Symbol.for("me"); assert(me_ === me, "not unique"); assert(Symbol.keyFor(me) === "me", "keyFor");
全局symbol会被统一管理,保证名称一样标识符也一样。
symbol备受青睐,javascript里内置了很多他的职位:
assert(Symbol.hasInstance.toString() === "Symbol(Symbol.hasInstance)", "Symbol.hasInstance"); assert(Symbol.isConcatSpreadable.toString() === "Symbol(Symbol.isConcatSpreadable)", "Symbol.isConcatSpreadable"); assert(Symbol.iterator.toString() === "Symbol(Symbol.iterator)", "Symbol.iterator"); assert(Symbol.toPrimitive.toString() === "Symbol(Symbol.toPrimitive)", "Symbol.toPrimitive");
typeof
typeof和小镇里的每个人都认识,它专职就是分辨谁是哪个数据类型:
assert(typeof null === "object", "null"); // 是一个bug,需要特别注意 assert(typeof undefined === "undefined", "undefined"); assert(typeof true === "boolean", "true"); assert(typeof false === "boolean", "false"); assert(typeof 0 === "number", "number"); assert(typeof -0 === "number", "number"); assert(typeof +0 === "number", "number"); assert(typeof 1 === "number", "number"); assert(typeof 3.14 === "number", "number"); assert(typeof NaN === "number", "number"); assert(typeof Infinity === "number", "number"); assert(typeof -Infinity === "number", "number"); assert(typeof "abc" === "string", "string"); var obj = { name : 'wgs' }; function fun() { return 1; } assert(typeof {} === "object", "object"); assert(typeof [] === "object", "object"); assert(typeof obj === "object", "object"); assert(typeof fun === "function", "function"); assert(typeof Symbol() === "symbol", "symbol");
基本上,typeof的工作还算是称职的,7种数据类型它能正确的识别出6种,唯一的例外就是null被认作了object,据说这是一个bug,但是再也改不了了。
还有一个问题,typeof或许对function情有独钟,每次都能正确的叫出她的名字,而对于镇长办公室里的其它人,typeof则很敷衍地一律打上了object的标签。