前面两章介绍了几大数据类型以及值类型,接下来的这个知识点,我以为它对于javascript程序员来讲是很重要的,javascript
在开始以前,咱们先看一个例子,以便以后更轻松的理解封装对象的概念。java
"tick".toUpperCase //function toUpperCase() String.prototype.toUpperCase //function toUpperCase() "tick".toUpperCase === String.prototype.toUpperCase //true // 这里使用恒等比较判断 常量的方法是否和Sting构造函数中的方法为同一个.
咱们先来阅读如下几条知识点,以避免对下文作出更好的理解。程序员
经过直接量的方式访问方法或属性,这种值咱们称之为常量方式
例如上面的第一行代码。es6
在JavaScript中对象类型包括:对象,数组,函数
这三种子类型,咱们一般有两种术语,引用类型 复合值
,引用类型值在作恒等
比较时比较的是他们内存指向
,便是否引用同一个值.web
javascript对象是一种复合值,它是属性或已命名值的集合,经过.
符号来读取属性值。json
经过上面的代码咱们能够看到,在使用常量方式访问某个方法时,依然会返回其
数据类型对应的内置构造函数方法
,上面的第三点已经说了,javascript经过.
操做符来访问属性或方法,但是常量
真的是对象吗?它在访问属性的过程当中底发生了什么?别急,跟随个人脚步,下面我会不遗余力把我知道的,个人观点通通都说出来.数组
在JavaScript中全部复合类型
(如:对象,数组,函数)都包含一个内部属性[[calss]]
,此属性能够看做是一个内部分类。它并非传统面向对象上的类,因为是内部属性,因此咱们没法直接访问,不过,能够转换为字符串来查看.浏览器
Object.prototype.toString.call([1,2,3]) // '[Object Array]' Object.prototype.toString.call(/^[1,2]$/) // '[Object RegExp]'
这里补充一点,咱们也能够经过此种方式去判断一个对象是否为数组。安全
咱们看到每一个不一样的常量类型
中的[[class]],都对应着它们相应的内部构造函数
,也就是对象的内部[[Class]]属性和建立该对象的内建原生构造函数相对应,但有些特例
.性能优化
//一说特例,我估计就有人想到javascript中比较蛋疼的两个类型 Object.prototype.toString.call(null) // '[Object Null]' Object.prototype.toString.call(undefined) // '[Object Undefined]'
除了null和undefined,其余都是javascript的内置构造函数。这里再次说一个小细节,Infinity
和NaN
它们返回什么呢?我想不用说你们也能够猜到了,它们都属于Number类型.
Object.prototype.toString.call(42) // '[Object Number]' Object.prototype.toString.call("42") // '[Object String]' Object.prototype.toString.call(true) // '[Object Boolean]'
上面的例子除了null和undefined,它们都有各自的构造类,这些类是javascript内置的.
在平常开发中,咱们一般不直接使用内置的构造类,而是直接经过常量访问.
var arr = new String("1234") arr // {0:"1",1:"2",2:"3",3:"4"}
经过构造函数实例出来的常量变成了对象,其实就是手动建立其封装对象,封装对象上存在对应的数据类型方法。咱们在使用常量的方式直接访问属性和方法时,javascript会自动为你包装一个封装对象,至关于上面咱们手动包装在操做属性或方法完成以后JavaScript也会释放当前封装对象
说到这里,咱们可能会想到一个问题,若是须要常常用到这些字符串的属性和方法,好比在for循环当中使用i<a.length,那么一开始建立一个封装对象也许更为方便,这样JavaScript引擎就不用每次都自动建立和自动释放循环执行这些操做了。
其实咱们的想法很好,但实际证实这并非一个好办法,由于浏览器已经为.length这样常见状况作了性能优化,直接使用封装对象来提早优化
代码反而会下降执行效率
。
通常状况下,咱们不须要直接使用封装对象,最好是让JavaScript引擎自动选择何时应该使用封装对象,换句话说,就是应该优先考虑使用'abc'和42这样的原始类型值,而非new String(‘abc’)和new Number(42)
看以下代码,思考它们的执行结果:
var test = 'abc'; test.len = 123; var t = test.len;
此处t为undefined
,第三行是经过新的原始对象访问其.len
属性,这并非上次添加的.len
,上次的已经被销毁,当前是一个新的封装对象.
说了这么多的理论与例子,不如咱们从头至尾来整理一下,经过基础类型值访问属性过程当中,到底发生了什么。
var s = 'hello world'; var world = s.toUpperCase();
咱们就以它为例:
首先javascript会讲字符串值经过new String(s)的方式转换为封装对象
,这个对象继承了来自字符串构造函数的全部方法(这些操做都从第二行访问方法时开始发生),当前s已经变成了一个封装对象
,接下来在封装对象中查找须要的方法或属性,找到了以后作出相应的操做.一旦引用结束,这个新建立的对象就会销毁。这时候s.toUpperCase已经运行了该方法,随即销毁封装对象。
想要等到封装对象中基本类型值,咱们可使用valueOf方法获取。
var ss = new String("123"); ss.valueOf() //"123"
javascipt 在须要用到封装对象中基本类型值时,会发生自动转换,即隐式强制类型转换
var t = new String("123"); t+""; "123"
javascript原始值(undefined null 字符串 数字 布尔)是不可修改的
,而对象是能够被引用和修改的
.讲值类型那章时咱们说过JavaScript变量没有所谓的类型而言,衡量类型的是值,即值类型
,在原始值上,咱们没法更改,任何方法都没法更改一个原始值,这对于数字自己就说不通,怎么更改数字自己呢?可是对于字符串来讲,看似有点说得通
由于它像是经过字符组成的数组,咱们能够指望经过指定的索引来修改其字符元素,但javascript并不容许这么作。字符串方法看上去返回了修改后的值,其实返回的是一个新字符串,与以前的没有任何关系.
封装对象与类型转换有很大关联,咱们只有弄懂封装对象的概念,才能更好的理解类型转换,还有以后的相等比较与恒等比较。
var num = 123; //1 num.toString(); // "123" //2 num + ""; //"123"
上面两种方式,第一种咱们称为显示强制类型转换
.第二种称之为隐式强制类型转换
。类型转换老是返回基本类型值
,不会返回对象类型,
第一个,num.toString()时,把num常量经过内部[[class]]生成临时的封装对象,再调用对象toString方法,返回转换完成的字符串.
第二个,因为+运算符的其中一个操做数是字符串,因此是字符串拼接操做,结果是数字123被转换成“123”。
介绍强制与隐式类型转换时,咱们须要掌握对字符串数字和布尔类型的转换规则。
基本类型的转换规则为,undefined -> “undefined”, null->"null",true->"true",数字则使用通用规则,若是有极大或极小的值则使用指数形式.
var num = 1.37 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 num.toString() //"1.37e+21"
普通对象除非自定义,不然它会返回对象内部的[[Class]]属性.
var obj = {}; obj.toString() //"[object Object]"
数组的toStirng有些特殊,它是经过","链接的字符串.
var arr = [1,2,3,4]; arr.toString(); // "1,2,3,4"
这里捎带讲一下json字符串化.
JSON.stringify在将JSON对象序列化为字符串时也使用了toString方法,但须要注意的是JSON.stringify并不是严格意义上的强制类型转换,只是涉及toString的相关规则.
var num = 123; var str = “123”; JSON.stringify(num) //“123” JSON.stringify(str) // “”123”” //两个引号
全部安全的JSON值均可以使用JSON.stringify序列化,那么, 何为不安全的值?例如: undefined,function,Symbol(es6新增),若是JSON中出现这些值,序列化时不能把它们识别,就把他们变成了null。
JSON.stringify([1,undefined,function(){}]) //"[1,null,null]"
若是对象定义了toJSON方法,会先调用此方法,而后用它的返回值来进行序列化。
var obj = {name:"Jack"} obj.toJSON = function(){ return {name:”Join"} } JSON.stringify(obj) // “{“name”:”Join"}"
在序列化以前,会先调用对象的toJSON方法,以它的返回值来进行序列化.默认对象是没有此属性的,若是有须要能够手动添加。
toJSON返回的不是一个通过JSON字符串化后的值,它应该是一个适当
的值,也就是没有通过任何处理的值.固然,它应该被返回一个能够被JSON化的值.
var ob = { val : [1,2,3], toJSON : function(){ return this.val.slice(2) } } var obj = { val : [1,2,3], toJSON: function(){ return this.val.slice(1).join() } } JSON.stringify(ob) //“[2,3]" JSON.stringify(obj) //“”2,3”” 双引号
以上咱们讲JSON字符串化彻底是toString捎带出来的,它和toString很类似,可是却还有些不一样。既然上面已经讲了些,咱们不妨再来看看它的几个参数.
不少开发者在使用JSON字符串化时候,只使用了第一个参数,其实它是有三个参数的。
咱们先来看第一个参数和第二参数.
var obj = { a:42, b:”42", c:[1,2,3] } JSON.stringify(obj,function(k,v){ if(k !== "c" ) return v })
第一个参数不用多介绍了吧,主要是第二个参数,它有两个参数,和map的参数正好相反,也有filter方法的那点意思,在JSON字符串化中指定哪些属性应该被处理.
等等,这里有个小细节,以上第二个回调参数实际上比我预想的多执行了一次,假设以上为例,三个属性,它第一次为undefined,第二次才是属性a。这是为何呢?由于它在处理时,obj也计入其中了。
第三个参数是缩进的字符,若是它是一个数字,就表明缩进多少空格符。若是是字符串,则固定以它为缩进符号。
var obj = { a:42, b:”42”, c:[1,2,3] } JSON.stringify(obj,function(k,v){ if(k !==“c”) return v },”---") //"{ //----"a": 42, //----"b": "42” //}"
在编辑代码时,因为编辑器缘由,引号的格式很很差把握,因此你们在复制代码运行时可能会出错,需检查引号是否为中文格式.
有时候咱们须要将非数字类型当作数字来使用,好比说数字运算
其中true转1,false转换为,null转换为0,undefined转换为NaN
toNumber时如不能转换为数字类型就会返回NaN,它对以0开头的数字并非以16进制来处理,而是10进制
var str = “123”; str - 0; //123 Number(str) //123
字符串转为number很简单,这里不作介绍。让咱们来看一下复合类型是如何转换为Number类型的
.
认真读下面这句话:
为了将值转化为基本类型值,抽象操做ToPrimite(参见ES5规范9.1节)会首先(经过内部操做DefaultValue)检查该值是否具备valueOf()方法,若是有就返回基本类型值,并使用该值进行强制类型转换
,若是没有就使用toString()的返回值来进行强制转换.`若是valueOf()和toString()均不返回基本类型值,则会产生TypeError错误
var obj = { valueOf:function(){ return "42" } } var obj_1 = { toString:function(){ return "42" } } var arr = [1,2,3] arr.toString = function(){ return this.join(“") } //“123" Number(obj) //42 Number(obj_1) //42 Number(arr) // 123
关于布尔值,咱们存在许多误解和困惑,须要咱们特别注意.
javascript中有两个关键词true和false,分别表明布尔类型中的真和假,咱们常误认觉得数值1和0分别等同于true和false,在有些语言中多是这样,但在javascript中布尔值和数字时不同的,虽然咱们能够将1强制类型转换为true,将0强制转换为false,反之亦然,但他们并非一回事.
咱们能够把他们分为两类
(1) 能够被强制转换为false的值
(2) 其余(被强制类型转换为true的值)
假值
JavaScript规范具体定义了一小撮能够被强制类型转换为false的值。
如下是一些假值:
undefined
null
false
+0 -0 和 NaN
“”
假值的布尔强制类型转换结果为false.
虽然javascript规范没有明确指出除了假值之外都是真值,但咱们能够暂时理解为假值之外的值都是真值
。
假值对象不是假值
var bool = new Boolean(false); var number = new Number(0); var string = new String(“0”);
这些都是假值封装后的对象
,让咱们来用复合条件来判断一下.
var a = new Boolean( bool && number && string ); a //true
咱们都知道web端的javascript依赖两个环境,javascript语言引擎,与宿主环境,宿主环境包括 DOM BOM,咱们为何说起它们呢?
由于document里面有一个类数组对象它会被转换为false,它包含了页面中全部的元素。
var dom = document.all; Boolean( dom ) //false
真值
var a = “false” var b = “0” var c = “‘'" Boolean( a && b && c ) // true
上例的字符串看似假值,但全部字符串都是真值,不过””除外,由于它是假值列表中的惟一字符串.
真值列表能够无限长,没法一一列举,因此咱们以假值做为参考。
真值有不少,能够无限延长,[],function,{} 都是真值。你们能够用一点时间去控制台上练习。