实习结束,临近开学了, 想起几个星期前给第一次发红包, 给爷爷奶奶, 还有弟弟妹妹。内心有点小开心 ( ̄▽ ̄)"
javascript
好, 回到主题,咱们今天要讲的是——
html
讲到类型, 首先要说的固然是JS的类型分类, 对于这一点,《javascript高级语言程序设计》和《你不知道的javasvript》的阐述是有差别的(但想表达的意思是相同的)java
我更倾向于前一本书(红宝书)的定义:
javascript的数据类型可分为两种: 基本类型和引用类型:
基本类型: String, Number, Boolean, Null, undefined 和Symbol
引用类型: Object, Array, Function, RegExp, Date, 封装类型(基本包装类型)node
我前面说了, 两本书对类型分类的阐述是有差别的, 关键出如今引用类型的Object身上, 由于Object是个特殊的存在, 事实上咱们知道全部其余的引用类型如Array, Function, RegExp等都属于Object, 也就是说Object是这些的“父类型"数组
对此:
《你不知道的javasvript》里把其余引用类型一并归结为Object
《javascript高级语言程序设计》则是根据“外观”把Object和Array等放在平行的位置,就是说“看上去像” { }的是对象, 而像[ ]这样的, 虽然也是对象, 但咱们仍是叫它数组吧浏览器
在了解JS弱类型以前,咱们须要了解一点—— JS里值才有类型,变量没有
咱们常常会谈到JS的类型,其实是针对变量的值的,而不是变量。安全
例如
假设a = 1,则准确的说typeof a; 中的typeof是用来检测a的值的类型,而不是变量a的类型的函数
咱们平时常常看到的检测某个变量(值)的类型, 实际上是一种有意无心的省略(省略了“值”),这可能会带来误解学习
了解了这一点以后再让咱们看看什么叫作JS的弱类型:
咱们上面说到JS里值才有类型,变量没有,也就是JS里不会对变量作类型“强制” :不会要求一个变量从头至尾都只能保持初始化时的类型:测试
var a = 'penghuwan'; console.log(typeof a); // string a = 1; console.log(typeof a); // number
因此说: 弱类型的特性是针对JS变量的(不要和前面的东西混淆了哦)
// 检测基本类型 console.log('检测string', typeof 'penghuwan'); // 检测string string console.log('检测number', typeof 1); // 检测number number console.log('检测boolean',typeof true); // 检测boolean boolean console.log('检测undefined', typeof undefined); // 检测undefined undefined console.log('检测symbol', typeof Symbol()); // 检测symbol symbol console.log('检测null', typeof null); // 检测null object // 检测引用类型 console.log('检测object', typeof {}); // 检测object object
typeof
检测一个数值的类型, 返回的是一个字符串(小写),去表示这个变量数值的类型
优势: 能检测出除了null外的全部内置类型
typeof的缺点:
1.不能检测除了function以外的引用类型
(function仍是能够的哦!)
var fn = function () {}; var array = [1,2,3]; var reg = /\./; console.log(typeof fn); // function console.log(typeof array); // object console.log(typeof reg); // object
2. 检测null会检测出object
让咱们看看《你不知道的javascript》中做者的原话:
“这个 bug 由来已久,在 JavaScript 中已经存在了将近二十年,也许永远也不会修复,由于这牵涉到太多的 Web 系统,“修复”它会产生更多的bug,令许多系统没法正常工做”
没错, 为了“向后兼容”, 咱们是无法用直接的手段检测出null,下面我将会以比较多的篇幅介绍如何检测null
【注意】: 返回的字符串都是小写的哦!是 'string' 不是 'String'
instanceof
检测某个变量是不是某个对象的实例, 返回一个布尔型的数值
var obj = {}; var array = [1,2,3]; var fn = function () {}; var reg = /\./; // 检测具体的引用类型 console.log(obj instanceof Object); // true console.log(array instanceof Array); // true console.log(fn instanceof Function); // true console.log(reg instanceof RegExp); // true // 引用类型用Object去检测也是返回true的 console.log(array instanceof Object); // true console.log(fn instanceof Object); // true console.log(reg instanceof Object); // true
优缺点
优势: 能检测出引用类型的具体类型, 不像typeof同样只能检测出object, 而是检测出更加具体的类型如Array, RegExp等
缺点:
1.返回布尔值, 形式不够灵活
2. 不能检测基本类型
如:
var strObj = new String('penghuwan'); console.log(str instanceof String); // false console.log(strObj instanceof String) // true
能够看到,纯粹的基本类型是不可以检测出来的, 而要转化成对应的基本包装类型才能检测出来,固然了, 我猜大多数时候你都不会这么干
1.这个是《你不知道的javascript》的解决方案
var a = null; if(!a && typeof a === 'object') { console.log('null被检测出来啦!!') } // null被检测出来啦!!
写这篇博客的时候随口问了下旁边的室友:
知道JS中怎么检测null不? 你确定不知道!
(此时我正作得意洋洋抖腿状)
而后他的表情是这样的:
说时迟那时快, 他一顿操做将我火速打脸。。。代码以下:
2. 经过null 包含的[[ class ]]内部属性检测(只作展现,不要这样作哦!!)
var a = Object.prototype.toString.call(null); console.log(a); // 输出 [object Null]
我立刻意识到他是想用下面这种方式检测, 通过测试发现可以成功
if(Object.prototype.toString.call(null) === '[object Null]') { console.log('null被检测出来啦'); } // 打印: null被检测出来啦
当时打脸场景以下:
其实我是不服气的,由于以为这段代码有点丑陋,因而又想了一种:
3. 经过JSON.stringfy(XXX) === 'null'检测null
if(JSON.stringify(null) === 'null') { console.log('null被检测出来啦'); } // 打印: null被检测出来啦
【注意】在《javascript高级语言程序设计》中叫作“基本包装类型”, 在《你不知道的javascript》中叫作“封装类型”, 其实是同一个意思, 本文主要之后者为名
javascript的一句毒奶名言: 万物皆对象! 但其实咱们发现: boolean, Number, String这些基本类型,好像和对象不要紧嘛。是的, 它们基本类型的性质决定了它们和对象有本质的不一样
但它们“背后”仍有股“来自对象”的 神秘力量的做用着...
(此处播放《走进科学》栏目让人不寒而栗的背景音乐...)
有一天彭先生忽然想起 var str = 'penghuwan'; str.substring(2)这种司空见惯的用法里,substring(2)是哪里来的? str是字符串,不是对象啊! 那又怎么会拥有对象才有的方法呢!!?
没错, 即便是boolean, number, string这种看似单纯地像一张白纸的基本类型, 在幕后也和“对象”有着肮脏的py交易。。。。 (我说的是朋友交易哦)
为了可以自由灵活地操做 Boolean, Number 和String这三个很是经常使用的 基本类型值(也就是有大量调用方法作处理的需求)
在访问这三个基本类型值的时候, javascript 就会建立一个“不可见”的封装类型,而后在读取完毕时候销毁
例如:
var s1 = "text"; var s2 = s1.substring(2);
(内部)至关于:
var s1 = new String("text"); var s2 = s1.substring(2); s1 = null
基本类型对应的封装类型的对象只在访问的时候建立,访问完毕就会销毁!该对象的生存期只有一瞬间, 用例子作个 对比:
// 显示建立封装类型的对象, 且在这段代码中始终存在 var str = new String('1'); str.color = 'red'; console.log(str.color); // 输出red
var str = '1' // 隐式建立了封装类型的对象,该对象只在str.color = 'red';这一条语句存在,随后立刻销毁 str.color = 'red'; // 下面这条console语句里面的访问会建立另一个封装对象 console.log(str.color); // 输出undefined
【注意】
1.访问字符串属性(方法)的时候建立的“封装类型对象”是不可见的
2. 只有访问一个保存了基本类型值的变量才会建立“封装类型对象”! 对于“直接的值”是不会建立封装类型对象的
例如:
console.log(1.toString()); // 报错!! 不能直接对值操做 // 这样搞才是对滴~~~~ var a = 1; console.log(a.toString()); // 输出1
字符串转数字
字符串转为数字有两种方式:
1. 经过Number()转化
2. 经过parseInt解析
二者的不一样:
1. Number: 当字符串内容包括数字外的内容时候(如"42px"),转化失败返回NaN
2. parseInt: 当字符串内容包括数字外的内容时候, 返回当前从左往右解析成功的数字
var a = "42"; var b = "42px"; Number( a ); // 42 parseInt( a ); // 42 Number( b ); // NaN parseInt( b ); // 42
其余类型转化为字符串
可调用toString方法转化
var a = [1,2,3]; a.toString(); // "1,2,3"
// 经过“幕后”的封装类型调用toString() var a = 1; console.log(a.toString()); // 输出 字符串的1
【注意】对undefined和 null 这两个特殊的类型你没法调toString(),由于它们根本就没有这个方法
var a = undefined; console.log(a.toString()) // 报错!根本找不到方法!
var b = null; console.log(b.toString()) // 报错!根本找不到方法!
###固然不少时候咱们会用更直接的方法: XXX + ""(加一个空串)去实现隐式的类型转化
JSON对象转化为字符串
(啊!首先我要先喊一句JSON大法好!)
咱们知道,强大的JSON.stringify能够将许多值转化为字符串, 但仅限于JSON安全的值(JSON-safe)
如:
JSON.stringify( 42 ); // "42" JSON.stringify( "42" ); // ""42"" (含有双引号的字符串) JSON.stringify( null ); // "null" JSON.stringify( true ); // "true"
对于非JSON安全的值(function, undefined和Symbol)
JSON.stringify却避之惟恐不及
对这些值:
JSON.stringify( undefined ); // undefined JSON.stringify( function(){} ); // undefined
JSON.stringify( [1,undefined,function(){},4] ); // "[1,null,null,4]"
JSON.stringify( { a:2, b:function(){} } ); // "{"a":2}"
如今你应该知道为何JSON.parse(JSON.stringify(XXX))这种深拷贝的神操做要求XXX对象里面不能有函数了吧
什么叫JSON安全的值(为何函数“不安全”?)
你觉得我会说答案? 对不起我也不知道 [哭脸], 不过等我继续努力学习,知道了后会来告诉你们的.....(或者评论区有高人的话帮忙一下咯)
console对象,window对象,DOM元素对象并不被javaScript真正“拥有”
javascript通常是不能独立运行的, 而要依赖于宿主环境,常见的宿主环境有:
1. 浏览器 (最多见)
2. node
console对象对许多JSer们来讲都很熟悉, 通常想到console你可能便会情不自禁地想起“console.log”这个刻骨铭心的方法(固然如今基本调试都用debugger了嘿嘿。。。)
console对象,window对象,DOM元素对象并不被javaScript真正“拥有”
console对象
正由于console和JS的联系如此的 密切, 因此一些人可能误觉得console对象是JS标准里的东西。
这里我要说一句:
console对象并非属于JS的而是浏属于浏览器的
也正因如此, 各大浏览器关于console对象API的实现也各不相同(固然log这种基本方法都有。。)
1. 低版本的IE甚至没有console对象(固然也就没有了与之对应的调试功能)
2.谷歌和火狐console对象下的方法基本相同,但也是有差别的。例如:
谷歌console下有memory方法火狐没有
连连看! 找不一样
这是我大谷歌中打印的console对象:
这是火狐打印的console对象
Window对象
学习JS的筒子们通常都知道, “JS有有个保存全局变量的顶层对象, 它叫Window对象,或者叫作global对象”
我一直以来也有一个困惑:“既然(若是)Window对象和global对象是同一个东西的话,干吗要取两个名称,大家玩我啊?”
阅读了相关资料后, 我发现:“Window对象和global对象是同一个东西”的说法并非很精确
最重要的一点是:他两隶属的“政治阵营”不同
1.Global对象是ECMAscript标准中定死的全局对象
2.Window对象是浏览器的一个实例,因此你容易推测出:不一样的浏览器对Window的实现应该是不同的,至少在许多细节上会有不一样, 也就是这些不一样的浏览器分别拥有并不太同样的Window对象
而javascript在宿主环境(浏览器)上运行的时候, 会把当前浏览器Window对象做为本身的Global对象,这时候,“从表面上看”, Window对象和Global对象“是同一个”
因此说javascript运行程序就是一个处处混吃混喝的主,找到哪一个“东家”(宿主环境/浏览器),就把东西的Window对象“偷”出来, 当成本身的Global对象
如下场景是我想象出来的:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
javascript敲了敲门:“诶,老谷啊(谷歌)! 我又上你家串门来了,那个啥。。。。。”
门开了
谷歌浏X器瞧见是JS, 从怀里掏出Window对象来,摆摆手:“走! 走! 走!!!”
javascript把Window对象放进本身的Global口袋里,心满意足地走了。
“明儿去老狐家和safari家吧! IE家太寒碜,我就不去了”
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DOM元素对象
例如 var a = document.createElement('div'); 取得的就是一个DOM元素对象
DOM元素对象也是浏览器提供的东西, 因此它并不像javascript标准里的其余对象那样服从“控制”
它有如下一些特色:
• 没法写覆盖;
• 包含一些预约义的只读属性;
• 包含没法将 this 重载为其余对象的方法
下面讲的这些东西, 有些你可能比较少用到, 但它们绝对有理由会坑到你,并且还会。。。。坑死你。。。
恼人的undefined
为了进一步明确undefined的行为, 这里我引入 undeclared的概念:
1.undefined表示变量声明了, 但没有赋值, 不会报错
2.undeclared表示变量历来就没有声明, 这会致使报错
var b; console.log(b); // 输出undefined
console.log(a); // 直接报错
究竟是什么让咱们对undefined一脸懵蔽?
首先我要告诉你上面1,2两条已经足以表征undefined和 undeclared的不一样了,但不少时候咱们仍会搞混,为何呢?
由于javascript会 故!意!搞!事!(此处有褒有贬)
1. 通常状况下使用一个未声明(undeclared)的变量是会直接报错的,但typeof运算符的“安全机制”会规避这一点, 例如:
// 此处a未声明 typeof a;
中使用了不曾声明过的变量a,可是无报错发生,并且此时返回的是undefined!!
也就是说typeof的安全机制把 undeclared的行为改变了,且和undefined同样, 这会让咱们感到困惑(要注意typeof中的这种变量行为只是一种特殊状况,不要奉为圭筹)
2. 输出undeclared变量的时候会报错,可是此时的输出具备迷惑性:
console.log(a) // a未声明时输出 Uncaught ReferenceError: a is not defined
实际上,这里的not defined若是改成undeclared或许会更好理解一些
为何要这样作呢? 这固然是有缘由的, 这让咱们能够安心地判断一个变量的类型,即便它不存在也不会报错, 咱们的程序
1. 浮点数计算失真问题
由于有些小数表示成二进制是无限循环的, 这致使没法“恰好”计算正确
console.log(0.2 + 0.5 === 0.7); // true console.log(0.5+ 0.5 === 1.0); // true console.log(0.2 + 0.4 === 0.6); // false console.log(0.2 + 0.1 === 0.3); // false
2. 诡异而有趣的NaN
首先你要搞清楚的一个问题是NaN的含义
让咱们猜猜它是什么意思:
1. NaN不是number,也就是number以外的类型, 例如字符串,布尔值等等
2. NaN属于number类型,只不过是一种很是特殊的number的值, 为NaN
NaN属于第2种而不是第1种!!也就是说字符串, 布尔值表面上是Not A Number(也即NaN的表面意思) , 但它们和NaN是八竿子打不着的关系,不要弄混了。
而在这里,为了让咱们能把NaN的概念变得混乱, javascript的一个糟心的API却开始了他的表演。。。
没错, ES5里面全局的isNaN方法不能区分纯粹的NaN和字符串等“非数字”
console.log(isNaN(NaN)); // true console.log(isNaN("看清楚老子是字符串不是NaN!!")); // true
ES6把isNaN方法归入到了Number封装对象中, 并对这个糟糕的情况进行了改进:
console.log(Number.isNaN(NaN)); // true console.log(Number.isNaN("看清楚老子是字符串不是NaN!!")); // false
NaN的自反特性
NaN最诡异的地方在于: NaN是不等于NaN的!
console.log(NaN === NaN) // false
在全部数值类型中独一无二的逆天特性
在ES6前怎么检测"纯粹"的NaN呢? (不要误判字符串等)
1.事实上是这样作的
// 省略诸多内容, 只留关键一句 return typeof n === "number" && window.isNaN( n )
其实还有另一种更简洁的方式,
2.利用NaN的自反特性:
// 省略诸多内容, 只留关键一句 return n !== n;
布尔值false的替身:能充当false的“假值”
这要从if条件语句开始提及:
if(someValue){ }
做为一个JSer, 你绝对知道把undefined, null, 0 填上去会发生什么
对, 由于类型转换的机制, 它们最终都会等同于false
而undefined, null, 0就是我上面所说的 “假值”
但显然又又又又又又有东西会让咱们搞混(啊!javascript你怎么老这样啊!!)
在这里我问你们
1."" "undefined", "null", "0", 是假值吗?(在判断条件下能被类型转换为false吗)
2. new Boolean(false), new Number(0), new String("")是假值吗?
3. [] (空数组), {}(空对象) function () { } 是假值吗?
若是你是一个有经验的JS开发者,这有可能不是什么难题, 但我认为这些“判断题”对初学JS的人来讲的确不那么友好
下面我给三点论断让你们可以正确判断
1. 除空字符串("")外的字符串的都不是假值,而是真值 (一击击破 "" "undefined", "null", "0"所形成的认知混乱)
2. 凡是对象的都不是假值, 而是真值 (一击击破. [] , {}, function () { } 所形成的认知混乱)
【注意】对于2中请注意数组和函数本质上也是对象!
3.真正的假值只有屈指可数的那几个:
1. undefined
2. null
3. false
4. +0 、 -0 和 NaN
5. ""(空字符串)
嗯嗯,就这样
强大的模板字符串
你可能遇到过这种问题:
有些时候你会写一些HTML字符串
而后当它变长一些的时候你决定要换行(其实主要是以为不换行太难看了)
而后换行后你就看到了这一幕:
嗯嗯, 看到下面的那个</div>颜色变了你就知道这绝对不是什么好兆头!!(实际上运行也会报错的,由于编译时候会认为下面的</div>前面缺乏字符串 ' 的符号)
因而你可能这样干
var str = '<div>' + '</div>'
但仔细想想, 你的HTML代码哪怕只长一点点就可能变成这样:
var str = '<html>' + '<head>' + '<meta charset="utf-8" />' + '<title></title>' + '</head>' + '</html>'
简直恐怖!!你把大量的工做花费在了写+号上和写单引号上 (虽然以我歪曲的审美以为这段代码看起来挺“漂亮”的)
而当你使用模板字符串时就不用换行了:
var str = `<html> <head> <meta charset="utf-8" /> <title></title> </head> </html>`
固然了, 更多时候咱们习惯于用模板字符串作字符串的动态插值
它能够把
var name = 'penghuwan' console.log('my name is ' + name);
变成
var name = 'penghuwan' console.log(`my name is ${ name }`);
形式上更加方便简洁, 可读性也大大加强了
总结一下模板字符串的做用:
1.容许多行
2. 简洁插值
【注意】: 模板字符串是ES6的特性
参考资料:
《你不知道的javascript》 — — [美] Kyle Simpson
《javascript高级语言程序设计》— — [美] Nicholas C·Zakas
【完】