JavaScript
(简称 JS)
- 是世界上最流行的编程语言之一
- 是一个脚本语言, 通过解释器运行
- 主要在客户端(浏览器)上运行, 现在也可以基于 node.js 在服务器端运行
JavaScript 最初只是为了完成简单的表单验证(验证数据合法性),结果后来不小心就火了。当前 JavaScript 已经成为了一个通用的编程语言。
💕 JavaScript能做的事情:
- HTML: 网页的结构(骨)
- CSS: 网页的表现(皮)
- JavaScript: 网页的行为(魂)
JS通常是运行在浏览器上的。
.html
文件浏览器(应用程序)就会读取文件, 把文件内容加载到内存中(数据流向: 硬盘 => 内存
)数据流向: 内存 => CPU
)💕 浏览器分成渲染引擎 + JS 引擎
渲染引擎:
解析 html + CSS, 俗称 “内核”JS 引擎:
也就是 JS 解释器. 典型的就是 Chrome 中内置的 V8
JS 引擎逐行读取 JS 代码内容, 然后解析成二进制指令, 再执行。
ECMAScript(简称 ES):
JavaScript 语法DOM:
页面文档对象模型, 对页面中的元素进行操作BOM:
浏览器对象模型, 对浏览器窗口进行操作光有 JS 语法, 只能写一些基础的逻辑流程,但是要想完成更复杂的任务, 完成和浏览器以及页面的交互, 那么久需要 DOM API
和 BOM API
这主要指在浏览器端运行的JS
,如果是运行在服务端的JS, 则需要使用 node.js 的 API,就不太需要关注 DOM 和 BOM
💕 重要概念: ECMAScript
这是一套 “标准”, 无论是啥样的 JS 引擎都要遵守这个标准来实现。
啥叫标准? 车同轨, 书同文. 秦始皇最大的贡献之一, 就是制定了一套标准,三流公司做产品, 一流公司做标准。
💕 行内式
直接嵌入到 html 元素内部
<input type="button" value="这是一个按钮" onclick="alert('哈哈哈')">
当我们按下按钮后,会有如下效果:
💕 内嵌式
写到 script 标签中
<script>
alert("haha");
script>
💕 外部式
<script src="./js01.js">script>
💕 第一个JS程序
注释:
//
[建议使用]/* */
使用 ctrl + /
切换注释,多行注释不能嵌套。形如这种代码就会报错:
/*
/*
我是多行注释
我是多行注释
我是多行注释
*/
*/
💕 输入:prompt
弹出一个输入框,提示输入自己的姓名。
<script>
prompt("请输入您的姓名: ");
script>
💕 输出: alert
<script>
alert("hello 你好");
script>
💕 输出: console.log
:向控制台打印一个日志,供程序员看。
<script>
console.log("hello 你好");
script>
注意:这样的输出一般不是给普通用户看的, 而是程序员来看的
重要概念: 日志
日志是程序员调试程序的重要手段
去医院看病, 医生会让患者做各种检查, 血常规, 尿常规, B超, CT等… 此时得到一堆检测结果. 这些结果普通人看不懂, 但是医生能看懂, 并且医生要通过这些信息来诊断病情,这些检测结果就是医生的 “日志”
PS: 相比于医生来说, 程序猿多一个终极大招, “重启下试试”。
重要概念: .
console 是一个 js 中的 “对象”,.
表示取对象中的某个属性或者方法. 可以直观理解成 “的”。console.log 就可以理解成: 使用 “控制台” 对象 “的” log 方法。
💕 创建变量(变量定义/变量声明/变量初始化)
<script>
var name = "chenjiale";
console.log(name)
let age = "23"
script>
这里定义变量时我们不需要指定数据类型,使用var
或者let
即可,那我们该如何选择呢?
let比var出现的晚,意味着避免了var这块定义变量的时候的一些缺陷,如果使用let定义变量,此时变量的生命周期、作用域基本和Java类似。
JS对于数字类型只有一种类型:数值类型。
💕 强类型变量和弱类型变量
强类型变量
意味着不同类型的变量之间进行赋值的时候,需要一定的手段(强制类型转换)。弱类型变量
在不同类型变量之间进行赋值的时候,可以直接赋值。
<script>
a = 1
console.log(a)
b = 'b'
b = a;
console.log(b);
script>
这里我们可以看到JS中的变量是弱类型变量。
💕 动态类型变量和静态类型变量
动态类型变量
意味着代码在执行过程中,变量的类型可以随时发生变化。静态类型变量
意味着变量定义的时候是什么类型,在运行过程中就是什么类型。
<script>
a = 1
console.log(typeof(a))
b = 'b'
a = b
console.log(typeof(a));
script>
随着程序运行, 变量的类型发生了改变,说明该变量类型是动态类型变量。
JS 中内置的几种类型
💕 number 数字类型
JS 中不区分整数和浮点数, 统一都使用 “数字类型” 来表示。
<script>
let a = 10;
console.log(typeof(a))
a = a / 3
console.log(a)
console.log(typeof(a))
script>
这里我们可以看到,整数和小数在JS中都用number类型表示。
数字进制表示
计算机中都是使用二进制来表示数据, 而人平时都是使用十进制,因为二进制在使用过程中不太方便(01太多会看花眼),所以在日常使用二进制数字时往往使用 八进制
和 十六进制
来表示二进制数字.
注意:
- 一个八进制数字对应三个二进制数字
- 一个十六进制数字对应四个二进制数字. (两个十六进制数字就是一个字节)
- 各种进制之间的转换, 不需要手工计算, 直接使用计算器即可
特殊的数字值
Infinity
: 无穷大, 大于任何数字. 表示数字已经超过了 JS 能表示的范围.-Infinity
: 负无穷大, 小于任何数字. 表示数字已经超过了 JS 能表示的范围.NaN
: 表示当前的结果不是一个数字.
<script>
let a = 10;
console.log(a/0)
console.log(-a/0)
console.log('hehe' - 10)
script>
注意:
- 负无穷大 和 无穷小 不是一回事. 无穷小指无限趋近与 0, 值为
1 / Infinity
。- ‘hehe’ + 10 得到的不是 NaN, 而是 ‘hehe10’, 会把数字隐式转成字符串, 再进行字符串拼接。
- 可以使用 isNaN 函数判定是不是一个非数字。
console.log(isNaN(10)); // false
console.log(isNaN('hehe' - 10)); // true
💕 string 字符串类型
基本规则:字符串字面值需要使用引号引起来, 单引号双引号均可
<script>
let a = '哈哈哈'
a = "JS"
a = "哈哈哈'JS'"
a = '哈哈哈"JS"'
alert(a)
script>
如果字符串中本来已经包含引号咋办?
var msg = "My name is "zhangsan""; // 出错
var msg = "My name is \"zhangsan\""; // 正确, 使用转义字符. \" 来表示字符串内部的引
号.
var msg = "My name is 'zhangsan'"; // 正确, 搭配使用单双引号
var msg = 'My name is "zhangsan"'; // 正确, 搭配使用单双引号
转义字符
有些字符不方便直接输入, 于是要通过一些特殊方式来表示.
\n
\\
\'
\"
\t
求长度: 使用 String 的 length
属性即可
<script>
let a = "哈哈哈"
alert(a.length)
script>
字符串拼接:使用 +
进行拼接
<script>
a = "程序人"
console.log("Hello" + a)
console.log("10" + 10) // 这里和Java中类似
script>
💕 boolean 布尔类型
boolean类型表示 “真” 和 “假”,boolean 原本是数学中的概念 (布尔代数),在计算机中 boolean 意义重大,往往要搭配条件/循环完成逻辑判断。
JS中的boolean类型和C语言中的bool类型类似,参与运算时,true表示1,false表示0
。
<script>
let a = false
console.log(typeof(a))
a = a + 1
console.log("a的值等于" + a + ",a的类型" + typeof(a))
script>
💕 undefined 未定义数据类型
如果一个变量没有被初始化过,结果就是 undefined,是 undefined 类型。
<script>
let a;
console.log(a)
console.log(a + "20")
console.log(a + 1)
console.log(a + true)
script>
💕 null 空值类型
<script>
let a;
let b = null;
console.log(a + 10);// 运行结果NaN
console.log(b + 10);// 运行结果是10
script>
JavaScript 中的运算符和 Java 用法基本相同. 此处不做详细介绍了。
💕 算术运算符
+
-
*
/
💕 赋值运算符 & 复合赋值运算符
=
+=
-=
*=
/=
%=
💕 自增自减运算符
++
: 自增1--
: 自减1
💕 比较运算符
<
<=
>=
==
比较相等(会进行隐式类型转换)!=
===
比较相等(不会进行隐式类型转换)!==
<script>
let a = 10;
let b = "10";
alert(a == b)
script>
<script>
let a = 10;
let b = "10";
alert(a === b)
script>
💕 逻辑运算符
用于计算多个 boolean 表达式的值.
&&
与: 一假则假||
或: 一真则真!
非
💕 移位运算
<<
左移>>
有符号右移(算术右移)>>>
无符号右移(逻辑右移)
💕 if语句
// 形式1
if (条件) {
语句
}
// 形式2
if (条件) {
语句1
} else {
语句2
}
// 形式3
if (条件1) {
语句1
} else if (条件2) {
语句2
} else if .... {
语句...
} else {
语句N
}
案例:
<script>
let num = prompt("请输入数字")
if(num % 2 == 0) {
alert("这个数字是偶数")
} else if(num % 2 == 1) {
alert("这个数字是奇数")
} else {
alert("非法输入")
}
</script>
💕 三元表达式
是 if else 的简化写法,
条件 ? 表达式1 : 表达式2
条件为真, 返回表达式1 的值. 条件为假, 返回表达式2 的值。注意, 三元表达式的优先级是比较低的
alert(1 === "1" ? true : false)
💕 switch语句
用户输入一个整数, 提示今天是星期几
let day = prompt("请输入今天是星期几:")
day = parseInt(day)
switch(day) {
case 1:
alert("今天是星期一")
break
case 2:
alert("今天是星期二")
break
case 3:
alert("今天是星期三")
break
case 4:
alert("今天是星期四")
break
case 5:
alert("今天是星期五")
break
case 6:
alert("今天是星期六")
break
case 7:
alert("今天是星期天")
break
default:
alert("非法输入")
}
💕 for循环语句
let result = 0;
for(let i = 100; i <= 200; i++) {
if(i % 3 == 0) {
result = i;
break;
}
}
alert("100~200之间第一个3的倍数是" + result)
💕 while循环语句
while (条件) {
循环体;
}
执行过程:
案例:
var num = 1;
while (num <= 10) {
console.log(num);
num++;
}
💕 创建数组
使用 new 关键字创建
// Array 的 A 要大写
var arr = new Array();
使用字面量方式创建 [常用]
var arr = [];
var arr2 = [1, 2, 'haha', false]; // 数组中保存的内容称为 "元素"
案例:
let array = new Array();
let array1 = [1, 2, 3, "4"]
console.log(typeof(array1))
console.log(array1)
console.log(array1[2])
注意: JS 的数组不要求元素是相同类型,这一点和 C, C++, Java 等静态类型的语言差别很大. 但是 Python, PHP 等动态类型语言
也是如此。
let array1 = [1, 2, 3, "4"]
// 这种写法在JS中并不会抛异常, 如果下标超出范围读取元素, 则结果为 undefined
console.log(array1[-10])
array1 = "程序人"
console.log(typeof(array1))
console.log(array1)
注意
: 不要给数组名直接赋值, 此时数组中的所有元素都没了,相当于本来 arr 是一个数组, 重新赋值后变成字符串了。
💕 新增数组元素
undefined
let array = [1, 2, 3, 4]
array.length = 10;
console.log(array)
array[7] = 8
console.log(array)
console.log(array[9])
let array = new Array()
console.log(array)
for(i = 0; i < 10; i++) {
array[i] = i;
}
console.log(array)
let array = new Array()
console.log(array)
for(i = 0; i < 10; i++) {
array[i] = i;
}
let new_array = new Array()
// 将array中所有的元素追加到new_array中
for(i = 0 ;i < array.length; i++) {
new_array.push(array[i])
}
console.log(new_array)
💕 删除数组元素
使用 splice
方法删除元素
let array = new Array()
console.log(array)
for(i = 0; i < 10; i++) {
array[i] = i;
}
console.log(array)
// 第一个参数表示从下表为 1 的位置开始删除. 第二个参数表示要删除的元素个数是 2 个
array.splice(1, 1)
console.log(array)
💕 语法格式
// 创建函数/函数声明/函数定义
function 函数名(形参列表) {
函数体
return 返回值;
}
// 函数调用
函数名(实参列表) // 不考虑返回值
返回值 = 函数名(实参列表) // 考虑返回值
注意:
function hello() {
console.log("hello");
}
// 如果不调用函数, 则没有执行打印语句
hello();
// 调用函数
hello();
// 定义函数
function hello() {
console.log("hello");
}
案例
function hello() {
console.log("hello")
}
hello()
function HelloXiaobite(num, name) {
console.log(num + " Hello " + name)
return 1;
}
HelloXiaobite(1, "小明")
如果我们将一个函数返回值赋值给一个变量,那么函数的返回值是什么类型,这个变量就是什么类型。
function HelloXiaobite(num, name) {
console.log(num + " Hello " + name)
return 1;
}
let b = HelloXiaobite(1, "小明")
console.log(typeof(b));
💕 关于参数个数
实参和形参之间的个数可以不匹配,但是实际开发一般要求形参和实参个数要匹配
sum(10, 20, 30); // 30
sum(10); // NaN, 相当于 num2 为 undefined
JS 的函数传参比较灵活, 这一点和其他语言差别较大. 事实上这种灵活性往往不是好事。
案例:
function HelloXiaobite(num, name) {
console.log(num + " Hello " + name)
return 1;
}
HelloXiaobite();
HelloXiaobite(1, "小明", 3)
💕 函数表达式
我们可以在定义函数时在最前面赋给一个变量,后面就可以通过这个变量来调用函数了。
let result = function Sum() {
// 计算1~100之间的和
ret = 0
for(i = 0; i <= 100; i++) {
ret += i
}
return ret
}
console.log(result())
当然在这里我们可以省略函数名,变量 = function() { }
这样的写法定义了一个匿名函数,随后直接通过这个变量来调用函数。
let b = function() {
console.log(arguments)
}
b();
b(1, 2, 3)
💕 作用域
某个标识符名字在代码中的有效范围,在 ES6 标准之前, 作用域主要分成两个:
全局作用域
: 在整个 script 标签中, 或者单独的 js 文件中生效局部作用域/函数作用域
: 在函数内部生效例如:
for(i = 0; i <= 100; i++) {
}
console.log(i)
// 在JS,在for循环中如果变量没有通过let,var定义,此时就变成一个全局变量
例如:
let j = 0
for(let i = 0; i <= 100; i++) {
j += i
}
console.log(j)
💕 作用域链
- 函数可以定义在函数内部
- 内层函数可以访问外层函数的局部变量.
内部函数可以访问外部函数的变量,采取的是链式查找的方式,从内到外依次进行查找。
let num = 10
function test01() {
let num = 100
console.log(num)
function test02() {
let num = 200
console.log(num)
}
test02()
}
test01()
💕 使用 字面量 创建对象 [常用]
let a = {}
let student = {
name:"蔡徐坤",
height:"175",
weight:"170kg",
sayHello:function() {
console.log("Hello")
}
}
{ }
创建对象键值对
的形式来组织.,
分割 最后一个属性后面的 ,
可有可无:
分割.使用对象的属性和方法:
- 使用
.
成员访问运算符来访问属性.
可以理解成 “的”
console.log(student.name);- 使用 [ ] 访问属性, 此时属性需要加上
引号
console.log(student[‘height’]);- 调用方法, 别忘记加上
()
student.sayHello();
💕 使用 new Object 创建对象
let student = new Object()
student.name = "小明"
student.height = 180
student['weight'] = 75
student.SayHello = function() {
console.log("Say Hello")
}
console.log(student.name)
console.log(student['height'])
student.SayHello()
💕 使用 构造函数 创建对象
function 构造函数名(形参) {
this.属性 = 值;
this.方法 = function...
}
var obj = new 构造函数名(实参);
例如:
function People(name, height, weight) {
this.name = name
this.height = height
this.weight = weight
this.Say = function() {
console.log(name + "say hello")
}
}
let xiaoming = new People("小明", 175, 75)
let zhangsan = new People("张三", 175, 75)
console.log(xiaoming)
xiaoming.Say()
console.log(zhangsan)
zhangsan.Say()
💕 理解 new 关键字
new 的执行过程:
- 先在内存中创建一个空的对象 { }
- this 指向刚才的空对象(将上一步的对象作为 this 的上下文)
- 执行构造函数的代码, 给对象创建属性和方法
- 返回这个对象 (构造函数本身不需要 return, 由 new 代劳了)
- JavaScript 没有 “
类
” 的概念
对象其实就是 “属性” + “方法” .
类相当于把一些具有共性的对象的属性和方法单独提取了出来, 相当于一个 “月饼模子”
在 JavaScript 中的 “构造函数” 也能起到类似的效果.
而且即使不是用构造函数, 也可以随时的通过 { } 的方式指定出一些对象
在 ES6 中也引入了 class 关键字, 就能按照类似于 Java 的方式创建类和对象了- JavaScript 对象不区分 “属性” 和 “方法”
JavaScript 中的函数是 “一等公民”, 和普通的变量一样. 存储了函数的变量能够通过 ( ) 来进行调用执行- JavaScript 对象没有
private / public
等访问控制机制
对象中的属性都可以被外界随意访问- JavaScript 对象没有 “
继承
”
继承本质就是 “让两个对象建立关联”. 或者说是让一个对象能够重用另一个对象的属性/方法,JavaScript 中使用 “原型” 机制实现类似的效果- JavaScript 没有 “
多态
”
多态的本质在于 “程序猿不必关注具体的类型, 就能使用其中的某个方法”,C++ / Java 等静态类型的语言对于类型的约束和校验比较严格. 因此通过 子类继承父类, 并重写父类的方法的方式 来实现多态的效果。但是在 JavaScript 中本身就支持动态类型, 程序猿在使用对象的某个方法的时候本身也不需要对对象的类型做出明确区分. 因此并不需要在语法层面上支持多态。
- 例如,在 Java 中已经学过 ArrayList 和 LinkedList. 为了让程序猿使用方便, 往往写
List
,然后我们可以写一个方法:list = new ArrayList<>()
我们不必关注 list 是 ArrayList 还是 LinkedList, 只要是 List 就行. 因为 List 内部带有 add 方法.
当我们使用 JavaScript 的代码的时候
add 对于 list 这个参数的类型本身就没有任何限制. 只需要 list 这个对象有 add 方法即可. 就不必
像 Java 那样先继承再重写绕一个圈子
💕 class创建类
当我们使用class
关键字创建类的时候,构造函数名为关键字constructor
class People {
constructor(name, height, weight) {
this.name = name
this.height = height
this.weight = weight
}
Say() {
console.log(this.name + "正在说:Hello")
}
}
let xiaoming = new People("小明", 175, 75)
console.log(xiaoming)
xiaoming.Say()
💕 static关键字的理解
如果我们想要让类中的一些成员属于类而不是属于某个对象时,就可以使用static
关键字来修饰它。
class People {
constructor(name, height, weight) {
this.name = name
this.height = height
this.weight = weight
}
Say() {
console.log(this.name + "正在说:Hello")
}
static other = "other"
static Sum() {
return 100
}
}
let xiaoming = new People("小明", 175, 75)
alert(People.other)
alert(People.Sum())
当我们使用某个对象
去访问static修饰的成员变量或者成员方法时,就会报错。
💕 继承
继承的关键字和Java中是一样的,都是使用extends
关键字。
class People {
constructor(name, height, weight) {
this.name = name
this.height = height
this.weight = weight
}
Say() {
console.log(this.name + "正在说:Hello")
}
static other = "other"
static Sum() {
return 100
}
}
class Student extends People {
constructor(name, height, weight, number) {
super(name, height, weight)
this.number = number
}
Say() {
alert(this.name)
}
}
let student = new Student("张三", 180, 70, 1)
console.log(student)
student.Say()
尽管Java和JavaScript有相似的名称,但他们是两种不同的编程语言,它们有着不同的应用领域、语法和类型系统、执行环境以及对象模型和生态系统。JavaScript在Web开发领域有着丰富的库和框架,如React、Angular和Vue等,但与Java相比,JavaScript的生态系统更为注重于Web开发。