原生函数
1、常见的原生函数有:
- String()
- Number()
- Boolean()
- Array()
- Object()
- Function()
- RegExp()
- Date()
- Error()
- Symbol() --- ES6中新加入的
2、所有typeof返回值为"object"的对象(如数组)都包含一个内部属性。我们可以通过Object.prototype.toString()来查看。(对象的内部[[Class]]属性和创建该对象的内建原生构造函数相对应)
Object.prototype.toString.call([1,2,3]) // "[object Array]" Object.prototype.toString.call(null) // "[object Null]" Object.prototype.toString.call(undefined) // "[object Undefined]"复制代码
3、如果想要自行封装基本类型值,可以使用Object()
函数(不带new关键字)
var a = "abc"; var b = new String(a); var c = Object(a); typeof a; // "string" typeof b; // "object" typeof c; // "object" b instanceof String // true c instanceof String // true Object.prototype.toString.call(b) // "[object String]" Object.prototype.toString.call(c) // "[object String]"复制代码
4、如果想要得到封装对象中的基本类型值
,可以使用valueOf()
函数。
var a = new String('abc'); var b = new Number(42); var c = new Boolean(true); var d = a + ""; // 隐式拆封 a.valueOf() // "abc" b.valueOf() // 42 c.valueOf(); // true typeof d // "string"复制代码
5、Array构造函数只带一个数字
参数的时候,该参数会被作为数组的预设长度
,而非只充当数组中的一个元素
。
var a = new Array(1,2,3) a // [1,2,3]复制代码
6、我们将包含至少一个"空单元"
的数组称为稀疏数组
。
7、创建数组的不同的方式会有所区别
var a = new Array(3); var b = [undefined,undefined,undefined] a.join("-"); // "--" b.join("-"); // "--" a.map(function(v,i){ return i}); // [undefined * 3] b.map(function(v,i){ return i}); // [0,1,2] function fakeJoin(arr,connector){ var str = ""; for(var i = 0;i0){ str += connector } if(arr[i]!==undefined){ str += arr[i] } } return str; }复制代码
8、new Object() 可以创建对象,但是无法像常量形式那样一次设定多个属性。new Function() 比如动态定义函数参数
和函数体
的时候可以创建函数。 new RegExp() 可以动态定义正则表达式
。
var c = new Object(); var e = new Function("a","return a*2;") // 相当于var f = function(a){ return a * 2} var name = 'xxx' var namePattern = new RegExp("\\b(?:"+name+")+\\b","ig"); var matches = someText.match(namePattern) 复制代码
9、Symbol
- 符号是具有唯一性的特殊值(并非绝对),用它来命名对象属性不容易导致重名。
- ES6中有一些预定义符号,以Symbol的静态属性形式出现,如
Symbol.create
、Symbol.iterator
// 可以自定义对象的迭代器,for..of可以遍历值 obj[Symbol.iterator] = function(){}复制代码
var mysym = Symbol('symbol') mysym // Symbol(symbol) mysym.toString() //"Symbol(symbol)" typeof mysym // "symbol" var a = {} a[mysym] = 'foobar' Object.getOwnPropertySymbols(a) // [Symbol(symbol)]复制代码
强制类型转换
值类型转换
1、将值从一种类型转换为另一种类型通常称为类型转换
,这是显式的情况;隐式的情况称为强制类型转换
。
2、Javascript中的强制类型转换总是返回标量基本类型值
,如字符串、数字和布尔值,不会返回对象和函数。
3、类型转换发生在静态类型语言的编译阶段
,而强制类型转换发生在动态类型语言的运行时(runtime)
ToString
1、抽象操作ToString,它负责处理非字符串到字符串的强制类型转换。null转换为"null",undefined转换为"undefined",true转换为"true"。
2、对普通对象
来说,除非自行定义,否则toString()返回内部属性
[[class]]的值,如"[object Object]"
3、如果对象有自己的toString()方法,字符串化时就会调用该方法并使用其返回值。
4、数组
的默认toString()方法经过了重新定义,将所有单元字符串化以后在用,连接起来
。
var a = [1,2,3] a.toString(); // "1,2,3"复制代码
JSON字符串化
1、JSON字符串化(JSON.stringify)并非严格意义上的强制类型转换。安全的JSON值都可以使用JSON.stringify字符串化。安全的JSON值是指能够呈现为有效的JSON格式的值。
2、不安全的JSON值:
- undefined
- symbol
- function
- 包含循环引用(对象之间相互引用,形成一个无限循环)的对象。
3、JSON.stringify()在对象
中遇到undefined、function和symbol时会自动将其忽略
。在数组
中则会返回null
(以保证单元位置不变)。对包含循环引用的对象执行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}"复制代码
4、如果对象中定义了toJSON()
方法,JSON字符串化时会首先调用该方法,然后用它的返回值
来进行序列化
。(如果含有非法JSON值的对象做字符串化,或者对象中的某些值无法序列化时,就需要定义toJSON()方法来返回一个安全的JSON值
)
var o = {} var a = { b:41, c:o, d:function(){} } // 循环引用 o.e = a; JSON.stringify(a) // 产生错误 a.toJSON = function(){ // 序列化仅包含b return {b:this.b} } JSON.stringify(a) // "{"b":42}"复制代码
5、toJSON() 返回的应该是一个适当的值,可以是任何类型
,然后在由JSON.stringify()对其进行字符串化。(也就是说返回一个能够被字符串化的安全的JSON值
,而不是返回一个JSON字符串
)
var a = { val:[1,2,3], toJSON:function(){ return this.val.slice(1); } } JSON.stringify(a); // "[2,3]"复制代码
6、可以向JSON.stringify()传递一个可选参数replacer,它可以是数组
或者函数
。用来指定对象序列化过程中哪些属性应该会处理
,哪些应该被排除,和toJSON很像。
- 如果replacer是一个数组,它必须是一个
字符串数组
,其中包含序列化要处理的对象的属性名称
。 - 如果replacer是一个函数,它会对对象本身调用一次,然后对对象中的每个属各调用一次,每次传递两个参数,
键
和值
。如果要忽略某个键就返回undefined
,否则返回指定的值。
var a = { b:42, c:"42", d:[1,2,3] } JSON.stringify(a,["b","c"]) // "["b":42,"c":"42"]" JSON.stringify(a,function(k,v){ if(k!=="c") return v }) // "{"b":42,"d":[1,2,3]}"复制代码
- JSON.stringify还有一个可选参数space(第三个参数),用来指定输出的
缩进格式
。space为正整数
时是指定每一级缩进的字符数
,还可以是字符串
,此时最前面的十个字符
被用于每一级的缩进。(感觉没啥大用)
ToNumber
1、true转换为1,false转换为0。undefined转换为NaN,Null转换为0。
2、对象(包含数组)转换为基本的类型值,会按照如下规则:
- 检查该值是否有valueOf的方法。如果有
并且返回基本类型值
,就使用该值进行强制类型转换。 - 如果没有的话就使用toString的返回值(如果存在)来进行强制类型转换。
- 如果上面两个均不返回基本类型值,会产生TypeError错误。
3、使用Object.create(null)
创建的对象[[Prototype]]属性为null,并且没有valueOf和toString方法,因此无法进行强制类型转换
。
ToBoolean
1、以下这些是假值:
- undefined
- null
- false
- +0、-0和NaN
- ""
2、假值对象(了解以下吧)
document.all
显示强制类型转换
字符串和数字之间的转换
1、字符串=>数字
- Number()
- +(一元运算符)
2、数字=>字符串
- String()
- toString()
var a = 42; var b = a.toString() var c = "3.14" var d = +c; b // "42" d // 3.14复制代码
3、一元运算符+的另一个常见用途是将日期(Date)对象强制类型
转换为数字
,返回结果为Unix时间戳,以毫秒为单位。
var d = +new Date() d // 1560354322864 // 别的方式获取时间戳 var timestamp = new Date().getTime() // Date.now if(!Date.now){ Date.now = function(){ return +new Date(); } }复制代码
~运算符(非)
- ~x == -(x+1) (记住这一个就行了)
- 使用场景,if(~a.indexOf(..)),如果if判断的结果为false,证明不存在,否则就是存在。
显示解析数字字符串
1、parseInt解析允许字符串中含有非数字字符
,解析从左到右的顺序,如果遇到非数字字符就停止。而转换不允许出现非数字字符,否则会失败并返回NaN。(解析浮点数可以使用parseFloat())
var a = "42" var b = "42px" Number(a) // 42 parseInt(b) // 42复制代码
2、早期版本的parseInt()有一个问题,如果没有第二个参数来指定的基数,parseInt会根据字符串的第一个字符来自行决定基数
。如果第一个字符是x或X,则转换为十六进制数字。如果是0,则转换为八进制数字。(注意ES5之后默认转换为十进制数。如果第二个参数不传或者false值,按照十进制处理。)
显示转换为布尔值
1、一元运算符 ! 显式地将值强制类型转换为布尔值。但是它同时还将 真值反转为假值(或者将假值反转为真值)。所以显式强制类型转换为布尔值最常用的方法是 !!,因为第二个 ! 会将结果反转回原值。
隐式强制类型转换
字符串和数字之间的隐式强制类型转换
1、根据 ES5 规范 11.6.1节,如果某个操作数是字符串
或者能够通过以下步骤转换为字符串
的话,+ 将进行拼接操作
。如果其中一个操作数是对象(包括数组),则首先对其调用 ToPrimitive 抽象操作(规范 9.1 节),该抽象操作再调用 [[DefaultValue]](规范 8.12.8 节),以数字作为上下文。 2、如果 + 的其中一个操作数是字符串
(或者通过以上步骤可以得到字符串), 则执行字符串拼接;否则执行数字加法。
var a = [1,2]; var b = [3,4]; a + b; // "1,23,4"复制代码
3、我们可以将数字
和空字符串 ""
相 + 来将其转换为字符串
var a = 42; var b = a + ""; b; // "42"复制代码
4、a + ""(隐式)和前面的String(a)(显式)之间有一个细微的差别需要注意。根据 ToPrimitive抽象操作规则,a + ""会对a调用valueOf()方法
,然后通过ToString
抽象 操作将返回值转换为字符串
。而 String(a) 则是直接调用 ToString()
。
var a = { valueOf: function() { return 42; }, toString: function() { return 4; } }; a + ""; // "42" String( a ); // "4"复制代码
5、-是数字减法运算符,因此a - 0会将a强制类型转换为数字。也可以使用a * 1和a / 1。
var a = "3.14"; var b = a - 0; b; // 3.14 var a = [3]; var b = [1]; a - b; // 2复制代码
布尔值到数字的隐式强制类型转换
1、可以使用!!来将值转换为布尔值,再通过 Number(..) 显式强制类型转换为 0 或 1。
隐式强制类型转换为布尔值
1、下面的情况会发生 布尔值隐式强制类型转换:
- if (..)语句中的条件判断表达式。
- for ( .. ; .. ; .. )语句中的条件判断表达式(第二个)。
- while (..) 和 do..while(..) 循环中的条件判断表达式。
- ? :中的条件判断表达式。
- 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)。
|| 和 &&
1、&& 和 || 运算符的返回值并不一定是布尔类型,而是两个操作数其中一个的值。 2、使用|| 和 &&注意以下几点:
- || 和 && 首先会对第一个操作数(a 和 c)执行条件判断,如果其不是布尔值(如上例)就 先进行 ToBoolean 强制类型转换,然后再执行条件判断。
- 对于 || 来说,如果条件判断结果为 true 就返回第一个操作数(a 和 c)的值,如果为 false 就返回第二个操作数(b)的值。
- && 则相反,如果条件判断结果为 true 就返回第二个操作数(b)的值,如果为 false 就返 回第一个操作数(a 和 c)的值。
var a = 42; var b = "abc"; var c = null; a || b; a && b; c || b; c && b; // 42 // "abc" // "abc" // null复制代码
符号的强制类型转换
1、ES6 允许 从符号到字符串的显式强制类型转换,然而隐式强制类型转换会产生错误。 2、符号不能够被强制类型转换为数字(显式和隐式都会产生错误),但可以被强制类型转换 为布尔值(显式和隐式结果都是 true)
var s1 = Symbol( "cool" ); String( s1 ); // "Symbol(cool)" var s2 = Symbol( "not cool" ); s2 + ""; // TypeError复制代码
宽松相等和严格相等
1、常见的误区是“== 检查值是否相等,=== 检查值和类型是否相等”。正确的解释是:“== 允许在相等比较中进行强制类型转换
,而 === 不允许。”
抽象相等(挺重要的!!)
1、字符串
和数字
之间的相等比较
ES5 规范 11.9.3.4-5 这样定义:
- 如果 Type(x) 是数字,Type(y) 是字符串,则返回 x == ToNumber(y) 的结果。
- 如果 Type(x) 是字符串,Type(y) 是数字,则返回 ToNumber(x) == y 的结果。
var a = 42; var b = "42"; a === b; // false 不进行强制类型转换比较 a == b; // true 进行强制转换后进行比较复制代码
2、其他类型和布尔类型之间的相等比较
ES5 规范 11.9.3.6-7 这样定义:
- 如果 Type(x) 是布尔类型,则返回 ToNumber(x) == y 的结果。
- 如果 Type(y) 是布尔类型,则返回 x == ToNumber(y) 的结果。
var a = "42"; var b = true; a == b; // false复制代码
3、null 和 undefined 之间的相等比较
ES5 规范 11.9.3.2-3 这样定义:
- 如果 x 为 null,y 为 undefined,则结果为 true。
- 如果 x 为 undefined,y 为 null,则结果为 true。
在 == 中 null 和 undefined 相等(它们也与其自身相等),除此之外其他值都不存在这种 情况
。
var a = null; var b; a == b; // true a == null; // true b == null; // true a == false; // false b == false; // false a == ""; // false b == "";// false a == 0;// false b == 0;// false 复制代码
4、对象和非对象之间的相等比较
ES5 规范 11.9.3.8-9 这样定义:
- 如果 Type(x) 是字符串或数字,Type(y) 是对象,则返回 x == ToPrimitive(y) 的结果;
- 如果 Type(x) 是对象,Type(y) 是字符串或数字,则返回 ToPromitive(x) == y 的结果。
var a = 42; var b = [ 42 ]; a == b; // true var a = 'abc' var b = Object(a) a===b // false a==b // true复制代码
5、极端情况
[] == ![] // true复制代码
让我们看看 ! 运算符都做了些什么?根据 ToBoolean 规则,它会进行布尔 值的显式强制类型转换(同时反转奇偶校验位)。所以[] == ![]变成了[] == false。 []=>''=>0,而false=>0 ,所以两边自然就相等了。
抽象关系比较
ES5 规范 11.8.5 节定义:
- “抽象关系比较”(abstract relational comparison),分为两个部 分:比较双方都是字符串(后半部分)和其他情况(前半部分)。
1、比较双方首先调用 ToPrimitive,如果结果出现非字符串,就根据 ToNumber 规则将双方强 制类型转换为数字来进行比较。(感觉这个有点问题)
var a = [ 42 ]; var b = [ "43" ]; a < b; // true b < a; // false复制代码
2、如果比较双方都是字符串,则按字母顺序来进行比较
var a = [ "42" ]; var b = [ "043" ]; a < b; // false复制代码
3、下面的例子有点奇怪
var a = { b: 42 }; var b = { b: 43 }; a < b; // false a == b; // false 对象比较 值不同直接是false a > b; // false a <= b; // true a >= b; // true复制代码
规范规定,<= 应该是“小于或者等于”。实际上 JavaScript 中 <= 是 “不大于”的意思(即 !(a > b),处理为 !(b < a))。同理 a >= b 处理为 b <= a