类型和语法
函数不仅是对象,还可以拥有属性。函数对象的length属性是其声明的参数的个数(希望接收参数的个数)。
JavaScript中的变量是没有类型的,只有值才有。变量可以随时持有任何类型的值。
在对变量执行typeof操作时,得到的结果并不是该变量的类型,而是该变量持有的值的类型,因为JavaScript中的变量没有类型。
变量在未持有值的时候为undefined,还没有在作用域中声明过的变量是undeclared。(is undefined 和 is not defined是两码事)
使用delete运算符可以将单元从数组中删除。单元删除后数组的length属性并不会发生变化。
数组通过数字进行索引,但也可以包含字符串键值和属性(这些并不计算在数组长度内)。
如果字符串键值能够被强制类型转换为十进制数字的话,它就会被当做数字索引来处理。
字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。而数组的成员函数都是在其原始值上进行操作。(这也并不意味着所有的数组api都会改变原数组,不要误解。)
toFixed方法不仅适用于数字变量,也适用于数字字面量。不过对于.运算符需要给予特别注意,因为它是一个有效的数字字符,会被优先识别为数字字面量的一部分,然后才是对象属性访问运算符。
整数检测&安全的整数
if(Number.isInteger){ Number.isInteger = function(num){ return typeof num === 'number' && num % 1 == 0 } } if(Number.isSafeInteger){ Number.isSafeInteger = function(num){ return Number.isInteger(num) && Math.abs(num) <= Number.MAX_SAFE_INTEGER } }
isNaN(...)有一个严重的缺陷,它的检查方式过于死板,就是“检查参数是否不是NaN,也不是数字”。
NaN是JavaScript中唯一一个不等于自身的值。
if(!Number.isNaN){ Number.isNaN = function(n){ return n !== n } }
计算结果一旦溢出为无穷数(infinity)就无法再得到有穷数。换句话说,就是你可以从有穷数走向无穷,但无法从无穷回到有穷。
验证-0(区分0与-0)
function isNegZero(n){ n = Number(n) return n === 0 && 1 / n === -Infinity }
Object.is(...)来判断两个值是否绝对相等
if(!Object.is){ Object.is = function(v1,v2){ if(v1 === 0 && v2 === 0){ return 1 / v1 === 1 / v2 } if(v1 !== v1){ return v2 !== v2 } return v1 === v2 } }
参数值总是通过值复制的方式传递,即便对复杂的对象值也是如此。
简单值总是通过值复制的方式来赋值或传递;复合值总是通过引用复制的方法来赋值或传递。
典型面试题
let array = [1,2,3] function foo(arr){ arr.push(4) arr = [5,6,7] console.log(arr)//[5,6,7] } foo(array) console.log(array)//[1,2,3,4]
我们无法自行决定使用值复制还是引用复制,一切由值的类型来决定。
一般情况下,我们不需要直接使用封装对象,最好是让JavaScript引擎自己决定什么时候应该使用封装对象。因为浏览器已经为这样的常见情况作了性能优化,直接使用封装对象来“提前优化”代码反而会降低执行效率。
如果想要自行封装基本类型值,可以使用Object(...)函数(不带new关键字)。
我们将包含至少一个“空单元”的数组称为“稀疏数组”。
Function.prototype是一个函数;RegExp.prototype是一个正则表达式;Array.prototype是一个数组。
将值从一种类型转换为另一种类型通常称为类型转换。
JavaScript中的强制类型转换总是返回标量基本类型值。
类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时(runtime)。
var a = 1; var b = a + '';//隐式强制类型转换 var c = String(a);//显式强制类型转换
抽象操作ToString,负责处理非字符串到字符串的强制类型转换。
对普通对象来说,除非自定义,否则toString()(Object.prototype.toString())返回内部属性[[class]]的值。
如果对象有自己的toString()方法,字符串化时就会调用该方法并使用其返回值。
数组的默认toString()方法经过了重新定义,将所有单元字符串化后再用“,”连接起来。
所有安全的JSON值(JSON-safe)都可以使用JSON.stringify(...)字符串化。安全的JSON值是指能够呈现为有效JSON格式的值。
不安全的JSON值:undefined、function、symbol和包含循环引用的对象都不符合JSON结构标准,支持JSON的语言无法处理它们。
JSON.stringify(...)在对象中遇到undefined、function、symbol时会自动将其忽略,在数组中则会返回null(以保证单元位置不变)。
如果对象中定义了toJSON()方法,JSON字符串化时就会首先调用该方法,然后用它的返回值来进行序列化。
如果要对含有非法JSON值的对象进行字符串化,或者对象中某些值无法被序列化时,就需要定义toJSON()方法来返回一个安全的JSON值。
toJSON()返回的应该是一个适当的值,可以是任何类型,然后再由JSON.stringify()对其进行字符串化。也就是说,toJSON()应该返回一个能够被字符串化的安全的JSON值,而不是返回一个JSON字符串。
我们可以向JSON.stringify(...)传递一个可选参数replacer,它可以是数组或是函数,用来指定对象序列化过程中哪些属性应该被处理,哪些应该被排除。如果replacer是一个数组,那么它会对对象本身调用一次,然后对对象中的每个属性各调用一次,每次传递两个参数,键和值。如果要忽略某个键就返回undefined,否则返回指定的值。
JSON.stringify(...)还有一个可选参数space,用来指定缩进格式。
- 字符串、数字、布尔值和null的JSON.stringify(...)规则与toString基本相同。
- 如果传递给JSON.stringify(...)的对象中定义了toJSON()方法,那么该方法会在字符串化前调用,以便将对象转换为安全的JSON值。
JavaScript中的值可以分为两类:
- 可以被强制类型转换为false的值
- 其他(被强制类型转换为true的值)
假值
- undefined
- null
- false
- +0、-0、NaN
- ""
假值的布尔强制类型转换结果为false。
解析允许字符串中含有非数字字符,解析按从左到右的顺序,如果遇到非数字字符就停止。而转换不允许出现非数字字符,否则会失败并返回NaN。
一元运算符 ! 显示地将值强制类型转换为布尔值,但是它同时还将真值反转为假值(或者将假值转为真值)。所以显示强制类型转换为布尔值最常用的方法是 !!,因为第二个 ! 会将结果反转回原值。
ES6允许符号到字符串的显示强制类型转换 ,然而隐式强制类型转换会产生错误,符号不能够被强制类型转换为数字(显式和隐式都会产生错误),但可以被强制类型转换为布尔值(显式和隐式结果都是true)。
常见误区:==检查值是否相等;===检查值和类型是否相等;
正确解释:==允许在相等比较中进行强制类型转换,而===不允许。
- 如果Type(x)是数字,Type(y)是字符串,则返回x == ToNumber(y)的结果。
- 如果Type(x)是字符串,Type(y)是数字,则返回ToNumber(x) == y的结果。
- 如果Type(x)是布尔类型,则返回ToNumber(x) == y的结果。
- 如果Type(y)是布尔类型,则返回x == toNumber(y)的结果。
了解超过一个运算符是表达式的执行顺序,这些规则被称为“运算符优先级”。
&&运算符优先于||执行,而且执行顺序并非我们所设想的从左到右。 而||的优先级又高于? : 。
如果运算符优先级/关联规则能够令代码更简洁就是用运算符优先级/关联规则;如果( )有助于提高代码可读性,就是用( )。
finally中的代码总会在try之后执行,如果有catch的话则在catch之后执行。也可以将finally中的代码看做一个回调函数,即无论出现什么情况最后一定会被调用。
在函数中省略return的结果和return;即return undefined;是一样的,但是在finally中省略return则会返回前面的return设定的返回值。
case表达式的匹配算法与===相同。