【JavaScript学习笔记】JavaScript踩坑(1)

本文整理了在JavaScript学习过程中遇到的一些典型问题及其原因,包括parseInt的参数理解、NaN的判断、Object.defineProperty()的陷阱、浮点数运算误差、变量作用域与提升、函数参数传递与作用域、对象赋值的深拷贝浅拷贝概念,以及同名参数的处理等,旨在帮助开发者更好地理解和避免这些常见的‘坑’。
摘要由CSDN通过智能技术生成

使用js的过程中出现过一些意向不到的输出,在这里做个整理,已备查阅。

一:

["0", "1", "2"].map(parseInt)

原因:输出结果为[0, NaN, NaN]。原因是map传递给parseInt的参数为3个,分别是element,index,array,指元素的值,索引,整个数组。parseInt接收2个参数,第一个为待转换字符,第二个为进制(2-36,0代表10进制)。当element为"1"时,index为1,不满足2-36之间;当element为"2"时,index为2,二进制时不能有数字大于等于2,因此仍是NaN。

二:

NaN === NaN
NaN == NaN

原因:返回值均为false,false。NaN不等于自身,要判断是否是NaN,要用isNaN()方法。

isNaN(NaN)

三:

在尝试自己写双向绑定时,发现通过Object.defineProperty()方法设置属性时,无法通过set外部修改属性的值,如下:

var obj = {};
Object.defineProperty(obj, 'txt', {  
    get: function () {  
        return obj;  
    },  
    set: function (newValue) {
        document.getElementById('input').value = newValue;  
        document.getElementById('show').innerHTML = newValue;  
    }  
});  
document.getElementById('input').addEventListener('keyup', function (e) {  
    obj.txt = e.target.value;  
}); 

在console中打印obj.txt,输出为undefined,说明obj.txt = e.target.value仅将将值传进set函数,并未真正改变obj.txt的值。尝试在set中赋值,如下:

var obj = {};
Object.defineProperty(obj, 'txt', {  
    get: function () {  
        return obj;  
    },  
    set: function (newValue) {
        obj.txt = newValue;
        document.getElementById('input').value = newValue;  
        document.getElementById('show').innerHTML = newValue;  
    }  
});  
document.getElementById('input').addEventListener('keyup', function (e) {  
    obj.txt = e.target.value;  
}); 

会发生死循环,因为在set中又进行了赋值,会再次调用set。

解决办法,构造函数,保存变量。

function defineProperty(obj, key){
    var val;
    Object.defineProperty(obj, 'txt', {  
        get: function () {  
            return val;  
        },  
        set: function (newValue) {
            if(newValue === val){
                return;
            }
            val = newValue;
            document.getElementById('input').value = newValue;  
            document.getElementById('show').innerHTML = newValue;  
        }  
    });
}
var obj = {};
defineProperty(obj, 'txt');
document.getElementById('input').addEventListener('keyup', function (e) {  
    obj.txt = e.target.value;  
}); 

四:

0.2-0.1 == 0.3-0.2

结果为false,因为0.2-0.1=0.1,0.3-0.2=0.09999999。

五:

var a = [];
for (var i = 0; i<3; i++){
    a[i] = function () {
        console.log(i);
    };
}
a[0]();

输出为3。a[i]绑定的i为全局变量i,当调用a[i]时,会输出全局变量i。

var a = [];
for (let i = 0; i<3; i++){
    a[i] = function () {
        console.log(i);
    };
}
a[0]();

输出为0。a[i]绑定局部变量i,因此每次声明都会重新分配一个i给a[i],因此当调用a[i]时,输出的是单独分配的i。

六:

参考阮一峰的ESMAScript6入门,var变量提升会导致如下输出。

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined

var会“穿过”if作用域,因此相当于外层会先声明var tmp,此时tmp未赋值,因此打印undefined。

七:

function test(x) {
    console.log(x);
    var x = 2;
    console.log(x);
}

test(1);

输出为1,2。

for (var x = 0; x < 3; x++) {
    console.log(x);
    var x = 2;
    console.log(x);
}

输出为0,2。上面两个例子说明function、for括号内的作用域是花括号{}的父作用域。

for (var x = 0; x < 3; x++) {
    let x = 2;
    console.log(x);
}

输出为3个2。此时内部使用let重新声明x,但不会改变圆括号内父作用域的同名变量。

八:

var test1 = [1, 2],
    test2 = 3;

function func1(test) {
    test[0] += 1;
}

function func2(test) {
    test += 1;
}

func1(test1);
func2(test2);
console.log(test1);//[ 2, 2 ]
console.log(test2);//3

函数参数传递是按值传参,但当传入的是对象时,传入的是指向对象的指针。因此在函数内修改对象会改变对象本身。

九:

function test (x, x, x) {
    console.log(x);
}
test(1, 2, 3);//3

非严格模式下,函数可以有同名参数,后面的会覆盖前面的。

十:

var a = {n:1};  
var b = a;  
a.x = a = {n:2};  
console.log(a.x);  
console.log(b.x);

结果为undefined,{n:2}。由此可知多个等号的赋值顺序:先获取最左侧的地址,再由右向左依次赋值。

function test () {
    let a = b = 1;
}
test();
console.log(b);//1

如果多个等号赋值的中间某一变量之前未声明,则在非严格模式下提升为全局变量,严格模式下报错。

十一:

(function f(f){
    return typeof f();//"number"
})(function(){ return 1; });

传入的参数是一个函数,函数执行后返回1,因此typeof 1是number。虽然函数名和参数名同名,但互不影响。

function test (test) {
    console.log(typeof test);
}
test(1);//number

上面的例子说明函数参数中有与函数名同名的变量时,会覆盖函数名,说明参数域是一个独立的子作用域。

var test = function test2() {
    console.log(typeof test2);
};
test();//function
console.log(typeof test);//function
console.log(typeof test2);//undefined

函数可以通过三种方式声明:1、函数声明;2、函数表达式;3、new Function("x","return x;")构造函数。当函数以函数表达式的方式声明时,等号后可以是匿名函数,也可以带函数名,但函数名只能在函数体内引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值