yoowinsu / blog

issues blog
17 stars 3 forks source link

总结JavaScript踩过的坑 #23

Open yoowinsu opened 7 years ago

yoowinsu commented 7 years ago

本文首发在个人博客yoowin.me

数组排序问题

[0,1,10,11,8,-2].sort();  //[[-2, 0, 1, 10, 11, 8]

sort()默认情况下会按照Unicode码点排序,而不是数值顺序排序,所以正确的解决方案为:

[0,1,10,11,8,-2].sort((a,b)=>a-b);  //[[-2, 0, 1, 8, 10, 11]

0.1+0.2 !== 0.3的问题

先看如下代码

0.1 + 0.2 === 0.30000000000000004     //true
1000000000000000128 === 1000000000000000129     //true

本质上这是二进制浮点数造成的精度丢失问题,这种问题也存在于除JavaScript之外的其他语言中,可以参考0.30000000000000004.com 因为计算机只能读懂二进制数值,我们看下0.1和0.2转换成二进制:

0.1 => 0.0001 1001 1001 1001…(无限循环) 0.2 => 0.0011 0011 0011 0011…(无限循环)

双精度浮点数的小数部分最多支持 52 位,所以计算后会进行小数位的限制截断,也就造成了舍入误差,再次转换成十进制后就是我们所看到的0.30000000000000004

解决方法

(0.1+0.2).toFixed(1)     //"0.3"
(0.1+0.2).toFixed(2)     //"0.30"

toFixed()方法是保留小数后面的位数是几位,注意:得到的结果是字符串

全局变量问题

var b = 0;
function fn() {
    var a = b = 3; 
}
fn()
console.log(b);  //3

一般会有不少人觉得应该打印0,因为b=3是函数内声明,属于局部变量。

这里var a=b=3是个坑,赋值运算是从右往左的,所以这里的b=3是全局变量。

所以声明变量建议单个单个的来。

变量提升问题

if(d){
  var d = 5;
}
console.log(d)     //undefined

因为预解析的原因,所以var d的代码会提升到代码顶部,且值为undefined,然后执行流程语句,因为d为undefined,转为布尔值为false,所以不会进入if判断语句,所以打印结果为undefined

下面的代码也是同理

if(!('d' in window)){
  var d = 5;
}
console.log(d)     //undefined

匿名函数预解析1

function foo() {
    console.log('1')
}
foo()     //2
function foo() {
    console.log('2')
}
foo()    //2

匿名函数的预解析会把函数提升到函数所在作用域的顶部,声明先于调用,而第二个foo函数会覆盖第一个foo函数的声明,所以两次foo()的调用都是在执行第二个声明的函数,所以打印两次2

匿名函数预解析2

var getName = function(){
    console.log(2);
}
function getName (){
    console.log(1);
}
getName();  //2

匿名函数会预解析,但是函数表达式不会预解析。 所以匿名函数getName会提升到所在作用域顶部,然后依次执行代码,执行到函数表达式时,getName函数表达式会覆盖匿名函数getName,所以打印结果是2

匿名函数预解析3

function fn2(){
    var a = 666;
    fn1()  
}
fn2()  //undefined

function fn1(){
    console.log(a);
}
var a = 333;

匿名函数fn1会提前预解析,提升到作用域顶部。 a变量也会提升到代码顶部并初始化值为undefined。 执行fn2。调用fn1是执行fn1函数,而fn1执行时a只和fn1所在作用域有关,调用时a只是初始化,还未赋值为333,所以打印结果为undefined

null和undefined

null == undefined     //true

请记住这个特殊情况吧! 所以我建议写代码判断相等用=== 还有null和undefined的区别

null表示"没有对象",即该处不应该有值。 undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。

Math.min>Math.max()

console.log(Math.max())  //-Infinity
console.log(Math.min())  //+Infinity
console.log(Math.min()>Math.max())  //true

typeof(null)==='object'

typeof(null)==='object'  //true

这是错的,虽然他返回的结果是true,但这是JavaScript设计的错误,考虑到后兼容,就保持了这一错误

JavaScript注释

let x = 5;
let y = 1<!--x;

上面的代码之后x和y的值是多少? 答案是5和1. 因为<!--会把后面的代码注释掉(虽然是HTML注释,但这里浏览器依然会当做注释),所以后面的代码都是不影响y的值的,就像下面代码

let z = 8<!--你好#¥#%¥……456-->
console.log(z)  //8

哪怕是浏览器不能识别,会报错的字符依然不受影响,因为浏览器会跳过注释执行代码

JavaScript函数中参数的传递

var young = {name: 'su'}
var old = {age: 66}
function fn(a,b){
  a.name = 'yoowin'
  b = {age: 20}
}
fn(young,old)
console.log(young)
console.log(old)

这道代码题最开始我疑惑了挺久,在网上也有搜过,貌似对于值传递一直有争议,我自己刚开始也不是很明白。 先看下答案:

var young = {name: 'su'}
var old = {age: 66}
function fn(a,b){
  a.name = 'yoowin'
  b = {age: 20}
}
fn(young,old)
console.log(young)  //{name: "yoowin"}
console.log(old)  //{age: 66}

现在我认为的是实参是原始类型的话,其对于形参是值传递,是值的拷贝;实参是引用类型的话,其对于形参是引用传递,形参的指针指向实参指针对应的对象。

在看题目中,重点就在于fn函数中, 执行的第一行是通过指针把对象的属性改变了,则原对象是会改变的,所以打印的young值也确实改变了; 执行的第二行中,本来形参b的指针指向{age: 66},然后 b = {age: 20}的赋值语句把b的指针改变了,指向了{age: 20},所以实参old对应的对象并没有改变,依然是{age: 66},所以会打印{age: 66}