JavaScript易错知识点整理

智码流光使
• 阅读 1926

前言

本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一些ES6的知识点。

JavaScript知识点

1.变量作用域

var a = 1;
function test() {
    var a = 2;

    console.log(a); // 2
}

test();

上方的函数作用域中声明并赋值了a,且在console之上,所以遵循就近原则输出a等于2。

var a = 1;
function test2() {
    console.log(a); // undefined

    var a = 2;
}

test2();

上方的函数作用域中虽然声明并赋值了a,但位于console之下,a变量被提升,输出时已声明但尚未被赋值,所以输出undefined。

var a = 1;
function test3() {
    console.log(a); // 1

    a = 2;
}

test3();

上方的函数作用域中a被重新赋值,未被重新声明,且位于console之下,所以输出全局作用域中的a。

let b = 1;
function test4() {
    console.log(b); // b is not defined

    let b = 2;
}

test4();

上方函数作用域中使用了ES6的let重新声明了变量b,而let不同于var其不存在变量提升的功能,所以输出报错b is not defined。

function test5() {
    let a = 1;

    {
        let a = 2;
    }

    console.log(a); // 1
}

test5();

上方的函数作用域中用let声明了a为1,并在块级作用域中声明了a为2,因为console并不在函数内的块级作用域中,所以输出1。

2.类型比较

var arr = [],
    arr2 = [1];

console.log(arr === arr2); // false

上方两个不同的数组比较,console为false。

var arr = [],
    arr2 = [];

console.log(arr === arr2); // false

上方两个相同的数组比较,因为两个单独的数组永不相等,所以console为false。

var arr = [],
    arr2 = {};

console.log(typeof(arr) === typeof(arr2)); // true

上方利用typeof比较数组和对象,因为typeof获取NULL、数组、对象的类型都为object,所以console为true。

var arr = [];

console.log(arr instanceof Object); // true
console.log(arr instanceof Array); // true

上方利用instanceof判断一个变量是否属于某个对象的实例,因为在JavaScript中数组也是对象的一种,所以两个console都为true。

3.this指向

var obj = {
    name: 'xiaoming',
    getName: function () {
        return this.name
    }
};

console.log(obj.getName());  // 'xiaoming'

上方对象方法中的this指向对象本身,所以输出xiaoming。

var obj = { 
    myName: 'xiaoming', 
    getName: function () { 
        return this.myName 
    } 
}; 
var nameFn = obj.getName;
console.log(nameFn()); // undefined

上方将对象中的方法赋值给了一个变量,此时方法中的this也将不再指向obj对象,从而指向window对象,所以console为undefined。

var obj = { 
    myName: 'xiaoming',
    getName: function () { 
        return this.myName
    } 
}; 
var obj2 = { 
    myName: 'xiaohua'
}; 
var nameFn = obj.getName; 
console.log(nameFn.apply(obj2)); // 'xiaohua'

上方同样将obj对象中的方法赋值给了变量nameFn,但是通过apply方法将this指向了obj2对象,所以最终console为xiaohua。

4.函数参数

function test6() {
    console.log(Array.prototype.slice.call(arguments)); // [1, 2]
}

test6(1, 2);

上方利用函数中的arguments类数组对象获取传入函数的参数数组,所以输出数组[1, 2]。

function test7 () {
    return function () {
        console.log(Array.prototype.slice.call(arguments)); // 未执行到此,无输出
    }
}

test7(1, 2);

上方同样利用arguments获取参数,但因test7(1, 2)未执行return中的函数,所以无输出。若执行test7(1, 2)(3, 4)则会输出[3, 4]。

var args = [1, 2]; 
function test9() { 
    console.log(Array.prototype.slice.call(arguments)); // [1, 2, 3, 4]
} 
Array.prototype.push.call(args, 3, 4);
test9(...args);

上方利用Array.prototype.push.call()方法向args数组中插入了3和4,并利用ES6延展操作符(...)将数组展开并传入test9,所以console为[1, 2, 3, 4]。

5.闭包问题

var elem = document.getElementsByTagName('div'); // 如果页面上有5个div 
for(var i = 0; i < elem.length; i++) {
    elem[i].onclick = function () { 
        alert(i); // 总是5 
    };
}

上方是一个很常见闭包问题,点击任何div弹出的值总是5,因为当你触发点击事件的时候i的值早已是5,可以用下面方式解决:

var elem = document.getElementsByTagName('div'); // 如果页面上有5个div 
for(var i = 0; i < elem.length; i++) { 
    (function (w) { 
        elem[w].onclick = function () {
            alert(w); // 依次为0,1,2,3,4 
        }; 
    })(i); 
}

在绑定点击事件外部封装一个立即执行函数,并将i传入该函数即可。

6.对象拷贝与赋值

var obj = { 
    name: 'xiaoming', 
    age: 23 
}; 
var newObj = obj;
newObj.name = 'xiaohua';
console.log(obj.name); // 'xiaohua' 
console.log(newObj.name); // 'xiaohua'

上方我们将obj对象赋值给了newObj对象,从而改变newObj的name属性,但是obj对象的name属性也被篡改,这是因为实际上newObj对象获得的只是一个内存地址,而不是真正的拷贝,所以obj对象被篡改。

var obj2 = { 
    name: 'xiaoming',
    age: 23 
}; 
var newObj2 = Object.assign({}, obj2, {color: 'blue'});
newObj2.name = 'xiaohua'; 
console.log(obj2.name); // 'xiaoming' 
console.log(newObj2.name); // 'xiaohua' 

上方利用Object.assign()方法进行对象的深拷贝可以避免源对象被篡改的可能。因为Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是Object.assign() 只是一级属性复制,比浅拷贝多深拷贝了一层,使用的时候,还要注意这个问题。

var obj3 = { 
    name: 'xiaoming',
    age: 23 
 }; 
 var newObj3 = Object.create(obj3); 
 newObj3.name = 'xiaohua'; 
 console.log(obj3.name); // 'xiaoming' 
 console.log(newObj3.name); // 'xiaohua'

我们也可以使用Object.create()方法进行对象的拷贝,Object.create()方法可以创建一个具有指定原型对象和属性的新对象。

结语

学习JavaScript是一个漫长的过程,不能一蹴而就。希望本文介绍的几点内容能够帮助学习JavaScript的同学更加深入的了解和掌握JavaScript的语法,少走弯路。

参考

转载地址:http://www.jianshu.com/p/1c77853d4f01

点赞
收藏
评论区
推荐文章
Dax Dax
4年前
JS核心原理理解闭包
前置概念在正式看闭包之前,我们先来学习一下前置知识,那就是JS中的作用域,我们知道,在ES5之中,作用域分为两种:全局作用域和函数作用域,随着ES6的到来,新增了块级作用域,想更好的理解闭包,那么搞清楚作用域是首要条件全局作用域我们知道,对于变量而言,我们一般会分成两类:全局变量和局部变量,一般定义在最外围环境的为全局变量,定义在函数当中的为局部变量,在we
Karen110 Karen110
4年前
一篇文章带你了解JavaScript作用域
在JavaScript中,对象和函数也是变量。在JavaScript中,作用域是你可以访问的变量、对象和函数的集合。JavaScript有函数作用域:这个作用域在函数内变化。一、本地JavaScript变量一个变量声明在JavaScript函数内部,成为函数的局部变量。局部变量有局部作用域:它们只能在函数中访问。JS://codeherecann
徐小夕 徐小夕
5年前
如何用不到200行代码写一款属于自己的js类库
前言JavaScript的核心是支持面向对象的,同时它也提供了强大灵活的OOP语言能力。本文将使用面向对象的方式,来教大家用原生js写出一个类似jQuery这样的类库。我们将会学到如下知识点:闭包:减少变量污染,缩短变量查找范围自执行函数在对象中的运用extend的实现原理如何实现跨浏览器的事件监听原型链与继承接下来我会对类库
晴空闲云 晴空闲云
4年前
也谈JavaScript浅拷贝和深拷贝
网上关于这个话题,讨论有很多了,根据各路情况我自己整理了一下,最后还是能接近完美的实现深拷贝,欢迎大家讨论。javascript中的对象是引用类型,在复制对象的时候就要考虑是用浅拷贝还是用深拷贝。直接赋值对象是引用类型,如果直接赋值给另外一个对象,那么只是赋值一个引用,实际上两个变量指向的同一个数据对象,如果其中一个对象的属性变更,那么另外一个也会变更。示
菜园前端 菜园前端
2年前
为你解惑JS作用域和作用域链知识
原文链接:变量作用域一个变量的作用域(scope)是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,在JavaScript代码中的任何地方都是可以访问的。然而在函数内声明的变量只能在函数体内访问,它们是局部变量,作用域是局部性的。函数参数也是局部变
Jacquelyn38 Jacquelyn38
4年前
面试官:JavaScript的数据类型你了解多少?
前言作为JavaScript的入门知识点,Js数据类型在整个JavaScript的学习过程中其实尤为重要。最常见的是边界数据类型条件判断问题。我们将通过这几个方面来了解数据类型:概念检测方法转换方法概念undefined、Null、Boolean、String、Number、Symbol、BigInt为基础类型;Ob
御弟哥哥 御弟哥哥
4年前
彻底理解js的作用域链
在之前的文章(https://www.helloworld.net/p/G4dFV7tALU4J)中我已经介绍了执行上下文的变量对象。在这一篇文章我要介绍执行上下文的作用域链了。执行上下文.作用域链(scopechain)作用域链与变量对象有着密不可分的关系,因为作用域链就是变量对象的数组!其中第
Jacquelyn38 Jacquelyn38
4年前
你所知道的JS变量作用域
变量的作用域,指的是变量在脚本代码中的可读、可写的有效范围,也就是脚本代码中可以使用这个变量的区域。在ES6之前,变量的作用域主要分为全局作用域、局部作用域(也称函数作用域)两种;在ES6及其之后,变量的作用域主要分为全局作用域、局部作用域、块级作用域这3种。相应作用域变量分别称为全局变量、局部变量、块级变量。全局变量声明在所有函数之外;局部变量是在函数体内
Jacquelyn38 Jacquelyn38
4年前
重学JavaScript第1集|变量提升
变量提升就好比JavaScript引擎用一个很小的代码起重机将所有var声明和function函数声明都举起到所属作用域(所谓作用域,指的是可访问变量和函数的区域)的最高处。这句话的意思是:如果在函数体外定义函数或使用var声明变量。则变量和函数的作用域会提升到整个代码的最高处,此时任何地方访问这个变量和调用这个函数都不会报错;而在函数体内定义函数或使用va
Stella981 Stella981
4年前
JavaScript易错知识点整理
前言本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一些ES6的知识点。JavaScript知识点1.变量作用域vara1;functio
待兔 待兔
1年前
JS的数据类型你了解多少?
JS的数据类型你了解多少?作为JavaScript的⼊⻔级知识点,JS数据类型在整个JavaScript的学习过程中其实尤为重要。因为在JavaScript编程中,经常会遇到边界数据类型条件判断问题,很多代码只有在某种特定的数据类型下,才能可靠地执⾏。希望