十二、类和对象进阶2
1.自执行方法
定义一个自执行函数,函数定义完成后,自己执行一次,函数名可以省略,因为没有任何意义
注意:要以分号结束,否则可能会影响后面的语句。
(function sayHello() {
console.log('sayHello');
})();
自执行函数的简写形式
+ function sayYes() {
console.log('sayYes');
}();
自执行函数,也可以直接定义成箭头函数
(()=>{
console.log('aaa');
})()
<!DOCTYPE html>
<html>
<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>自执行函数</title>
</head>
<body>
<script>
function fun(){
console.log('Hello');
}
fun();
//自执行函数
// 注意:要以分号结束,否则可能会影响后面的语句。
(function fun2(){
console.log('你好');
})();
//+号是自执行函数的简写
+function fun3(){
console.log('你好,中国');
}();
(()=>{
console.log('你好,迪丽热巴');
})();
</script>
</body>
</html>
2.rest参数
// ...args 就是rest参数
function fun1(a,b,c,...args){
console.log(a,b,c);
// arguments 是一个类数组对象,结构长得像数组,其实是一个object对象
console.log(arguments);
// rest参数 是一个数组对象,既然是数组,就可以直接使用数组的方法。
console.log(args);
}
<!DOCTYPE html>
<html>
<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>rest参数</title>
</head>
<body>
<script>
// 方法的形参前面添加...,就是方法的rest参数
//...xxx 是函数的rest参数,用于接收剩余的实参,注意:通常情况下rest参数放在最后面
//rest参数,解决了函数中arguments对象不是数组类型的缺陷
function fun1(a,b,c,...args){
console.log(a,b,c);
console.log(args);
let arr = args.map(r=>r*2)
console.log(arr);
// console.log(arguments);
}
fun1(100,200,300)
fun1(100)
fun1(10,20,30,40,50,60,70,80)
console.log('------------------------');
// 在定义函数时,可以给参数设置默认值
function fun2(a,b=200,c=300){
console.log(a,b,c);
}
fun2(1,2,3)
fun2(1,2)
fun2(1)
fun2()
</script>
</body>
</html>
3.展开运算符
展开运算符就是...,可以将一个数组全部展开
let arr3 = [...arr1,...arr2]
展开运算符,可以展开对象的全部成员,也可以将一个对象的成员,克隆给另一个对象
let lh2 = {...lh}
展开运算符,可以将多个对象的成员,合并到一个大的对象中,后面对象中的成员,如果跟前面对象中的成员同名,会覆盖前面的
let lxt = {...lh,...gxt}
<!DOCTYPE html>
<html>
<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>展开运算符</title>
</head>
<body>
<script>
let arr1 = [11,22,33]
let arr2 = [44,55,66]
let arr3 = arr1.concat(arr2)
console.log(arr3);
// ...在这里就是展开运算符,在这里,展开运算符用于展开数组中的所有成员。
let arr4 = [...arr1,...arr2]
console.log(arr4);
console.log('---------------------------');
let obj1 = {
a:100,
b:200
}
let obj2 = {
c:300,
d:400,
a:500
}
// ...在这里,用于将对象的所有属性展开,并返回一个全新的对象
let obj3 = {...obj1,...obj2}
console.log(obj3);
</script>
</body>
</html>
4.解构赋值
ES6中的解构赋值语句,可以直接将数组中的每个元素提取出来
方式是:let [变量名1,变量名2,...] = 数组
ES6中的解构赋值语句,可以直接将对象中的每个元素提取出来
方式是:let {name,age,gender,job} = obj
通常情况下,对象的属性名称叫什么,就定义什么名称的变量去接,如果出现了同名,可以修改名称
方式是:let {name,age:age1,gender,job} = obj
在ES6中,定义对象时,属性的左右两边的表达式相同时,可以省略右边的表达式,该对象在定义的时候,会自动往父级作用域寻找同名属性名对应的值
<!DOCTYPE html>
<html>
<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>解构赋值</title>
</head>
<body>
<script>
//定义变量
let no = 1001
let name = '小明'
let age = 30
let sex = '男'
//定义对象
let stu1 = {
//属性名:属性值
//这里的属性值是上面定义的变量保存的值
no:no,
name:name,
age:age,
sex:sex
}
console.log(stu1);
console.log('-----------------');
//对象的属性名和属性值的标识相同时,可以省略属性值
let stu2 = {
// 是no:no的简写
no,
name,
age,
sex
}
console.log(stu2);
console.log('--------------------------------------');
let stu3 = {
username:'小明',
userage:30,
usersex:'男',
car:{
carName:'奔驰',
carPrice:'100W'
}
}
//过去我们这样写
// let username = stu3.username
// let userage = stu3.userage
// let usersex = stu3.usersex
//现在我们这样写(解构赋值)
// usersex:usersex2 表示在解构的时候对变量名进行重命名
let {username,userage,usersex:usersex2} = stu3
console.log(username,userage,usersex2);
console.log('-----------');
// let {car} = stu3
// let {carName,carPrice} = car
let {car:{carName,carPrice}} = stu3 //这一行代码,最终会编译成下面的两行代码
// let carName = stu3.car.carName
// let carPrice = stu3.car.carPrice
console.log(carName,carPrice);
let arr = [11,22,33,44,55]
// 解构数组中的元素,采用的[]
let [a,b] = arr
console.log(a,b);
</script>
</body>
</html>
5.值类型和引用类型
在js中,number,string,boolean,都是值类型,值类型的变量,直接将数据保存到内存的栈空间中。值类型的变量,在传递时,传的是副本。
在js中,对象,数组,都是引用类型,引用类型的变量,将数据保存在堆中,然后将堆的地址保存到栈中。
<!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>值类型和引用类型</title>
</head>
<body>
<script>
function upateNum(num2){
console.log('num2='+num2);
num2+=5
console.log('num2='+num2);
}
let num1 = 5 //number类型
// 值类型在调用方法传递是,传的是值
upateNum(num1)
console.log('num1='+num1);//可以看出num1的值没变
console.log('-------------------------');
function updateArr(arr2){
console.log('arr2',arr2);//[11,22,33,44,55]
arr2.push(66)//[11,22,33,44,55,66]
console.log('arr2',arr2);//[11,22,33,44,55,66]
}
//数组是引用类型
let arr1 = [11,22,33,44,55]
updateArr(arr1)
console.log('arr1',arr1);//此时arr1与arr2的值都变了=>[11,22,33,44,55,66]
</script>
</body>
</html>
6.原型对象
prototype属性是类的原型对象。通常情况下,我们习惯将类的方法,定义到类的原型对象中,这样做的好处是,提高代码的利用率,不会开辟多余的内存空间。
__proto__属性是对象的原型对象,注意:同种类型多个对象上的原型对象 共同指向类型上的原型对象。类的原型对象上面的方法,类的对象,可以直接调用
<!DOCTYPE html>
<html>
<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>原型对象</title>
</head>
<body>
<script>
// 构造函数(类)有原型对象,其实就是构造函数身上的一个自带属性,这个属性是:prototype
// 对象也有原型对象,其实就是对象身上的一个自带属性,这个属性是:__proto__
// 所有同类型的对象身上的原型对象属性,都指向类的原型对象属性。
// 类和对象的原型对象身上挂的方法,对象可以直接使用,不需要经过原型对象。
function Student(name,age,sex){
this.name = name
this.age = age
this.sex = sex
// 如果将方法直接定义在类里面,将来根据这个类创建的每个对象,都要创建自己独立的这些方法
// 如果要创建很多对象,对内存的开销会很大。
/* this.sayHi = function(){
console.log(`Hi!我叫${this.name},今年${this.age}岁,性别是${this.sex}`);
}
this.study = function(time){
console.log(`Hi!我叫${this.name},我每天学习${time}小时`);
}
this.play = function(time){
console.log(`Hi!我叫${this.name},我每天玩${time}小时`);
} */
}
// 我们可以将类的方法,添加到类的原型对象身上
Student.prototype.sayHi = function(){
console.log(`Hi!我叫${this.name},今年${this.age}岁,性别是${this.sex}`);
}
Student.prototype.study = function(time){
console.log(`Hi!我叫${this.name},我每天学习${time}小时`);
}
Student.prototype.play = function(time){
console.log(`Hi!我叫${this.name},我每天玩${time}小时`);
}
let s1 = new Student('张三',20,'男')
let s2 = new Student('李四',22,'女')
let s3 = new Student('王五',24,'男')
// 查看Student类的原型对象
console.log(Student.prototype);
// 查看三个对象的原型对象,一模一样。
console.log(s1.__proto__);
console.log(s2.__proto__);
console.log(s3.__proto__);
s1.sayHi()
s1.study(8)
s1.play(3)
console.log('------------------');
s2.sayHi()
s2.study(6)
s2.play(6)
console.log('------------------');
s3.sayHi()
s3.study(10)
s3.play(1)
</script>
</body>
</html>
7.ES6中定义类的新语法
// 定义一个Person类型
class Person{
// 构造函数
constructor(name,age,gender){
this.name = name,
this.age = age
this.gender = gender
}
// 给类添加一个方法
sayHi = function(){
console.log(this.name,this.age,this.gender);
}
//用这种方式定义的方法,是将方法定义的类的原型对象中去
sayHello(){
console.log('hello!');
}
}
8.继承
<!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>Document</title>
</head>
<body>
<script>
//ES5:
// 定义一个人类
function Person1(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
Person1.prototype.eat = function () {
console.log(`我叫${this.name},我在吃饭...`);
}
Person1.prototype.say = function () {
console.log(`大家好!我叫${this.name} 今年${this.age}岁 性别是${this.gender}`);
}
// 通过Person类型,创建出了两个对象
let p1 = new Person1('张三', 20, '男')
p1.say()
p1.eat()
let p2 = new Person1('李四', 21, '男')
p2.say()
p2.eat()
console.log('-------------------------------------');
//定义了学生类
function Student1(name, age, gender, no) {
// 继承Person类的属性
Person1.call(this, name, age, gender)
// Student类特有的属性
this.no = no
}
// 给Student类的prototype属性 new一个Person类型的对象
// 用于继承Person类的方法
Student1.prototype = new Person1()
Student1.prototype.study = function () {
console.log(`我叫${this.name},我的学号是${this.no},我在学习...`);
}
let s1 = new Student1('王五', 20, '女', '1001')
s1.study()
s1.eat()
s1.say()
console.log("******************************************************");
//ES6:
// 定义人类
class Person2 {
// 定义构造函数
constructor(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
// 说话方法
say() {
console.log(`大家好!我叫${this.name} 今年${this.age}岁 性别是${this.gender}`);
}
// 吃方法
eat() {
console.log(`我叫${this.name},我在吃饭...`);
}
}
// 每个类型都一个prototype属性,我们称它为类的原型对象。
// 类的原型对象上面的成员,给类的所有实例(实例就是类创建出来的对象)共享。
console.log(Person2.prototype);
// 通过Person类型,创建出了两个对象
let p3 = new Person2('张三', 20, '男')
console.log(p3);
p3.say()
p3.eat()
let p4 = new Person2('李四', 21, '男')
console.log(p4);
p4.say()
p4.eat()
console.log('-------------------------------------');
// extends关键字,表示继承
class Student2 extends Person2 {
// 构造函数
constructor(name, age, gender, no) {
// 调用父类的构造函数
super(name, age, gender)
// 学生特有的属性
this.no = no
}
//学生学习的方法
study() {
console.log(`我叫${this.name},我的学号是${this.no},我在学习...`);
}
}
let s2 = new Student2('王五', 20, '女', '1001')
console.log(s2);
s2.study()
s2.eat()
s2.say()
</script>
</body>
</html>