JS课程安排:
- JS基础 :6天
- WebAPI: 6-7天
- JS高级:5天
- jQuery:5天
JS基础:
- 基本介绍、语法、变量、数据类型、运算符
- 条件、循环
- 数组、函数
- 函数——作用域、预解析
- 创建对象、数据类型深入、内置对象
- 内置对象和方法
目录
1. 简介
Brendan Eich 在95年花了10天时间设计出了JavaScript,并且因为它不喜欢Java,所以也不喜欢JavaScript
1.1 JS分三个部分:
- ECMAScript 标准——基本语法
- DOM——Document Object Model
- BOM—— Browser Object Model
1.2 JS能做的东西:
- 网页特效
- 服务端开发 node.js
- 命令行工具 node.js
- 桌面 Electron
- App Cordova
- 控制硬件-物联网 Ruff
- 游戏开发 cocos2d-js
1.3 JavaScript语言特点
- 脚本语言
- 解释性语言
- 动态类型语言
- 基于对象的语言
1.4 Js代码书写位置
- 在script标签中
<script>
alert('haha');
</script>
- 写在html标签中:
<button onclick="alert('abc');" />
- **在外部.js文件中,由html文件引入: **
<script src="demo.js" ></script>
1.5 js代码的注意问题
- 在一对
script
标签中有错误的代码,后面的代码也不会执行 - 一对
script
标签的错误,不会影响其他script
标签中的代码执行; - 在
script
标签中,本来要写type="text/javascript"
或者language="JavaScript"
,但是html5标准中可以省略 type
和language
也可以同时写在script
中script
标签在页面中可以出现多个script
标签的位置一般放在<body>
内部的最后端,有时会放在head中- 如果一个
script
标签的作用是引入外部的js文件,那么这个script
标签中不要写代码
WebStorm不是直接打开文件显示的,而是自己以一个小型服务器的方式浏览页面
2. 变量
2.1 变量声明初始化
a. 基本代码规范:
- JS变量区分大小写
- 声明变量使用
var
- 字符串使用单引号或者双引号
- 语句用分号结尾
b. 变量名规范:
- 一般以字母,$,下划线,数组组成,但不能以数字开头
- 变量名一般小写
- 使用camelCase方式命名
- 不能使用关键字
- 第二次初始化同一个变量名,则初始化会变成赋值
c. 声明+初始化:
var num = 10;
var num1, num2, num3;
var num1, num2, num3 = 10;
var num1=10, num2 = 10, num3 = 20;
var num1 = num2 = num3 = 30;
var name = '小黑'; // 字符串使用 " 或者 '
var flag = true;
var obj = null;
console.log(flag);
2.2 代码注释
// 单行注释
/*
多行注释
*/
/**
* 这是说明这个函数是干什么的
* @param param1 这里说明这个param1参数是干什么的
* @return {} 这里说明返回值是个什么
* */
2.3 变量类型
a. 原始数据类型:
number
: 整数和小数都是 number 类型, NaN也是number类型string
boolean
null
: 类型还是 objectundefined
: 变量声明没赋值、 函数没有返回值确赋值给了一个变量; undefined 和数字运算,结果为 NaN,也是number类型object
b. 查看变量类型:
typeof(变量);
typeof 变量;
// number string boolean object undefined object function
c. 进制表示
十六进制: 0x
八进制: 0
d. 数字类型
数字最大最小值
Number.MAX_VALUE
Number.MIN_VALUE
Infinity // 无穷大
-Infinity //无穷小
不要用小数和小数相等
(0.1+0.2) == 0.3 // false
和NaN比较
- 数字和undefined运算,结果是NaN,类型为number;
- 数字和NaN运算,结果是NaN,类型为number
- 使用
isNaN
判断 isNaN
不光能用来判断NaN
,主要可以判断是不是数字
var num;
typeof (num+10); // number---NaN
num+10 == NaN; // false , NaN之间用 == 比较都是false
// 使用 isNaN判断
isNaN(10); // NaN != NaN
e. 字符串
长度
// 长度
var strvar = "hahsasdsad";
strvar.length
拼接
- 加法中有一个是字符串,则会转变成字符串拼接
- 如果使用了减法、乘法、除法,则会尝试将字符串变成数字后运算。如果转换失败,则返回
NaN
// 拼接:
str3 = str1 + str2
console.log(10+"20"); // 1020
console.log(10-"20"); // -10
console.log(10 * "5"); // 50
2.4 ★类型转换
其他类型转数字:
parseInt("10"); //10
parseInt("10asdasdasd"); //10
parseInt("10.98"); //10
parseInt("asd10"); //NaN
parseFloat("10") //10
parseFloat("10asdasd") //10
parseFloat("10.98") //10.98
parseFloat("10.98asdsad") //10.98
parseFloat("asd10asdasd") //NaN
Number
就是转数字, 和parse不同,它需要全部都是数字,更加严格。
Number("10"); //10
Number("10asdasd"); //NaN
Number("10.98"); //10.98
Number("10.98asdsad"); //NaN
Number("asd10asdasd"); //NaN
其他类型 转字符串:
第一种:(要变量有意义,比如不能为 undefined, null)
num.toString(); //变量无意义,则无法使用 toString()
第二种:
String(var);
第三种:
var + '';
其他类型转boolean:
Boolean(var):
1 true
0 false
11 true
-10 true
"hah" true
"" false
null false
undefined false
3. 操作符
- 算数运算符:
+ - * / %
( python没有整除,需要用 parseInt来取整) - 一元运算符:
++ --
- 二元运算符:
- 三元运算符:
?:
- 赋值运算符:
+= -= /= %= *=
(字符串和数字比较的时候会有类型转换) - 关系运算符:
> >= < <= == === != !===
== 不严格的等于:
var str = "5";
var int = 5;
str == int ; // true
=== 严格的等于:
str === int ; // false
- 逻辑运算符:
&& || !
var a = null;
// 没报错,短路比较
if(a != null && a.length > 5){
alert(a.length);
}
算术运算表达式
关系运算表达式
逻辑运算表达式
优先级
优先级从高到底
1. () 优先级最高
2. 一元运算符 ++ -- !
3. 算数运算符 先* / % 后 + -
4. 关系运算符 > >= < <=
5. 相等运算符 == != === !==
6. 逻辑运算符 先&& 后||
7. 赋值运算符
4. 条件
if (条件){ // 条件部分会被转换成 Boolean
}
if(){
} else{
}
if(){
} else if(){
} else{
}
//三元表达式
let result = a?b:c;
// switch判断
switch (key) {
case value:
//代码..
break;
default:
break;
}
switch 注意事项
switch内部的比较是使用的===
,判断类型+内容,而不是==
var age = "10"
switch (key) {
case 10:
..
break;
case "10":
console.log("字符串10"); // 会执行这一句
break;
}
5. 循环
document.write()
写到HTML文档中的东西,在源代码中看不到,而在调试窗口的element能看到。
- while
- do-while
- for
continue
,break
while (condition) {
语句
}
do {
语句
} while (condition);
for (let index = 0; index < array.length; index++) {
const element = array[index];
}
for in
for of
a.foreach
6. 数组
6.1 创建数组
- 通过构造函数
var ar = new Array(); // 构造函数创建空数组
var ar = new Array(5); // 创建长度为5的数组, 每个元素都是 undefined
var ar = new Array(1,2,"3",new Object()); // 直接传入内容
console.log(arra.length);
- 通过字面量
var ar = []; //字面量创建
数组特点
- 使用长度的方式创建数组,数组内部的值都是
undefined
- 数组可以存储不同类型内容
- 可以动态给数组的任意索引赋值,这能改变数组长度,并且没有值的都是
undefined
6.2 动态改变数组长度
var ar = [];
alert(a.length); // 0
ar[1] = "";
alert(a.length); // 2
a[2] ; //undefined 不存在的值会undefined
6.3 遍历
for(let i = 0; i < a.length; i++){
}
a.forEach(element => {
console.log(element);
});
for (const iterator of a) {
console.log(iterator);
}
7. 函数
7.1 函数定义
function funcname(arg1, arg2){
...
return result;
}
Math.PI
★函数的注意问题:
- 函数一旦重名(多次定义),后面的会覆盖前面的函数
function f1(){
console.log('1');
}
f1();
function f1(){
console.log('2');
}
f1();
// 结果会打印2次 2
- 形参的个数和实参的个数可以不一致(可多可少), 没有传递的值或者接收了没有return的函数值,结果是
undefined
function haha(x,y) {
console.log(x);
console.log(y);
console.log(arguments.length);
}
haha(); // undefined undefined 0
haha(1,2,3); // 1 2 3
- 直接输出函数名字,结果为函数的代码
7.2 函数的参数和返回值
a. 返回值问题
- 如果没有return,函数返回值为
undefined
- 如果return后面没有内容,则返回值也是
undefined
b. arguments
arguments
是一个伪数组
function funcname(x, y){
console.log(arguments); // 获取所有传入的参数 arguments.length
}
7.3 命名函数和匿名函数
函数有名字就是命名函数,没名字就是匿名函数
function(){
// 这是匿名函数
}
function f1(){
// 这是匿名函数
}
函数自调用——匿名函数直接调用
(function (){
})()
7.4 ★函数声明和函数表达式
- 函数能够调用,是因为函数名存储的函数的代码:
- 如果函数能被调用,则调用方式为:
函数的代码();
1. 函数声明
function f1(){
}
f1(); // 函数代码();
2. 函数表达式
函数表达式后面要加分号
var f2 = function (){
};
f2();
3. 函数表达式的覆盖问题
函数声明重名的,后面覆盖前面的,导致前面的失效; 而函数表达式,只有在赋值的时候,才会覆盖:
function f1(){
console.log('1');
}
f1(); // 2
function f1(){
console.log('2');
}
f1(); // 2
f2 = function (){
}
f2();
// 1
f2 = function (){
console.log('2');
}
f2();
// 2
7.5 函数作为参数和返回值
函数作为参数,叫做回调函数
typeof f1 // function
function f1(f2){
f2();
}
function f2(){
}
作为返回值:
function f3(){
return function(){
console.log();
}
}
fn = f3();
fn();
7.6 ★作用域
1. 跨越script标签
跨越script标签,也可以引用:
<script>
var num = 10;
console.log(num); // 10
</script>
<script>
console.log(num); // 10
</script>
2. 块级作用域
在没有
let
之前,js没有块级作用域
{
var num = 10;
}
console.log(num); // 10
使用let
定义的,才是块级作用域; 用var
的话,没有块级作用域
3. 全局变量
var
在函数外面声明的变量是全局变量- 除了函数内部定义的, 其他定义的都是全局变量(除了let)
4. 局部变量
函数内部定义的是局部变量, 只在函数内部有效
5. 隐式全局变量
- 声明变量没有使用
var
,叫作隐式全局变量 - 隐式全局变量定义在函数中,外面也能访问
- 定义变量使用
var
是不会被删除的,没有使用var
是会被删除的
var num1 = 10;
num2 = 20;
delete num1;
delete num2;
console.log(typeof num1); // number
console.log(typeof num2); // undefined
6. 作用域链
- 标签内定义的叫做0级作用域
- 0级作用域定义的函数内部,叫做1级作用域
- …
- 函数最内层查找变量的顺序,是 N级–> … --> 3级–> 2级–>1级–>0级
下面的num
,会一层层向上找,直到找到
var num = 10;
function f1(){
var num = 20;
function f2(){
var num = 30;
function f3(){
var num = 40;
console.log(num);
}
f3();
}
f2();
}
f1();
7.5 ★★预解析
JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。JavaScript解析器执行JavaScript代码的时候,分为两个过程:
- 预解析过程
- 代码执行过程
- 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
- 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
- 先提升var,在提升function
- 变量提升
定义变量的时候,变量的声明会被提升到作用域的最上面,变量的赋值不会提升。 - 函数提升
JavaScript解析器首先会把当前作用域的函数声明提前到整个作用域的最前面
特点
- 浏览器把变量、函数的声明提前了,提前到当前作用域的最上面,不会提升到作用域之外
- 多对
<script>
之间的预解析不会冲突——只会在各自范围内进行预解析 - 先提升
var
再提升function
,即 var在function之前 - 函数表达式不会提升
例子
例1——预解析提前 变量声明
console.log(num); // 结果为 undefined,代表变量声明了但是没赋值
var num = 10;
例2——预解析提前函数声明
f1() // 调用成功,说明函数声明被提前了
function f1(){
console.log("haha")
}
例3——num提前到作用域内部的顶端
var num = 20;
function f1(){
console.log(num); // 结果为undefined, 说明 内部的num被提升到了 f1内部作用域的顶端
var num = 10;
}
f1();
//相当于:
var num = 20;
function f1(){
var num;
console.log(num);
num = 10;
}
例4:
f1(); // 打印 undefined
var num = 20;
function f1(){
console.log(num);
}
// 相当于:
var num;
function f1(){
console.log(num);
}
f1(); // 所以是undefined
num = 20;
例5——预解析不会把变量提升到函数外
function f1(){
console.log(num); // undefined
var num = 10;
}
f1();
console.log(num); // 报错
//相当于
function f1(){
var num;
console.log(num);
num = 10;
}
f1();
console.log(num);
例6:——预解析只在script标签内部(同一个全局作用域内)
<script>
f1(); // 1
function f1(){
console.log("1");
}
</script>
<script>
function f1(){
console.log("1");
}
</script>
<script>
function f1(){
console.log("1");
}
</script>
<script>
f1(); // 2
function f1(){
console.log("1");
}
</script>
例7:
console.log(a); // 易错点:输出函数代码
function a(){
console.log('hahaha');
}
var a = 1;
console.log(a); // 输出1
// 相当于
var a ;
function a(){...} // 此处a就变成了代码
console.log(a);
a = 1;
console.log(a);
例8:—— 隐式全局变量的提升
f1();
console.log(c); // 9
console.log(b); // 9
console.log(a); // 报错
function f1(){
var a = b= c = 9; //易错点
console.log(a);
console.log(b);
console.log(c);
}
//提升后为:
function f1(){
var a; //局部变量
a = 9;
b = 9; //隐式全局变量
c = 9; //隐式全局变量
console.log(a);
console.log(b);
console.log(c);
}
f1();
console.log(c);
console.log(b);
console.log(a);
例9——函数表达式不会提升
f1(); //无法调用, 因为函数表达式不会提升, 只提升函数定义
var f1 = function(){
console.log(a);
var a = 10;
}
// 提升后为:
var f1;
f1();
f1 = function(){...};
8. 对象
- JS本来没有类的概念,只有对象。
- JS是基于对象的语言——万物皆对象,而不是真正的面向对象的语言,但是可以模拟面向对象
- 判断对象类型:
变量 instanceof 类型
- 访问自己对象的属性/方法:
this
8.1 创建对象
1. 通过调用系统的构造函数创建
new Object()
var obj = new Object();
// 添加属性和方法
obj.age = 18;
obj.name = "foo";
obj.eat = function(){
this.age ... //当前对象中使用 this关键字
};
1b 工厂方法创建对象
function createPeople(name, age){
var person = new Object();
person.name = name;
person.age = age;
person.sayHi = function(){
};
}
2. 自定义构造函数创建
Object
是系统构造函数,系统定义方式为:
function Object(value){}
使用构造函数. 和普通函数的区别就是名字开头是否大写
function Person(name, age){
this.name = name;
this.age = age;
this.sayHi = function(){
console.log(this.name);
}
}
var obj = new Person('haha',18);
obj instanceof Person
创建对象经历的事情:
- 在内存中申请一块空闲的空间,创建对象
- 把
this
设置为当前对象 - 设置对象属性和方法
- 返回this对象
3. 字面量形式创建
var obj = {};
obj.name = "";
obj.sayHi = function(){
console.log(this.name):
}
var obj = {
name: "小明",
age: 20;
sayHi: function(){
},
eat: function(){
}
};
8.2 获取和设置对象的属性、方法—— .
和 []
以某个变量值当做属性名来访问,就只能使用[]
obj.属性名 // 不管有没有,都能 点出来,点出来是 undefined
obj["属性名"] = 值
obj.plary();
obj['play']();
8.3 Json
- 对象:是 属性与方法的集合
- 对象:也是一组无序属性的集合,属性的值可以是任意类型
json: 键/值 都用双引号括起来
var json = {
"name":"ab",
"age" : "18",
"sex": "male"
}
json.ddsdsd // 能点出来,但是没有的属性,都是 undefined
json.name
json["name"]
// 遍历对象
for(var key in obj){
console.log(key); // 遍历对象的属性
console.log(obj[key]); // 注意不要使用 obj.key,这就成了访问 'key'这个属性了
}
8.4 JS的数据类型
原始数据类型: number, string, boolean, undefined, null, object:
- 基本类型(简单),值类型: number, string, boolean
- 复杂类型(引用):object
- 空类型: undefined, null
var num1 = 66;
var num2 = 88;
function f1(num, num1) {
num = 100;
num1 = 100;
num2 = 100;
console.log(num1);
console.log(num2);
console.log(num);
}
f1(num1, num2);
console.log(num1);
console.log(num2);
console.log(num);
9. 内置对象
JS有三种对象:内置对象、自定义对象、浏览器对象
在MDN上自学
9.1 Math对象——是一个对象,但不是函数对象
Math的所有属性都是静态属性
Math在内部的定义方式:
Math = {} //Math是用 {}定义的,而不是 function Math() , 所以无法new
Math.abs = function(){} //静态
可以new的对象是这么定义的:
function XXX(){
}
XXX.prototype.funcname = function(){} // 实例方法
// Math 的方法/属性
Math.random(); // 返回[0,1)的小数
Math.PI
Math.E
Math.abs(null); ---0
Math.abs("abc"); ---NaN
.ceil .floor
.max .min
Math.pow(7,2); -- 49
Math.sqrt(16); -- 4
9.2 Date——创建实例对象使用
创建:
var birthday = new Date(1231252151251); // timestamp创建
var birthday = new Date(1995, 11, 17); // 12月17日,月份从0开始
var birthday = new Date(1995, 11, 17, 3, 24, 0);
var d = new Date('2019-9-11')
var current = new Date();
var now = Date.now(); 返回当前时间的timestamp(毫秒)
获取方法:
var now = new Date();
now.getFullYear(); // 获取年份
now.getMonth(); // 获取月份,从0开始
now.getDate(); // 获取日期
now.getDay(); // 获取星期,从0开始
now.getHours();
now.getMinutes();
now.getSeconds();
now.getMilliseconds()
d.valueof() //获得毫秒
var d = + new Date() //不支持H5的浏览器 通过这种方式获取毫秒
封装获取日期时间函数:
function formatDate(d) {
//如果date不是日期对象,返回
if (!date instanceof Date) {
return;
}
var year = d.getFullYear(),
month = d.getMonth() + 1,
date = d.getDate(),
hour = d.getHours(),
minute = d.getMinutes(),
second = d.getSeconds();
month = month < 10 ? '0' + month : month;
date = date < 10 ? '0' + date : date;
hour = hour < 10 ? '0' + hour : hour;
minute = minute < 10 ? '0' + minute:minute;
second = second < 10 ? '0' + second:second;
return year + '-' + month + '-' + date + ' ' + hour + ':' + minute + ':' + second;
}
9.3 String
字符串是不可变的
string
——字符串类型——基本类型
var str = "内容";
String
——字符串类型——引用类型
var str2 = new String("内容"); // 引用类型
str.length
str.concat(str2,str3..)
str.indexOf('substr')
str.match(regex)
.replace .search .slice .split .substr .substring
.toLowerCase .toUpperCase .trim()
String.fromcharcode()
9.4 Array
创建方式
- 字面值:
[]
- 构造函数:
new Array()
判断是不是数组
Array.isArray()
ar instanceof Array
方法 | 描述 |
---|---|
Array.isArray() |
判断是不是数组 |
arr.length | 获取长度 |
Array.from([1,2,3,4]) |
从已有数组创建新数组 |
arr1.concat(arr2) |
数组拼接 |
arr.every(callback[, thisArg]) |
所有元素都要满足条件,才返回true |
arr.filter(callback[, thisArg]) |
返回满足条件元素构成的新数组 |
添加和删除方法 | |
arr.pop() |
从后面删除并且返回 |
arr.push(值) |
在后面插入,返回数组长度 |
arr.shift() |
删除第一个元素并且返回 |
.arr.unshift(值) |
在前面插入,返回长度 |
arr.foreach(callBack) |
遍历 |
arr.indexOf() |
|
var str = arr.join('符号') |
|
arr.map(callback) |
|
arr.reverse() |
|
arr.sort(callback) |
|
arr.slice(1,3) |
返回浅拷贝,第一个包括,第二个参数不包括 |
9.5 基本包装类型
隐式转换成基本包装类型
- 普通变量没法调用属性和方法
- 本身是基本类型,但是在执行代码的过程中,如果这种类型变量调用了属性或者方法,那么这种类型就不再是基本类型了, 而是基本包装类型; 这个变量也就变成了包装类型:
基本类型:
- string
- number
- boolean
var str1 = "hello";
str1.replace("ll","HH"); // 调用了方法, 这个变量就成了基本包装类型对象
str.indexOf()
num.toString()
Boolean的基本包装类型
- 如果是一个对象&&true, 结果为true
- 如果是 true&&对象,返回结果为 这个对象
// boolean的基本包装类型
var flag = new Boolean(false);
flag && true // true , 因为第一个是对象,对象是true
true && flag // 返回是这个对象
基本包装类型 vs. 类型转换
var num2 = Number("10"); //类型转换
var num3 = new Number("10"); //基本包装类型
案例练习
交换2个变量的值:
var num1 = 10, num2 = 20;
var num1 = num1 + num2;
num2 = num1 - num2;
num1 = num1 - num2;
var num1 = 10, num2 = 20;
var temp = num2;
num2 = num1;
num1 = temp;
console.log(num1,num2);
var num1 = 10, num2 = 20;
num1 = num1 ^ num2;
num2 = num1 ^ num2;
num1 = num1 ^ num2;
++、–练习
var a = 1;
alert( ++a + ++a + ++a); //9
alert(a); //4
闰年
let year = parseInt(prompt("请输入年份"));
if (year % 400 == 0){
alert("闰年");
}
else if(year % 4 == 0 && year % 100 != 0 ){
alert("闰年");
}
else{
alert("不是闰年");
}
画星星、乘法口诀表
冒泡排序
var a = [4, 6, 7, 1, 3, 8, 10, 9, 5];
for (let i = 0; i < a.length - 1; i++) {
for (let j = 0; j < a.length -i-1; j++) {
if (a[j] > a[j + 1]) {
let temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
alert(a);