1 hello world 的输出
script 标签嵌入到 html 里面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js</title>
</head>
<body>
<script>
alert('hello world');
</script>
</body>
</html>
1.1 三种引入方式
1.1.1 内嵌式
直接写在 script 标签中。
<script>
alert('hello world');
</script>
1.1.2 行内式
直接嵌入到 html 元素内部。
<div onclick="alert('hello world')">
我是一个div, 点我一下试试
</div>
点击后,才会出现显示框。
1.1.3 外部式
写到单独的 .js 文件中。
<script src="app.js">
</script>
2 console.log
alert 虽然可以借助弹框的方式和用户进行交互,但是弹框操作不是特别的好。
有些对话框一弹出来就会阻止用户操作界面的其他部分,这叫做 模态对话框。
此时更建议使用 console.log,而 console.log 会在控制台打印日志。
<script>
console.log('hello world');
</script>
打开 开发者工具,点击 控制台就可以看到结果了。
3 变量的使用
使用 var 关键字来定义变量,js 定义变量的时候不需要写类型。
虽然是不写类型,但是不意味着没有类型,变量的类型是根据初始化的值来确定的。
下面的 a 变量就是 number 类型(js 不区分 int 和 double,统一都叫做 number)
b 变量就是一个 string 类型。
<script>
var a = 10;
var b = 'hello world';
console.log(a);
console.log(b);
</script>
3.1 var 与 let 的区别
js 定义变量除了 var 关键字,还可以使用 let 关键字。
var 属于老式写法,let 是新式写法,规则比较接近 C++ java 主流语言。
区别1
使用 var 变量是可以重定义的。
并没有报错,可以成功的看到结果。
如果使用 let 的话,变量重定义后会报错。
可以看到这里报了一个变量重定义的错误。
区别2
var 和 let 关键字定义的变量出作用域后代码的结果不同。
可以看到如果是 var 定义的变量,即使是出了作用域也会正确的运行。
可以看到这里报了一个变量不存在的错误。
3.2 js 的动态类型
动态类型 指的是一个变量在程序运行过程中,其类型是可以发生改变的。
<script>
let a = 10;
console.log(a);
a = 'hello';
console.log(a);
</script>
可以看到变量 a 的类型随着赋值的改变也改变了。
动态类型的好处就在于代码非常的灵活,比如要写一个函数来计算两个数字的和。
如果要使用 java ,则需要写两个 int 相加的版本、两个 double 相加的版本、两个 long 相加的版本…
如果是使用 动态类型 ,只需要写一个函数即可。
动态类型的坏处:一个变量当下到底是什么类型。里面存了什么样的值,里面提供了哪些1方法哪些属性…
这些都是不确定的。
4 基本数据类型
number、boolean、string 与 java 是一样的,只不过 number 是不区分整数和小数的。
undefined 和 null 是 java 当中没有的,下面来进行解释。
举个例子,比如说现在要统计学生的信息,有学号、姓名、性别、班级、专业。
但是在这里有的学生表示这里少了个 “电话” 这一个信息,这种情况就是属于 undefined。
而如果信息上面的学号有缺失,此时就是属于 null 情况。
在别的语言中,如果访问到的某个东西是没有定义的,此时会直接报错。
而 js 则不会报错,而是会返回一个 undefined,js 会把别的语言认为是非法的行为合法化。
5 运算符
js 里的大部分运算符用法与 java 是相同的,下面介绍几种不同的。
5.1 == 运算符
== 是比较相等的运算符,但是是会进行隐式类型转换的。
<script>
let a = 10;
let b = '10';
console.log(a == b);
</script>
此时的结果是 true ,说明 a b 两个变量是相同的类型,但是代码表名一个是 number,一个是 string。
这是由于此时的代码发生了 隐式类型转换,上面的 number 类型隐式的转换成了 string 类型。
如果是一个 boolean 和一个 number 比较相等。
<script>
let a = true;
let b = 1;
console.log(a == b);
</script>
此时的结果依然是一个 true,说明此时又发生了 隐式类型转换。
如果此时 b 变量的值不是 1 ,最终的结果则会是 false。
5.2 === 运算符
=== 运算符也是比较相等的运算符,但是是不会进行 隐式类型转换。
可以看到代码此时使用的是 === 比较相等的。
<script>
let a = true;
let b = 2;
console.log(a === b);
</script>
此时的结果是 false,表明当前的两个变量是不同的类型的,而且没有发生隐式类型转换。
6 数组
6.1 数组的创建
1、可以使用 new 关键字来创建一个数组。
let arr = new Array();
需要注意的是 Array 的第一个字母 A 必须要大写。
2、也可以使用 [] 表示一个数组。
let arr = [];
此时的 [] 就代表这是一个数组,数组名叫 arr。
3、创建的时候初始化
let arr = [1, 2, 3, 4];
此时的数组有 4 个元素,数组的长度就是 4。
下面的这种初始化的方式与 java 有着明显的不同。
let arr = [1, 'hello', true, []];
在 js 中,数组元素的类型不要求是统一的,可以是任意的类型。
不仅 js 如此,动态类型 的语言都是如此。
6.2 如何获取数组元素
这里的访问数组元素也是通过数组下标来实现的。
<script>
let arr = ['张三', '李四', '王五', '赵六'];
console.log(arr[0]);
console.log(arr[1]);
console.log(arr[2]);
console.log(arr[3]);
console.log(arr);
</script>
可以看到这里是按照下标的顺序显示出来了,也可以通过直接访问数组名的方式来获取整个数组的元素。
6.3 数组的越界行为
现在有一个长度为 4 的数组,现在尝试越界访问。
如果现在尝试输出下标为 100 的元素的值,不过很明显的是数组下标最大就到 3 ,是没有 100 的。
console.log(arr[100]);
console.log(arr[-1]);
console.log("程序正在运行,没有因为下标越界而异常终止!!!");
此时会发现不但没有报错,还提示了 2 个 undefined 来表示当前的 100 下标和 -1 下标是未定义的。
接下来尝试在 100 下标的位置上赋值,看看会发生什么。
<script>
let arr = ['张三', '李四', '王五', '赵六'];
console.log(arr);
arr[100] = '小八嘎';
console.log(arr);
console.log("程序正在运行,没有因为下标越界而异常终止!!!");
</script>
可以看到数组由原先的 4 个元素变成了现在的 101 个元素,
其中的 空属性*96 表示有96个位置是没有元素的,也就是说,上面的赋值操作将数组长度给扩大了。
可以看到 length 的值是 101,这就表示数组的长度是 101。
而有元素的下标的位置给标注出来了,没有的则没有标注出来。
接下来尝试在数组的 -1 下标处进行获取元素的操作,来观察越界访问后的行为。
<script>
let arr = ['张三', '李四', '王五', '赵六'];
console.log(arr);
arr[-1] = '小八嘎';
console.log(arr);
console.log("程序正在运行,没有因为下标越界而异常终止!!!");
</script>
可以发现当前的数组长度依然是 4 ,但是多了一个 -1 下标并且 -1 下标还有一个值。
接下来如果 把下标变为一个字符串 的话会发生什么呢?
<script>
let arr = ['张三', '李四', '王五', '赵六'];
arr[-1] = '小八嘎';
arr['hello'] = '小西八';
console.log(arr);
console.log("程序正在运行,没有因为下标越界而异常终止!!!");
</script>
可以看到此时数组的长度虽然还是 4 ,但是在 -1 和 hello 下标位置上各有了一个元素。
js 的数组,不仅仅是一个只能按下标来取元素的传统意义上的数组,而是一个带有 键值对 性质的东西。
此时数组可以按照 Map 键值对的方式来组织数据。
很多动态类型的语言都是如此,比如 php。
6.4 数组的遍历
js 中可以使用 for循环 和 foreach 来实现数组的遍历。
下面先来看 for循环 的写法。
<script>
let arr = ['张三', '李四', '王五', '赵六'];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
</script>
可以看到数组中的每一个元素都被打印出来了。
接下来的 foreach 有两种不同的写法。
第一种:
for (let i in arr) {
console.log(arr[i]);
}
此处的 i 表示的是数组下标。
第二种:
for (let elem of arr) {
console.log(elem);
}
此处的 elem 表示的是数组元素。
6.5 数组如何添加元素
给数组的尾部添加一个元素要使用 push 方法。
<script>
let arr = ['张三', '李四', '王五', '赵六'];
arr.push('小八嘎');
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
</script>
可以看到最终的结果多出来一个 “小八嘎”,说明成功的添加了一个元素。
6.6 万能方法 splice
splice 这个方法可以用来 插入、修改、删除…
splice(startIndex, count, 变长参数…)
startIndex 是开始下标,count 表示的是从开始下标之后要操作几个元素,
变长参数的意思就是前面两个参数必须有,后面的变长参数可以没有。
把后面的变长参数的内容,替换到前面的指定区间之内。
如果后面的没有变长参数,就相当于是删除,
如果后面变长参数和前面指定的区间个数一样,此时就是修改/替换。
如果后面变长参数比前面的区间个数长,此时就是新增。
<script>
let arr = ['张三', '李四', '王五', '赵六', '小八嘎'];
arr.splice(3, 2);
console.log(arr);
</script>
此时 splice 里没有变长参数,因此上面代码的含义就是,从 3 下标开始,将后面的 2 个元素全部删除。
可以看到从 “赵六” 开始到后面的两个元素都被删除了。
<script>
let arr = ['张三', '李四', '王五', '赵六', '小八嘎'];
arr.splice(3, 1, '小西八');
console.log(arr);
</script>
此时变长参数和前面指定的区间个数一样,此时代码的含义就是 将 3 下标的元素修改为小西八。
可以看到 3 下标元素(赵六)被替换成了 变长参数 里的元素。
<script>
let arr = ['张三', '李四', '王五', '赵六', '小八嘎'];
arr.splice(3, 0, '小西八');
console.log(arr);
</script>
此时变长参数比前面的区间个数长,因此,上述代码的含义就是 将 3 下标位置上的元素改为小西八。
可以看到 3 下表的内容被改为了 变长参数里的内容。
7 函数
创建函数的时候不需要写返回值类型,也不需要写 public、private之类的,也没有 static。
只需要使用 function 这个关键字,在 function 后面跟上函数名。
7.1 语法格式
// 创建函数/函数声明/函数定义
function 函数名(形参列表) {
函数体
return 返回值;
}
// 函数调用
函数名(实参列表) // 不考虑返回值
返回值 = 函数名(实参列表) // 考虑返回值
- 函数定义并不会执行函数体内容, 必须要调用才会执行,调用几次就会执行几次。
<script>
function hello() {
console.log('hello');
}
hello();
</script>
可以看到此时就输出了函数里的内容。
-
调用函数的时候进入函数内部执行,函数结束时回到调用位置继续执行。
-
函数的定义和调用的先后顺序没有要求。(这一点和变量不同,变量必须先定义再使用)
// 调用函数
hello();
// 定义函数
function hello() {
console.log("hello");
}
7.2 简单求和函数的实现
js 函数里的参数是不需要写类型,传的参数只要可以保证函数内部正常工作即可。
<script>
function add(x, y) {
return x + y;
}
console.log(add(10, 20));
console.log(add('hello', 'world'));
console.log(add(10, true));
console.log(add(undefined, 10));
console.log(add(undefined, '10'));
</script>
NaN 是 Not a Number 的缩写。
如果是 10 和 true 和相加,true 会转化为一个,所以结果就是 11 。
两个字符串相加,则就相当于是一个 字符串拼接 的作用。
undefined 和 数字 10 相加会变成一个 NaN。
undefined 加上一个 字符串 10 时,会把 undefined 转化成一个 字符串 undefined,
所以最终的结果也相当于是一个字符串拼接后的结果。
7.3 参数个数问题
如果实参个数少于形参个数,多出来的形参的值就是 undefined。
如果实参个数更多,多出来的实参,相当于是没有用上。
<script>
function add(x, y) {
return x + y;
}
console.log(add(10, 20, 30));
</script>
可以看到上面方法计算的是参数 10 和 参数 20 的值,而多出来的参数 30 ,则没有用上。
<script>
function add(x, y) {
return x + y;
}
console.log(add('10'));
</script>
可以看到第二个多出来的形参变成了 undefined。
在 js 中,当形参个数与实参个数不匹配的时候,是不会报错的,只会尽可能的执行。
通过一个特殊的变量,是可以拿到所有实参的。
这个变量就是 argument,每个函数里都会自动定义一个这个变量,这其实是一个数组,包含了所有的实参。
<script>
function add() {
let result = 0;
for (let elem of arguments) {
result += elem;
}
return result;
}
console.log(add(10));
console.log(add(10, 20));
console.log(add(10, 20, 30));
console.log(add(10, 20, 30, 40));
</script>
可以看到,虽然实参个数不相同,而且形参也没写,但是依然计算出了正确的结果。
7.4 函数表达式
函数表达式 是先定义一个没有名字的匿名函数,然后再把这个匿名函数赋值给一个 add 变量。
<script>
let add = function() {
let result = 0;
for (let elem of arguments) {
result += elem;
}
return result;
}
console.log(add(10));
console.log(add(10, 20));
console.log(add(10, 20, 30));
console.log(add(10, 20, 30, 40));
</script>
function() 就是一个匿名函数。
add 变量的类型就是 function类型,对于 function 类型的变量是可以调用的。
8 作用域
作用域就是当代码访问某个变量的时候,要去哪里找这个变量。
js 会先找当前作用域,如果当前没有,就会往上一层作用域找,如果一直找不到就会一直往上直到全局作用域。
如果还是找不到那就会报错,或者是报 undefined。
<script>
let num = 1;
function test1() {
let num = 2;
function test2() {
let num = 3;
console.log("test2:" + num);
}
test2();
console.log("test1:" + num);
}
test1();
console.log("globle:" + num);
</script>
当代码执行到 console.log(“test2:” + num); 这一条语句时,是在 test2 函数作用域中寻找 num,
发现 num 是 3 后,输出 3。
当代码执行到 console.log(“test1:” + num); 这一条语句时,是在 test1 函数作用域中寻找 num,
发现 num 是 2 后,输出 2。
当代码执行到 console.log(“globle:” + num); 这一条语句时,会在整个 script 标签里面寻找 num,
发现 num 是1 后,输出 1。
如果将 test2 函数中的 num 注释掉,此时当代码执行到 console.log(“test2:” + num); 这一条语句时,
先是在 test2 函数作用域中寻找 num,结果找不到,于是就会往上一层的 test1 函数的作用域中寻找,
找到后是 2 后,就输出 2。
如果将 test2 和 test1 函数中的 num 都注释掉,此时当代码执行到 console.log(“test2:” + num); 时,
先是在 test2 函数作用域中寻找 num,结果找不到,于是就会往上一层的 test1 函数的作用域中寻找,
结果还是找不到,于是在前往上一层,在全局作用域中找到后,发现是 1 ,就输出 1。
9 对象
JavaScript 的对象 和 Java 的对象概念上基本一致,只是具体的语法表项形式差别较大。
9.1 使用字面量创建对象
使用 { } 创建对象。
<script>
let student = {
name: '蔡徐坤',
age: 25,
weight: 70,
height: 180,
sing: function() {
console.log("鸡你太美");
},
dance: function() {
console.log("铁山靠");
}
};
console.log(student.name);
console.log(student.age);
student.dance();
</script>
9.2 使用 new Object 创建对象
此处的 new object 相当于是创建出来了一个空白的对象。
<script>
let student = new Object();
student.name = "蔡徐坤";
student.age = 25;
student.dance = function() {
console.log("铁山靠");
}
student.sing = function() {
console.log("鸡你太美");
}
console.log(student.name);
console.log(student.age);
student.dance();
student.sing();
</script>
js 中的对象里面的属性、方法,都不是提前约定好的,而是随时可以添加的。
<script>
let student = new Object();
student.name = "蔡徐坤";
student.age = 25;
student.dance = function() {
console.log("铁山靠");
}
student.sing = function() {
console.log("鸡你太美");
}
console.log(student.name);
console.log(student.age);
student.dance();
student.sing();
student.time = function() {
console.log("时长两年半");
}
student.time();
</script>
可以看到,再添加了一个 time 后,有输出了 time 里的内容。