(一)JavaScript概述
1、发展历史
- ScriptEase.(客户端执行的语言):1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中).后将其改名ScriptEase.(客户端执行的语言)
- Javascript:Netscape(网景)接收Nombas的理念,(Brendan Eich)在其Netscape Navigator 2.0产品中开发出一套livescript的脚本语言.Sun和Netscape共同完成.后改名叫Javascript
- Jscript:微软随后模仿在其IE3.0的产品中搭载了一个JavaScript的克隆版叫Jscript.
- EcmaScript规范:为了统一三家,ECMA(欧洲计算机制造协会)定义了ECMA-262规范.国际标准化组织及国际电工委员会(ISO/IEC)也采纳 ECMAScript 作为标准(ISO/IEC-16262)。从此,Web 浏览器就开始努力(虽然有着不同的程度的成功和失败)将 ECMAScript 作为 JavaScript 实现的基础。EcmaScript是规范.
2、JavaScript组成
ECMAScript 是一个重要的标准,但它并不是 JavaScript 唯一的部分,当然,也不是唯一被标准化的部分。实际上,一个完整的 JavaScript 实现是由以下 3 个不同部分组成的:
- 核心(ECMAScript)
- 文档对象模型(DOM) Document object model (整合js,css,html)
- 浏览器对象模型(BOM) Broswer object model(整合js和浏览器)
注:Javascript 在开发中绝大多数情况是基于对象的,也是面向对象的
(二)ECMAScript
包含内容:
- 语法
- 类型
- 语句
- 关键字
- 保留字
- 运算符
- 对象 (封装 继承 多态) 基于对象的语言.使用对象.
1、JavaScript的引入方式
使用<script>标签,写在哪个位置都行,一般放在body最后,因为加载顺序从上到下,放在前面标签还没加载找不到,放最后标签已经加载了,更好找到标签
1.1 嵌入式
javascript"><script>/*这里直接编写js代码,如:alert('hello yuan')*/
</script>
1.2 导入式(推荐)
javascript"><script src="hello.js"></script> // src是js文档的路径
2、Javascript语法
2.1 变量
- js变量是弱类型,声明变量时不区分数据类型,统一用var关键字声明变量,如:var a;
- 可以声明变量的同时赋值,一行可以声明多个变量(以逗号,分隔),并且可以是不同类型,如:var name="张三", age=18, job="lecturer";
- (了解) 声明变量时 可以不用var. 如果不用var 那么它是全局变量,但推荐都使用var关键字声明变量,效率更高,也能避免一些问题。
- 还可以用let关键字声明块作用域变量(更推荐):
- 只在声明它所在的块(由
{}
包围的代码块)、语句或表达式内部有效。- 同一作用域内,不能使用
let
重复声明同一个变量名,如果尝试这样做,将会引发一个错误。- 暂时性死区:变量声明之前的作用域范围内,该变量是不可访问的,尝试在声明之前访问
let
变量会引发一个ReferenceError
。- 虽然
let
声明的变量会被提升到它们所在的作用域顶部,但是在变量声明之前的任何访问都会因为暂时性死区而失败。- 在循环中的特殊行为:
let
在循环内部声明变量时(特别是for
循环),每次迭代都会为该变量创建一个新的绑定。这意味着循环体内的每个变量都是独立的,并且具有自己的值。javascript">/*例子:let 如何提供块作用域,并且如何在不同的块中独立地声明和使用变量。*/ {let x = 10;console.log(x); // 输出 10 } console.log(x); // ReferenceError: x is not definedlet y = 20; {let y = 30; // 这是一个新的块作用域内的 yconsole.log(y); // 输出 30 } console.log(y); // 输出 20,外部的 y 没有被内部的 y 影响// 在循环中使用 let for (let i = 0; i < 3; i++) {console.log(i); // 分别输出 0, 1, 2 } console.log(i); // ReferenceError: i is not defined,因为 i 只在循环块内有效
- 变量命名,首字符只能是字母,下划线,$美元符 三选一,且区分大小写,x与X是两个变量。
- 变量还应遵守以下某条著名的命名规则:
- Camel 标记法(小驼峰式):首字母是小写的,接下来的字母都以大写字符开头。例如:var myTestValue = 0, mySecondValue = "hi";
- Pascal 标记法(大驼峰式):首字母是大写的,接下来的字母都以大写字符开头。例如:Var MyTestValue = 0, MySecondValue = "hi";
- 匈牙利类型标记法:在以 Pascal 标记法命名的变量前附加一个小写字母(或小写字母序列),说明该变量的类型。例如,i 表示整数,s 表示字符串,则:Var iMyTestValue = 0, sMySecondValue = "hi";
2.2 常量
常量 :直接在程序中出现的数据值,或使用const关键字声明的变量,不可被修改和重新赋值
2.3 基础规范
- js代码每个语句后要有语句结束标志——分号;
- 但如果一个语句写一行的话,不加分号也不会有问题,因为js会自动把换行符视为这一行结束;
- 但如果语句结束不加分号也不换行就写别的语句,那就报错了
- 注释:
- 单行注释://
- 多行注释:/* */
- 使用{}来封装代码块,如:
- function f(参数列表){函数体}
- if (条件语句){代码块}
2.4 标识符
- 标识符:
- 由不以数字开头的字母、数字、下划线(_)、美元符号($)组成
- 常用于表示函数、变量等的名称
- 例如:_abc,$abc,abc,abc123是标识符,而1abc不是
- JavaScript语言中代表特定含义的词称为保留字,不允许程序再定义为标识符
2.5 数据类型
- 基本类型(数据存储在栈内存):数字类型(Number)、字符串(String)、布尔型(Boolean)、Null、Undefined
- 引用类型(数据存储在堆内存,把指向堆内存地址的引用存储在栈内存):对象(object)、数组、函数
- 类似python的字典在js中是object类型:不同于python中的字典,js中a={name:"alex"}就相当于a={"name":"alex"},取值方法为a.name或a["name"]
(1)数字类型(Number)
- 最基本的数据类型
- 不区分整型数值和浮点型数值
- 所有数字都采用64位浮点格式存储,相当于Java和C语言中的double格式
- 能表示的最大值是±1.7976931348623157 x 10308
- 能表示的最小值是±5 x 10 -324
- 在JavaScript中10进制的整数由数字的序列组成:
- 精确表达的范围是:-9007199254740992 (-253) 到 9007199254740992 (253)
- 超出范围的整数,精确度将受影响
- 浮点数:
- 使用小数点记录数据,例如:3.4,5.6
- 使用指数记录数据,例如:4.3e23 = 4.3 x 1023
- 16进制和8进制数的表达:
- 16进制数据前面加上0x,八进制前面加0
- 16进制数是由0-9,A-F等16个字符组成
- 8进制数由0-7等8个数字组成
- 16进制和8进制与2进制的换算:
javascript"># 2进制: 1111 0011 1101 0100 <-----> 16进制:0xF3D4 <-----> 10进制:62420 # 2进制: 1 111 001 111 010 100 <-----> 8进制:0171724
(2)字符串(String)
- 是由Unicode字符、数字、标点符号组成的序列
- 字符串常量:首尾由单引号或双引号括起的字符序列
- JavaScript中没有字符类型
- 常用特殊字符在字符串中的表达:
- 字符串中部分特殊字符必须加上反斜杠\,如Unicode字符\u4f60
- 常用的转义字符 \n:换行 \':单引号 \":双引号 \\:右划线
(3)布尔型(Boolean)
- Boolean类型仅有两个值:true和false,也代表1和0,实际运算中true=1,false=0
- 布尔值也可以看作on/off、yes/no、1/0对应true/false
- Boolean值主要用于JavaScript的控制语句,例如:
javascript">if (x==1){y=y+1; }else{y=y-1; }
(4)Null & Undefined
- Null 类型:只有一个专用值 null,即它的字面量。值 undefined 实际上是从值 null 派生来的,因此 ECMAScript 把它们定义为相等的
- Undefined 类型只有一个值,即 undefined:
- 当声明的变量未初始化时,该变量的默认值是 undefined
- 当函数无明确返回值时,返回的也是值 "undefined"
- 尽管这两个值相等,但它们的含义不同:
- undefined 是声明了变量,但未对其初始化时赋予该变量的值;
- null 则用于表示尚未存在的对象,如果函数或方法要返回的是对象,那么找不到该对象时,返回的通常是 null。
- typeof null的结果是object
(5)对象object
- 在JavaScript中,对象(Object)是一种包含多个键值对(key-value pairs)的数据结构。
- 每个键值对将一个键(也称为属性名)映射到一个值。
- JavaScript对象具有动态性,可以在运行时添加、修改或删除属性,而不需要预先定义对象的结构,这有助于JavaScript对象处理复杂和多变的数据。
- 对象是JavaScript编程中的核心概念之一,广泛用于存储和管理复杂的数据。
javascript">let person = {firstName: "John",lastName: "Doe",age: 30,isStudent: false,courses: ["Math", "Science", "History"],address: {street: "123 Main St",city: "Anytown",state: "Anystate",zipCode: "12345"},greet: function() {console.log("Hello, my name is " + this.firstName + " " + this.lastName);} }; /*示例中,person 对象包含了多个属性:firstName 和 lastName 是字符串类型的属性,分别存储人的名和姓。 age 是数字类型的属性,存储人的年龄。 isStudent 是布尔类型的属性,表示该人是否为学生。 courses 是数组类型的属性,存储该人参加的课程列表。 address 是另一个对象类型的属性,包含街道、城市、州和邮政编码等子属性。 greet 是一个方法(函数),当调用时,会打印出一个问候消息。 你可以通过以下方式访问和操作对象的属性: */// 访问属性 console.log(person.firstName); // 输出 "John" console.log(person["lastName"]); // 输出 "Doe" console.log(person.address.city); // 输出 "Anytown"// 修改属性 person.age = 31; console.log(person.age); // 输出 31// 调用方法 person.greet(); // 输出 "Hello, my name is John Doe"// 添加新属性 person.email = "john.doe@example.com"; console.log(person.email); // 输出 "john.doe@example.com"// 删除属性 delete person.isStudent; console.log(person.isStudent); // 输出 undefined,因为属性已被删除
(5.1)创建对象:对象字面量
最简单和最常用的方法,只需用花括号
{}
包围一组键值对即可javascript">let obj = {key1: "value1",key2: "value2",key3: 3,key4: true,key5: function() {console.log("This is a method!");} };
(5.2)创建对象:Object
构造函数
使用
new Object()
来创建一个空对象,然后添加属性。
javascript">let obj = new Object();
obj.key1 = "value1";
obj.key2 = "value2";
// ...
(5.3)创建对象:Object.create()
方法
创建一个拥有指定原型和属性的对象,其中第一个参数是现有对象的原型,还可以传递一个描述符对象来添加或修改新对象的属性。
javascript">let proto = {greet: function() {console.log("Hello!");} }; let obj = Object.create(proto); obj.key1 = "value1"; // ...
(5.4)创建对象:自定义构造函数
自定义一个构造函数,然后使用
new
关键字来创建对象实例javascript">function Person(name, age) {this.name = name;this.age = age;this.sayHello = function() {console.log("Hello, my name is " + this.name);}; }let person1 = new Person("John", 30); let person2 = new Person("Jane", 25);
(5.5)创建对象:class
关键字(ES6引入)
定义一个类,然后使用
new
关键字来创建类的实例。这是ES6及更高版本中推荐的方式,因为它提供了更清晰和更结构化的方式来定义对象和其行为。javascript">class Person {constructor(name, age) {this.name = name;this.age = age;}sayHello() {console.log("Hello, my name is " + this.name);} }let person1 = new Person("John", 30); let person2 = new Person("Jane", 25);
(5.6)创建对象:工厂函数
不使用
new
关键字就能创建对象的方法,通常返回一个包含所需属性和方法的普通对象。javascript">function createPerson(name, age) {return {name: name,age: age,sayHello: function() {console.log("Hello, my name is " + this.name);}}; }let person1 = createPerson("John", 30); let person2 = createPerson("Jane", 25);
(5.7)注意事项
- 每种方法都有其适用的场景和优缺点。
- 通常,对于简单的对象,使用对象字面量是最方便的。
- 对于需要多个实例的对象,使用构造函数或类可能更合适。
- 工厂函数则提供了一种更加灵活和不需要
this
绑定的创建对象的方式。
2.6 数据类型转换
- JavaScript属于松散类型的程序语言:
- 变量在声明的时候并不需要指定数据类型
- 变量只有在赋值的时候才会确定数据类型
- 表达式中包含不同类型数据则在计算过程中会强制进行类别转换:
数字 + 字符串:数字转换为字符串
数字 + 布尔值:true转换为1,false转换为0
字符串 + 布尔值:布尔值转换为字符串true或false
2.7 强制类型转换函数
- 函数parseInt:强制转换成整数,例如:
- parseInt("6.12")=6 ;
- parseInt(“12a")=12 ;
- parseInt(“a12")=NaN ; // NAN:意思是not a number,当强转数字时遇到不属于数字的字符时,会转换数字失败,如果前面有数字,截取前面的数字为结果,否则哪怕后面再有数字,值为NAN,属于Number类型
- parseInt(“1a2")=1
- 函数parseFloat:强制转换成浮点数,例如:parseFloat("6.12")=6.12
- 函数eval:将字符串强制转换为表达式并返回结果,例如:
- eval("1+1")=2 ;
- eval("1<2")=true
2.8 类型查询函数(typeof)
- ECMAScript 提供了 typeof 运算符来判断一个值是否在某种类型的范围内,(string / number / boolean / object )。
- 可以用这种运算符判断一个值是否表示一种原始类型:如果它是原始类型,还可以判断它表示哪种原始类型。
- 例如:
- typeof("test"+3) // "string";
- typeof(null) // "object ";
- typeof(true+1) // "number";
- typeof(true-false) // "number"
2.9 ECMAScript 运算符
(1)ECMAScript 算数运算符
加(+)、 减(-)、 乘(*) 、除(/) 、余数(% ) 加、减、乘、除、余数和数学中的运算方法一样 例如:9/2=4.5,4*5=20,9%2=1
-除了可以表示减号还可以表示负号 例如:x=-y
+除了可以表示加法运算还可以用于字符串的连接 例如:"abc"+"def"="abcdef"
递增(++) 、递减(--):
假如x=2,那么x++表达式执行后的值为3,x--表达式执行后的值为1
i++相当于i=i+1,i--相当于i=i-1
递增和递减运算符可以放在变量前也可以放在变量后:
i++,++在后,先取后加,即先把i本来的值作为表达式的值,i的值再自增1,如i=1, i++的表达式值=1,计算后i=2;
++i,++在前,先加后取,即i先自增1,再作为表达式的值,如上文的i,++i的表达式值=3,计算后i=3;
i--,--在后,先取后减,即先把i本来的值作为表达式的值,i的值再自减1,如上文的i,i--的表达式值=3,计算后i=2;
--i,--在前,先减后取,即i先自减1,再作为表达式的值,如上文的i, --i的表达式值=1计算后i=1
- 一元加减法:
- -a:负号,把a变成负值;
- +a:对a做类型转换;
- a="yuan";a=+a;alert(a); // 结果显示a的值为NaN,属于Number类型的一个特殊值,当遇到将字符串转成数字无效时,就会得到一个NaN数据
- NaN特点: NaN参与的所有的运算都是false,除了!=是true外,如:
- var n=NaN;
- alert(n>3); // false
- alert(n<3); // false
- alert(n==3); // false
- alert(n==NaN); // false
- alert(n!=NaN); // true
(2)ECMAScript 逻辑运算符
与 (&&) 、或(||) 、非(!):
- 与(&&)运算符:全真即真
- 运算数可以是任何类型的,不止是 Boolean 值。
- 如果某个运算数不是原始的 Boolean 型值,&&运算并不一定返回 Boolean 值:
- 如果某个运算数是null,返回 null;
- 如果某个运算数是NaN,返回 NaN;
- 如果某个运算数是undefined,返回undefined;
- 如果一个运算数是对象,另一个是boolean值,返回该对象;(除了Boolean、null、NaN和undefined都是对象)
- 如果两个运算数都是对象,返回第二个对象。
- 或(||)运算符:全假即假,但与逻辑 AND 运算符相似,如果某个运算数不是 Boolean 值,逻辑或 运算并不一定返回 Boolean 值
- 非(!)运算符:真即假,假即真
(3)ECMAScript位运算符
- 按位与&:二进制形式分别按照每一个位对应做与运算,都是1得1,否则得0,如:
1&2=0
0001
0010
————
0000- 按位或|:二进制形式分别按照每一个位对应做或运算,都是0得0,否则得1,如:
1|2=3
0001
0010
————
0011- 按位异或^:二进制形式分别按照每一个位对应做异或运算,只要位不同结果为1,否则结果为0,如:
1^2=3
0001
0010
————
0011- 左移<<:二进制形式每个位数字左移多少位,后面补0,如:
3<<2=12
0011
————
1100- 右移>>:二进制形式每个位数字右移多少位,前面补0,如:
12>>1=6
1100
————
0110- 取反(NOT)~:二进制形式每个位数字1变成0,0变成1,如;
~6=9
0110
————
1001
(4)ECMAScript 赋值运算符
赋值运算符=:JavaScript中一个等号=代表赋值,两个等号==表示判断是否相等(与c、c++、java、python中类似)
- 配合其他运算符形成的简化表达式,例如:i+=1相当于i=i+1,x&=y相当于x=x&y
(5)ECMAScript等性运算符
- 执行类型转换的规则如下:
- 如果一个运算数是 Boolean 值,在检查相等性之前,把它转换成数字值。false 转换成 0,true 为 1。
- 如果一个运算数是字符串,另一个是数字,在检查相等性之前,要尝试把字符串转换成数字。
- 如果一个运算数是对象,另一个是字符串,在检查相等性之前,要尝试把对象转换成字符串。
- 如果一个运算数是对象,另一个是数字,在检查相等性之前,要尝试把对象转换成数字。
- 在比较时,该运算符还遵守下列规则:
- 值 null 和 undefined 相等。
- 在检查相等性时,不能把 null 和 undefined 转换成其他值。
- 如果某个运算数是 NaN,等号将返回 false,非等号将返回 true。
- 如果两个运算数都是对象,那么比较的是它们的引用值。
- 如果两个运算数指向同一对象,那么等号返回 true,否则两个运算数不等。
(6)ECMAScript 关系运算符(重要)
等于 ( == ) 、全等(===)、不等于( != ) 、不全等(!==)、 大于( > ) 、 小于( < ) 大于等于(>=) 、小于等于(<=),比较规则如下:
- 比较运算符两侧如果一个是数字类型,一个是其他类型,会将其类型转换成数字类型,转换失败就是NaN
- 比较运算符两侧如果都是字符串类型,比较的是最高位的ascII码,如果最高位相等,继续取第二位比较.
- 全等(===)和不全等(!==)不会做类型转换,相当于其他语言如python的==和!=
(7)Boolean运算符(重要)
经常用于条件判断、控制流语句(如
if
语句)以及逻辑运算中,以帮助程序根据条件执行不同的代码块,包括:
- 逻辑运算符与(&&)、或(||)、非(!)
- 空值合并运算符(
??
)(ES2020引入):
- 当左侧的操作数为
null
或undefined
时,返回右侧的操作数;- 否则,返回左侧的操作数。
- 可选链运算符(?.)(ES2020引入):
- 用于安全地访问对象的深层属性;
- 当访问的属性不存在时,不会抛出错误,而是返回
undefined
。注:if的条件语句中[];0; null; undefined;object(new Object()创建的对象);都会判断为false
javascript">var temp=new Object();// false;[];0; null; undefined;object(new Object();)
if(temp){console.log("yuan")
}else {console.log("alex")
}
// 不论temp值是多少,只根据false和true判断走哪个分支语句let x = null;
let y = "default";
console.log(x ?? y); // "default"let z = 0;
console.log(z ?? y); // 0,因为0不是null或undefinedlet user = { name: "John", address: { city: "New York" } };
console.log(user?.address?.city); // "New York"
console.log(user?.profile?.age); // undefined,因为profile不存在
2.10 控制语句
(1)if条件控制语句
格式类似c语言的if:
if (表达式1){
语句块1;
}else if (表达式2){
语句块2;
}else{
语句块3;
} // 表达式的值为true则执行相应的{}里的语句块,if 可以单独使用
javascript">var x= (new Date()).getDay();
//获取今天的星期值,0为星期天
var y;if ( (x==6) || (x==0) ) {
y="周末";
}else{
y="工作日";
}alert(y);//等价于y="工作日";
if ( (x==6) || (x==0) ) {
y="周末";
}
(2)switch选择控制语句
格式如下:(类似c语言中的switch语句,python中没有switch语句)
switch (表达式) {
case 值1:语句1;break;
case 值2:语句2;break;
case 值3:语句3;break;
default:语句4;
} // 因为只判断一个条件,执行一个表达式,将表达式的值与每个case的值比较,进而选择从哪一个case的语句块开始执行(break跳出),效率比if-else多分支结构更高,同时结构更加简洁清晰,使程序可读性更强,但只能对基本类型进行数值比较,而if 语句适用范围比较广,只要是 boolean 表达式都可以用 if 判断
(3)for循环控制语句
- 格式如下:(类似c语言的for循环语句,与python的for循环不一样)
for (初始化;条件;增量){
语句块;
}- 功能说明:实现条件循环,当条件成立时,执行语句块,否则跳出循环体
- (初始化;条件;增量) 也可以写成(var i in arr),arr是数组,i得到的是索引下标,但这种方式渲染html时有bug,不推荐使用:循环的是你获取的一个DOM元素集,for in用来循环对象的所有属性,dom元素集包含了你上面输出的属性。
javascript">for (var i=1;i<=7;i++){document.write("<H"+i+">hello</H "+i+"> ");document.write("<br>");
}
----------------------------------------------
var arr=[1,"hello",true]//var dic={"1":"111"}
for (var i in arr){console.log(i)console.log(arr[i])
}
----------------------------------------------
doms=document.getElementsByTagName("p");for (var i in doms){console.log(i); // 0 1 2 length item namedItem//console.log(doms[i])
}//循环的是你获取的一个DOM元素集,for in用来循环对象的所有属性,dom元素集包含了你上面输出的属性。
//如果你只要循环dom对象的话,可以用for循环:for (var i=0;i<doms.length;i++){console.log(i) ; // 0 1 2//console.log(doms[i])}
(4)while循环控制语句
格式如下:
while (条件){
语句1;
...
} // 功能说明:运行功能和for类似,当条件成立循环执行语句花括号{}内的语句,否则跳出循环
2.11 异常处理
- 格式如下:
try {
//这段代码从上往下运行,其中任何一个语句抛出异常该代码块就结束运行
}
catch (e) {
// 如果try代码块中抛出了异常,catch代码块中的代码就会被执行。
//e是一个局部变量,用来指向Error对象或者其他抛出的对象
}
finally {
//无论try中代码是否有异常抛出(甚至是try代码块中有return语句),finally代码块中始终会被执行。
}- 注:主动抛出异常 throw Error('xxxx')
2.12 ECMAScript类
- 从ECMAScript 2015(也称为ES6)开始,ECMAScript引入了类的概念。类是通过
class
关键字来定义的,并且支持继承。这是JavaScript语言的一个重要更新,使得面向对象编程更加直观和方便。现代浏览器如Chrome、Firefox、Safari和Edge都已经广泛支持ES6的新特性。IE7到IE11等旧版浏览器则基本不支持ES6。要查看浏览器对ES6特性的具体支持情况,可以参考兼容性表,如Kangax的ECMAScript 6 Compatibility Table。
此外,Node.js也逐步增加了对ES6特性的支持,从较新的版本开始,许多ES6特性已经可以在Node.js环境中直接使用。
对于不支持ES6的环境,开发者可以使用Babel等转译工具将ES6代码转换为向后兼容的JavaScript代码。
javascript">class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}
}class Dog extends Animal {speak() {console.log(`${this.name} barks.`);}
}let dog = new Dog('Buddy');
dog.speak(); // 输出: Buddy barks./*例子中,定义了一个基类 Animal 和一个继承自 Animal 的子类 Dog。
每个类都有一个构造函数(constructor),用于初始化对象的状态。
Dog 类中还重写了Animal的speak 方法,以提供特定于狗的行为。ECMAScript的类是基于原型的,这意味着它们实际上是函数的语法糖。
每个类都有一个与之关联的原型对象,该对象包含类的所有方法。
当你创建类的实例时,该实例实际上是从类的原型对象上继承而来的。尽管ECMAScript的类提供了类似于传统面向对象编程语言的语法和特性,
但它们仍然是基于JavaScript的原型继承机制实现的。
这意味着你可以使用所有与原型相关的特性和操作
(如 Object.create() 和 instanceof 运算符)与类一起工作。*/
(1)定义类
- 使用class关键字(ES6引入)定义类,作为面向对象编程(OOP)在JavaScript中的一个更直观和易于理解的实现
- 构造函数(Constructor):
- 一个特殊的方法,用于在创建类的实例时初始化对象的属性,类似python类的__init__方法。
- 构造函数使用
constructor
关键字定义,并且会在你使用new
关键字创建类的实例时自动调用。- 属性(Properties):
- 类的属性通常是在构造函数中定义的;
- 也可以在类体内部直接定义;
- 属性可以是任何JavaScript数据类型,包括原始值、对象、数组、函数等。
- 方法(Methods):
- 在类体内部定义的函数;
- 可以访问和修改类的属性,并提供类的行为。
- 静态方法(Static Methods):
- 静态方法是属于类本身的,而不是属于类的实例的;
- 它们使用
static
关键字定义;- 可以通过类名直接调用,而不是通过类的实例。
- getter 和 setter:
- 控制对类属性的访问和修改。
getter
方法让你能够定义一个方法来获取一个属性的值;setter
方法让你能够定义一个方法来设置一个属性的值。- 计算属性名(Computed Property Names):在类体内部,可以使用计算属性名来动态地定义属性。
javascript">class Person {// 构造函数constructor(name, age) {this.name = name; // 定义name属性this.age = age; // 定义age属性}// 方法sayHello() {console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);}// 静态方法static greet() {console.log("Hello from the Person class!");}// getterget ageInDogYears() {return this.age * 7;}// setterset age(newAge) {if (newAge > 0) {this._age = newAge; // 通常,我们会在内部使用一个不同的属性名来存储实际的值} else {console.log("Age must be positive!");}}// 计算属性名(这里只是作为示例,通常不会在构造函数之外使用):ml-search[`ageIn${this.unit}`] {return this.age * (this.unit === 'dogYears' ? 7 : 1);}
}// 创建类的实例
let john = new Person("John", 30);// 调用实例的方法
john.sayHello(); // 输出: Hello, my name is John and I am 30 years old.// 访问getter
console.log(john.ageInDogYears); // 输出: 210// 调用静态方法
Person.greet(); // 输出: Hello from the Person class!// 使用setter设置属性
john.age = 31;
console.log(john.age); // 输出: 31 (注意:这里的输出是基于我们假设setter正确设置了_age属性)// 尝试设置一个无效的年龄
john.age = -5; // 输出: Age must be positive!/*例子中,Person 类有一个构造函数,
它接受 name 和 age 参数,并将它们分别赋值给实例的 name 和 age 属性。
类还有一个 sayHello 方法,用于输出一个问候语。
greet 是一个静态方法,它可以直接通过类名调用。
ageInDogYears 是一个 getter,它计算并返回人的年龄相当于狗的年龄(假设狗的年龄是人的年龄的7倍)。
age 的 setter 方法允许我们设置人的年龄,但会检查年龄是否为正数。
最后展示了如何使用计算属性名来定义一个动态的方法,
尽管在这个特定的例子中,它并不是很有用。*/
(2)实例化
javascript">// 创建类的实例
let john = new Person("John", 30);
2.13 ECMAScript对象
- ES6之前ECMAScript 并不真正具有类,ECMAScript 定义了“对象定义”,逻辑上等价于其他程序设计语言中的类。
- 对象(Object)是一种无序的集合,由键值对(key-value pairs)组成,用于存储和组织数据。
- 键(key)通常是字符串,但也可以是符号(Symbol),而值(value)则可以是任何JavaScript数据类型,包括原始值(如数字、字符串、布尔值)、对象、数组、函数等。
- 由ECMAScript定义的本地对象.独立于宿主环境的 ECMAScript 实现提供的对象.(native object)
- ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现.这意味着开发者不必明确实例化内置对象,它已被实例化了。ECMA-262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。(built-in object)
- 所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。所有 BOM 和 DOM 对象都是宿主对象。
(1)创建对象
- ES6之前,传统的创建对象的方法查看上面2.4(5)节
- ES6引入了类(
class
)语法,通过类实例化得到对象,提供了更清晰的面向对象编程风格
(2)访问和设置属性
- 使用点符号:
objectName.propertyName
- 使用方括号语法:
objectName["propertyName"]
,这在属性名是一个变量或包含特殊字符时特别有用。
(3)删除属性
使用
delete
操作符,如:delete objectName.propertyName
(4)遍历对象
- 使用
for...in
循环来遍历对象原型链上的所有可枚举属性。- 使用
Object.keys(obj)
来获取对象自身的所有可枚举属性名,并使用循环来遍历它们。使用Object.values(obj)
返回一个包含给定对象自身所有可枚举属性值的数组。- 使用
Object.entries(obj)
来获取对象自身的所有可枚举属性的键值对数组,并使用循环来遍历它们。javascript">const obj = { name: 'Alice', age: 25, city: 'New York' };const keys = Object.keys(obj); console.log(keys); // 输出: ['name', 'age', 'city']const values = Object.values(obj); console.log(values); // 输出: ['Alice', 25, 'New York']const entries = Object.entries(obj); console.log(entries); // 输出: [['name', 'Alice'], ['age', 25], ['city', 'New York']]entries.forEach(([key, value]) => {console.log(`${key}: ${value}`); }); // 输出: // name: Alice // age: 25 // city: New York
(5)对象方法
对象可以包含方法(即作为对象属性的函数),这些方法可以访问和修改对象的属性
(6)this关键字
在对象的方法内部,
this
关键字引用该方法所属的对象
(7)对象的内置方法
JavaScript提供了一些内置的对象方法,如
Object.create()
、Object.defineProperty()
、Object.freeze()
等,用于创建对象、定义属性、冻结对象等
(8)示例
javascript">// 创建一个对象
let person = {name: "John",age: 30,greet: function() {console.log("Hello, my name is " + this.name);}
};// 访问对象的属性
console.log(person.name); // 输出 "John"
console.log(person["age"]); // 输出 30// 调用对象的方法
person.greet(); // 输出 "Hello, my name is John"// 设置对象的属性
person.age = 31;
console.log(person.age); // 输出 31// 删除对象的属性
delete person.age;
console.log(person.age); // 输出 undefined,因为属性已经被删除// 遍历对象的属性
for (let key in person) {console.log(key + ": " + person[key]);
}
// 输出:
// name: John
// greet: function () { ... }
/*
例子中,创建了一个名为person的对象,它包含name和age属性以及一个greet方法。
我们演示了如何访问和设置对象的属性,如何调用对象的方法,以及如何删除对象的属性。
最后,我们还展示了如何使用for...in循环来遍历对象的属性*/
(9)高级用法
涵盖了多种技术和模式,更有效地利用对象的特性,提升代码的可读性、可维护性和重用性,常见高级用法如下:
对象字面量增强:
- 在ES6及更高版本中,对象字面量得到了增强,支持属性名的简写、方法的简写以及计算属性名。
- 例如,你可以直接在对象字面量中定义方法,而无需使用
function
关键字。对象解构:
- 解构赋值允许你从数组或对象中提取数据,并将其赋值给变量。
- 对于对象,你可以使用花括号
{}
来解构,并指定要提取的属性名。- 你还可以使用默认值来处理不存在的属性。
对象的扩展运算符:
- 扩展运算符
...
可以用于对象,以复制对象的属性到另一个对象中。- 这在合并对象、创建对象副本或向对象添加新属性时非常有用。
对象的保护:
Object.freeze()
方法可以冻结一个对象,阻止修改其现有属性,阻止添加新属性,以及阻止删除已有属性。(数据属性变为不可写,访问器属性变为不可配置)- 冻结的对象在浅比较时是相等的,因为它们的内容不会改变。
Object.seal()
方法可以封闭一个对象,防止新属性的添加,但允许修改现有属性的值。代理(Proxy)和反射(Reflect):
- 代理允许你创建一个对象的代理来定义其基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。
- 反射是一个内置的对象,它提供拦截JavaScript底层操作的方法。
- 代理和反射通常一起使用,以实现高级的功能,如数据绑定、数据验证等。
对象的封装和私有字段:
- 在类(class)中,你可以使用私有字段(在字段名前加
#
)来封装数据,使其只能在类的内部访问。- 这有助于隐藏实现细节,并提供一个更清晰的公共API。
混合(Mixins):
- 混合是一种将多个对象的行为合并到一个对象中的技术。
- 它允许你重用代码,通过组合而不是继承来实现对象的多样化行为。
对象的迭代器:</