【javascript】详解变量,值,类型和宿主对象

JS数据类型

JS类型分类

讲到类型, 首先要说的当然是JS的类型分类, 对于这一点,《javascript高级语言程序设计》和《你不知道的javasvript》的阐述是有差异的(但想表达的意思是相同的)

我更倾向于前一本书(红宝书)的定义: javascript的数据类型可分为两种: 基本类型和引用类型基本类型: String, Number, Boolean, Null, undefined 和Symbol 引用类型: Object, Array, Function, RegExp, Date, 封装类型(基本包装类型)

我前面说了, 两本书对类型分类的阐述是有差异的, 关键出现在引用类型的Object身上因为Object是个特殊的存在, 事实上我们知道所有其他的引用类型如Array, Function, RegExp等都属于Object, 也就是说Object是这些的“父类型"

对此: 《你不知道的javasvript》里把其他引用类型一并归结为Object 《javascript高级语言程序设计》则是根据“外观”把Object和Array等放在平行的位置,就是说“看上去像” { }的是对象, 而像[ ]这样的, 虽然也是对象, 但我们还是叫它数组吧

弱类型的JS

在了解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变量的(不要和前面的东西混淆了哦)

typeof和instanceof, 各有千秋

// 检测基本类型
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

可以看到,纯粹的基本类型是不能够检测出来的, 而要转化成对应的基本包装类型才能检测出来,当然了, 我猜大多数时候你都不会这么干

检测null的3种方式

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

闲话javascript类型转换

字符串转数字

字符串转为数字有两种方式: 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却避之唯恐不及

对这些值:

  • 作为单个值使用的时候会一律返回undefined
JSON.stringify( undefined ); // undefined
JSON.stringify( function(){} ); // undefined
  • 在数组中出现会将其重置为null
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对象来,摆摆手:“走! 走! 走!!!” javascriptWindow对象放进自己的Global口袋里,心满意足地走了。 “明儿去老狐家和safari家吧! IE家太寒碜,我就不去了”

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

DOM元素对象

例如 var a = document.createElement('div'); 取得的就是一个DOM元素对象 DOM元素对象也是浏览器提供的东西, 所以它并不像javascript标准里的其他对象那样服从“控制” 它有以下一些特点:

• 无法写覆盖; • 包含一些预定义的只读属性; • 包含无法将 this 重载为其他对象的方法

基本类型大杂谈——聊聊那些坑点

下面讲的这些东西, 有些你可能比较少用到, 但它们绝对有理由会坑到你,而且还会。。。。坑死你。。

undefined篇

恼人的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或许会更好理解一些

为什么要这样做呢? 这当然是有原因的, 这让我们可以安心地判断一个变量的类型,即使它不存在也不会报错, 我们的程序

Number篇

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;

Boolean篇

布尔值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. ""(空字符串)

嗯嗯,就这样

String篇

强大的模板字符串

你可能遇到过这种问题: 有些时候你会写一些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

【完】

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏owent

打造最快的Hash表(转) [以暴雪的游戏的Hash为例]

先提一个简单的问题,如果有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做?

763
来自专栏GopherCoder

专栏:003:正则表达式

1727
来自专栏后端技术探索

是时候来彻底了解字符编码了!!

你是否认为“ASCII码 = 一个字符就是8比特”?你是否认为一个字节就是一个字符,一个字符就是8比特?你是否还认为你是否还认为UTF-8就是用8比特表示一个字...

752
来自专栏King_3的技术专栏

leetcode-137-Single Number II-第二种解法

1.8K11
来自专栏数据处理

proc-tabulate-report

1374
来自专栏工科狗和生物喵

【毕设进行时-工业大数据,数据挖掘】Java读取文本数据转化为二维数组

其实没什么好说的,就是一个文本读取类。当然,为了体现效果,我还这几在里面留了一个测试的数据。如果本身有数据的话把测试的数据删掉就可以了。这个基本上是通用吧,很多...

1133
来自专栏AzMark

Python 学习笔记之类「面向对象,超类,抽象」

在面向对象编程中,术语对象大致意味着一系列数据 (属性) 以及一套访问和操作这些数据的方法。

882
来自专栏HTML5学堂

原生JS | 当兔子遇到鸡

HTML5学堂-码匠:当兔子遇到鸡,会怎样呢?先别急,看个小视频~ 视频内容 当兔子遇到鸡 —— 不要害怕和别人不一样,在这个世界上,你就是独一无二的自己! 不...

37710
来自专栏好好学java的技术栈

“365算法每日学计划”:java语言基础题目及解答(11-15打卡)

自从开始做公众号开始,就一直在思考,怎么把算法的训练做好,因为思海同学在算法这方面的掌握确实还不够。因此,我现在想做一个“365算法每日学计划”。

641
来自专栏青蛙要fly的专栏

Android技能树 — 排序算法基础小结

现在安卓面试,对于算法的问题也越来越多了,要求也越来越多,特别是排序,基本必考题,而且还动不动就要手写,所以陆续要写算法的文章,也正好当自己学习。o(╥﹏╥)o

662

扫码关注云+社区