常见错误
一、js中的错误分类
错误分类通常分为:语法错误 、引用错误、类型错误、范围错误和逻辑错误
1.语法错误
JS代码在预编译时 就发现了错误
特点:代码没有机会执行,错误最好找
<script>
function () //语法错误(没有写函数名)
{
}
</script>
Uncaught SyntaxError: Function statements require a function name
2.引用错误
通常访问一个没有定义的变量就会发生引用错误
解决办法:先定义好这个变量,再使用之
特点:JS代码在执行时的时候,才会发现引用错误,错误之后的代码就不执行
<script>
console.log("start")
// index.html:38 Uncaught ReferenceError: a is not defined
console.log(a); // 引用错误
console.log("end")
</script>
Uncaught ReferenceError: a is not defined
3.类型错误
使用类型不当,你提供的数据,不符合js的数据类型规范
特点:在JS代码在执行时才会发现,类型错误之前的代码也会执行,之后的代码不会执行。
解决办法:一般是你数据类型使用不当,修改之就OK了
<script>
console.log("start")
var f = 110;
f(); // 类型错误
console.log("end")
</script>
TypeError: f is not a function
4.范围错误
使用容器,范围指定不当
var arr = new Array(-5);
Uncaught RangeError: Invalid array length
5.逻辑错误
上面的4类错误,在控制台都会报错,修改就OK了。还有一类错误,控制台不会报错,叫逻辑错误。
逻辑错误解决办法:通过debugger调试错误。
二、异常处理
异常不是错误,异常是指可能出错的代码。
语法:
try{
// 放可能出错的代码
}catch(e){
// 如果上面的代码错误了,就到这里进行错误的处理,其中e表示错误对象
// 它里面包含了错误信息。通过查看e,可以知道它到底出了什么错误
}finally{
// 无论对错,代码都会执行到此处,在JS中用的不多
}
<script>
try{
console.log(a); // 放可能出错的代码
}catch (e) {
console.log("有错误:",e);
}finally {
console.log("最终finally一定会执行");
}
</script>
三、逻辑与 和 逻辑或
逻辑与 && :双元运算符,有两个操作数。只有两个操作数都为真,整体的结果才为真
true && true 真
true && false 假
false && true 假
false && false 假
逻辑或 || :双元运算符,有两个操作数。 只要有一个操作数为真,整体的结果就为真
true || true 真
true || false 真
false || true 真
false || false 假
逻辑非 ! :单目运算符,1个操作数
!true 假
!false 真
如果说第一个操作数能决定整体的值,JS会停止对第二个操作数的运算
<script>
var a = 10;
var b = 0;
var c = b && a++; // b的值是false 按理说是把false赋值给c c的值是false 但是&&得到的值不是隐式类型转化后的值,是隐式类型转化之前的值
var d = a++ && b; // a++后 a的值是11 整体的值是10 10转成true 不能确定 看第2个操作数 b是0 0转成false 整体就是false 第1个操作数不能确定整体的值,那么整体的值就是第2个操作数的值
console.log(a); // 11
console.log(b); // 0
console.log(c); // 0
console.log(d); // 0
</script>
第1个操作数不能确定整体的值,那么整体的值就是第2个操作数的值
<script>
var a = 0;
var b = 2;
var c = a || b++;
console.log(a); // 0
console.log(b); // 3
console.log(c); // 2
var m = 1;
var n = "";
var q = m || n;
console.log(m); // 1
console.log(n); // ""
console.log(q); // 1
</script>
四、同步和异步代码(闭包的使用)
同步:代码的书写顺序和代码的执行顺序一样。
异步:代码的书写顺序和代码的执行顺序不一样。
一般同步代码执行之后,异步代码才进行执行
98%代码都是同步代码。
在JS中,异步代码仅仅是个别:
1)事件绑定
2)定时器
3)ajax
…
<ul>
<li>click me</li>
<li>click me</li>
<li>click me</li>
<li>click me</li>
</ul>
<script>
var elements=document.getElementsByTagName('li');
var length=elements.length;
for(var i=0;i<length;i++){
elements[i].onclick=function(){
alert(i);
}
}
</script>
执行结果为: 5 5 5 5 5。同步代码与异步代码出现时,先执行同步代码,同步代码执行过之后,i的值为5,异步代码执行时,将i的最后数值输出5次,所以最后输出结果为5 5 5 5 5。
显示异步代码与同步代码一同执行
实现效果:依次点击按钮1,2,3时,控制台分别输出0,1,2数值
1)使用函数闭包,返回信息
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
var btns = document.getElementsByTagName("button"); // 数组容器
// bnts.length; 说明数组也是对象 arr[0] obj.name 矩形和正方形
// 同步代码
for (var i=0; i<btns.length; i++){
// 事件绑定 异步代码 浏览器在执行时直接忽略掉
btns[i].onclick = (function (i) {
return function g() {
console.log(i)
}
})(i);
}
</script>
2)利用数组绑定事件源
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
var btns = document.getElementsByTagName("button");
for (var i=0; i<btns.length; i++){
// console.log(btns[i].abc)// 访问一个对象上不存在的属性 得到的是und
btns[i].index = i; // 把i值赋值给对象上的index属性
btns[i].onclick = function () {
console.log(this.index); //绑定事件源
}
}
</script>
浏览器会帮我们自动调用函数,
3)let形成块级作用域
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
var btns = document.getElementsByTagName("button");
for (let i=0; i<btns.length; i++){
btns[i].onclick = function () {
console.log(i)
}
}
</script>
五、this大总结
结论:
1)如果this出现在普通的函数中,this表示window,如果你通过window打点调用一个函数,这个函数中的this也是window。
2)事件绑定,事件处理程序,事件发生时,浏览器帮我们调用这个函数,此函数中的this表示事件源
3)在一个对象中,如果有方法(函数),如果通过这个对象调用方法,方法中的this表示这个对象
4)在IIFE中,this表示window
5)前四点都是在非严格模式下,在严格模式下,调用一个普通函数,this表示undefined。IIFE中的this也表示undefined
案例01
1.代码
<button id="box">box</button>
<script>
let box = document.getElementById("box");
box.onclick = function () {
console.log(this); // this表示事件源
}
</script>
2.解析
解析:onclick是事件调用时间,代表鼠标点击之后,事件发生相应的活动。并且,浏览器帮我们调用这个函数,此时的this满足条件2),即this表示事件源。
案例02
1.代码
<button id="box">box</button>
<script>
let box = document.getElementById("box");
function f() {
console.log(this); // window
}
f(); 程序员自己调用一个函数 一个普通函数 函数中的this表示window
box.onclick = f();
</script>
2.解析
解析:其中f();代表普通函数调用,符合条件1),所以函数里面的this表示的是window。执行box.onclick = f();这一行, 主要看 = 后面f(); 表示函数普通调用,符合条件1)所以这里的this也代表window。
案例03
1.代码
<button id="box">box</button>
<script>
let box = document.getElementById("box");
function f(){
console.log(this); // window
return function g(){
console.log(this); // 事件源
}
}
box.onclick = f();
</script>
2.解析
解析:box.onclick表示响应事件,调用函数f();符合条件1),所以第一个this表示window。因为函数返回的是一个堆地址,这个堆地址浏览器自动调用,并且进行了事件绑定,所以符合条件2),因此第二个this表示事件源。
案例03
1.代码
<script>
function k() {
console.log(this); // window
}
window.k(); // window打点调用
k(); // 程序员自己调用
</script>
2.解析
解析:因为由window加点进行调用,符合条件1),所以这个this也表示window。函数k由自己调用k();符合条件1),所以第一个this表示window。
案例04
1.代码
<script>
// JS中的对象是属性的无序集合
// 属性:变量/方法
// 无序:容器中的属性没有顺序
// 集合:存放了N个数据
var wc = {
name:"wangcai", // 属性 var name = "wangcai"
age:100, // 属性
eat:function () { // 叫方法(属性)
console.log("eat...")
console.log(this);
}
}
// 一个方法中的this是谁,就看点前面是谁。
wc.eat(); // 调用一个对象上的方法
</script>
2.解析
解析:this是通过wc对象调用的,这里的this指的是对象wc。
案例05
1.代码
<script>
var name = "window";
var Wangcai = {
name:"Wangcai",
show:function () {
console.log(this.name)
},
f:function () {
var fun = this.show;
fun(); // 普通函数调用
}
}
Wangcai.f();
</script>
2.解析
解析:执行Wangcai.f();语句,函数f是通过Wangcai这个对象调用的,所以this.show中的this指的是对象Wangcai。调用函数fun是函数的普通调用,所以this.name中的this指的是window。
案例06
1.代码
<script>
var fullname = "language";
var obj = {
fullname:"javascript",
prop:{
getFullName:function () {
return this.fullname;
}
}
}
// 谁用了getFullName 看.前面是谁
// .前面是obj.prop 说明getFullName中的this是obj.prop
// 问:obj.prop中有没有fullname;
// 答:访问一个对象上不存在的属性 得到und
console.log(obj.prop.getFullName()); // undefined
</script>
2.解析
解析:函数getFullName()点的前面是obj.prop,其中对象obj中的prop属性中没有fullname属性,所以此时的this属性指的是undefined。
案例07
1.代码
<script>
var fullname = "language";
var obj = {
fullname:"javascript",
prop:{
fullname:"vue",
getFullName:function () {
return this.fullname;
}
}
}
var t = obj.prop.getFullName;
console.log(t());
</script>
2.解析
解析:其中obj.prop.getFullName表示的是function () { return this.fullname;}返回的堆地址,调用t使用的是t(),即普通函数调用,符合条件1),所以此时的this表示的是window,最后输出结果为全局变量中的fullname:language。
案例08
1.代码
<script>
let obj = {
fn:(function () {
return function () {
console.log(this)
}
})()
}
obj.fn();
</script>
2.解析
解析:函数fn是立即调用函数(IIFE),此时内部使用对象obj调用,符合条件3),此时的this指的是obj这个对象。
案例09
1.代码
<script>
let obj = {
fn:(function () {
return function () {
console.log(this)
}
})()
}
var t = obj.fn;
t();
</script>
2.解析
解析:对象obj中的fn函数将内部返回地址赋值给t,调用普通函数t(); ,符合条件1)此时的this表示的是window。
案例10
1.代码
<script>
(function fn() {
console.log(this)
})();
</script>
2.解析
解析:函数属于立即调用函数(IIFE),符合条件4),此时的this表示window。
案例11
1.代码
<script>
"use strict"; // 启用JS的严格模式
function f() {
console.log(this); // und
}
f();
(function () {
console.log(this); // und
})();
</script>
2.解析
解析:严格模式下,this的值指的是undefined。