JS进阶Ⅱ
今日份重难点:① this ; ② 类与对象 ;③ 原型与原型链
同步代码与异步代码
简单来说:
同步代码:代码的执行顺序与代码的书写顺序一样,由上至下。
异步代码:代码的执行顺序与代码的书写顺序不一样,遇到异步代码浏览器会忽略,直到满足某些条件才会执行这些代码。
不同于JAVA,JS是一门单线程语言。在JS代码中,95%以上的代码都是同步代码,而异步代码仅仅是个别:事件、定时器、ajax等。
<button>一</button>
<button>二</button>
<button>三</button>
let btns = document.getElementsByTagName("button"); // 同步代码 立即执行
// 异步代码 浏览器在执行时会直接忽略掉 只有触发了满足条件才会执行
btns[0].onclick = function () {
console.log("我是"+0+"号") // 点击了第一个按钮后输出:我是0号
}
btns[1].onclick = function () {
console.log("我是"+1+"号") // 点击了第二个按钮后输出: 我是1号
}
btns[2].onclick = function () {
console.log("我是"+2+"号") // 点击了第三个按钮后输出: 我是2号
}
报错
1. 语法错误(SyntaxError
): JS代码在预编译阶段就发现了错误
特点:
1)直接报错,一行代码都不会执行;
2)错误好找。
// Uncaught SyntaxError: Unexpected token (
function (){
console.log("哈哈哈");
};
2. 引用错误(ReferenceError
): 通常访问一个没有定义的变量就会发生引用错误
特点:JS代码在执行到有引用错误的地方才会报错,引用错误之后的代码就不再执行,已执行的代码不受影响。
解决办法:先定义好变量,再去使用。
var a = 1;
var c = 2;
console.log(a); // 1
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // 因为上一行代码有引用错误,此行代码不再执行。
3. 类型错误(TypeError)
: 使用类型不当
特点:也是JS代码在执行阶段时才会发现,类型错误之前的代码会执行,之后的代码不会执行。
解决办法:一般是你数据类型使用不当,修改后使用即可。
var a = 1;
console.log(a); // 1
a(); // Uncaught TypeError: a is not a function
console.log(a); // 因为上一行代码有类型错误,此行代码不再执行。
- 范围错误(
RangeError
): 使用容器时,范围指定不当
特点:在执行阶段时发现,类型错误之前的代码会执行,之后的代码不会执行。
解决办法:指定正确的容器范围。
var arr = ["a","b","c"];
var arr2 = new Array(10); // 10表示数组中可以存储10个数据
console.log(arr); // (3) ["a", "b", "c"]
var arr3 = new Array(-5); // Uncaught RangeError: Invalid array length
console.log(arr); 因为上一行代码有范围错误,此行代码不再执行。
JS中的this
面向对象语言中 this 表示当前对象的一个引用。
但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。
1. 如果this
出现在普通的函数中,this
表示window
,如果你通过window.xxx
调用一个函数,这个函数中的this
也是window
。
2. 事件绑定,事件处理程序,事件发生时,浏览器帮我们调用这个函数,此函数中的this
表示事件源
3. 在一个对象中,如果有方法(函数),如果通过这个对象调用方法,方法中的this
表示这个对象
4. 在IIFE
中,this
表示window
。
5. 前四点都是在非严格模式下,在严格模式下(use strict
),调用一个普通函数,this
表示undefined
。IIFE
中的this也表示undefined
。
类似
call()
和apply()
方法可以将this
引用到任何对象,使得我们在一个对象中可以使用另一个对象中的属性与方法。
call
apply
bind
都可以修改this指向
call
改变了this
指向且会使函数执行,参数一个个传递,逗号隔开。apply
改变了this
指向且会使函数执行,参数数组形式传递bind
仅仅改变了this
指向,函数不会执行。
var obj1 = {
a : 1 ,
getName: function(){
return this.a
}
}
var obj2 = {
a : 2
}
console.log(obj1.getName.call(obj2)); // 2 得到的是obj2里的 a
// apply和call一模一样,唯一区别是传递参数的区别
function F(a,b) {
console.log("F...")
return a+b;
}
var obj = {};
console.log(F.call(obj,1,2)); // F... 以及 3
console.log(F.apply(obj,[1,2])); // F... 以及 3
// bind 绑定
function F(a,b) {
console.log("F...")
return a+b;
}
var obj = {};
let k = F.bind(obj)
console.log(k(1, 2)); // bind仅仅会改变this指向,并不会执行函数,因此需要我们手动调用 结果: F... 以及 3
类和对象
类:JS中的类本质还是一个函数,是抽象的,并不具体,与new操作符一起使用,就可以得到一个对象。
对象:真实存在的对象个体,在JS中一个{}就是一个对象。
每个类包含数据说明和一组操作数据或传递消息的函数,类的实例称为对象。
1. 类
类的数据类型就是函数,类本身就指向构造函数。
JS封装了许多默认的类:Number
类、String
类、Boolean
类、Array
类、Object
类等;这些类都可以配合new
来创建一个实例化对象。其中Math
这个类是单体内置类,不需要new
。
我们还可以使用function
关键字来自定义类:
// 使用function自定义一个类:
function Fn(sum){
this.sum = sum;
return sum;
};
var num = new Fn(10); // 用new+类创建一个实例化对象
console.log(num); // Fn {sum: 10}
2. 对象
对象是一个容器,是属性的无序集合,对象里面都是一组一组的键值对。
在JS创建对象有两种方式:
1)使用{} 字面量方式
2)new
+ 构造器(本质是函数)
// 使用new 构造器 创建对象
let dog1 = new Object();
dog1.name = "wangcai";
dog1.age = 10;
dog1.run = function(){
console.log("run....")
};
// 使用{} 字面量的方式 创建对象
let dog2 = {
name:"wangcai",
age:10,
run:function(){
console.log("run....")
}
};
JS中一切数据都是对象
判断一个数据是否是对象的方法:
typeof
:typeof
运算符用于判断一个对象的类型,对于一些自己创建的对象,一般会返回object
。
instanceof
:判断一个实例对象是否属于某个类。
console.dir()
: 可以在控制台输出一个对象的所有属性和方法。
// 数组是一个对象
var arr = [];
console.dir(arr); // Array(0)
console.log(typeof arr); // object
console.log(arr instanceof Array); // true
// 函数是一个对象
var f = function(){
};
console.dir(f); // f f()
console.log(typeof f); // function
console.log(f instanceof Function); // true
// html标签是一个对象
let btns = document.getElementsByTagName("button"); // 获取button标签
console.dir(btns); // HTMLCollection(3)
console.log(typeof btns); // object
console.log(btns instanceof HTMLCollection); // true
对象中只有属性
对象中的内容全是属性,也就是一组一组的键值对,方法也是属性,只不过它与一般属性有区别,因此叫方法。
对象中的属性分为私有属性和公有属性,可以通过
object
打点调用。私有属性:指对象自身的属性。
公有属性:指对象从原型中继承的属性。
如果一个私有属性和公有属性重名了 ,私有属性会把公有属性覆盖掉。
可以通过一个api
来查看一个属性是否是私有属性:hasOwnProperty()
。
// 通过hasOwnProperty()来查看某个属性是否是对象的私有属性
var obj = {
name:"wangcai",
age:18
}
console.log(obj.hasOwnProperty("name")); // true
console.log(obj.hasOwnProperty("age")); // true
console.log(obj.hasOwnProperty("sex")); // false sex不是obj的私有属性
console.log(obj.sex); // undefined
sex
不是obj
的私有属性,那么它是obj
的公有属性么?可以看到我们使用obj.sex
并没有报错,那么说明sex
就是obj
的一个属性,且是公有属性,只不过sex
没有赋值,所以输出的是undefined
。
对象中的属性有4大特征
configurable
表示是否可以被删除,true
表示可以删除。writable
表示是否可以修改,true
表示可以修改。enumerable
是否可以枚举 ,是否可以输出true
表示可以输出。value
属性值 默认是undefined
。
原型与原型链
原型: 在JS中,当我们定义了一个函数数据类型的数据时,都会生成一个prototype
属性,它是一个对象 这个对象我们叫原型对象,简称原型。
隐式原型: 每一个对象数类型都有一个__proto__
属性,叫隐式原型。
原型链: 类似于之前讲过的作用域链,原型链是一个对象在自身找不到所需属性的情况下沿着__proto__
一级一级地向上一级原型对象去寻找所需属性的链。
var arr1 = new Array(1,2,3);
console.dir(Array); // ƒ Array()
console.dir(arr1); // Array(3) ==> 0: 1 , 1: 2 , 2: 3 , length: 3