面向对象
(1)定义:
①面向过程变成POP:面向过程就是分析出解决问题需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个依次调用。
②面向对象变成OOP:把事物分解成一个个对象,然后由对象之间分工合作。
(2)对比:
①面向过程:优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机采用面向过程编程。缺点:没有面向对象易维护、易复用、易扩展。
②面向对象:优点:易维护、易复用、易扩展,由于三特性可以设计出低耦合性的系统,使系统更灵活。缺点:性能比面向过程低。
(3)面向对象特点:面向对象编程具有灵活、代码可复用、容易维护和开发的优点,适合多人合作的大型项目。具有封装性、继承性和多态性。
(4)面向对象的思维特点:①抽取对象共用的属性和行为封装成一个类;②对类实例化,获取类的对象。
(5)对象:在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象(字符串、数值、数组、函数),由属性和方法组成。
类
(1)类的定义:可以使用class关键字声明一个类,之后以这个类来实例化对象。
(2)利用类创建对象:通过class关键字创建类,类名习惯性首字母大写;类里面有constructor函数,可以接受传递过来的参数,同时返回实例对象。
// constructor方法是类的构造方法(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor()
// 创建类star
class Star {
// 类的共有属性放到constructor里面
constructor(uname) {
this.uname = uname;
this.age = age;
}
sing(song) {
console.log(this.uname+song);
}
}
// 利用类创建对象
var xz = new Star('肖战',18);
console.log(xz);
xz.sing('满足');
// 过程分析:在new创建对象时自动调用constructor构造函数,将对象传入的参数'肖战'传给形参uname,然后uname指向this.uname,此时this指向创建的实例。故这个时候实例中就有uname属性了。
(3)类的继承:子类可以继承父类的一些属性和方法。
// 定义一个Father类
class Father {
constructor() {
}
money() {
console.log('jicheng')
}
}
// Son类继承Father类
class Son extends Father {
}
var son = new Son();
son.money();
(4)super关键字
①super关键字用于访问和调用父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数。
②super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。
// 1. 当子类调用父类构造函数并传递参数时,需用到super关键字
// 定义一个Father类
class Father {
constructor(x,y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y)
}
}
// Son类继承Father类
class Son extends Father {
constructor(x,y) {
super(x,y); // 调用了父类中的构造函数
}
}
var son = new Son(1,2);
son.sum();
// 2. 调用父类的普通函数
class Father {
say() {
return '我是爸爸'
}
}
class Son extends Father {
say() {
console.log(super.say() + '的儿子')
}
}
var son = new Son();
son.say();
// 3.子类继承父类加法方法时,同时扩展减法方法
// 定义一个Father类
class Father {
constructor(x,y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y)
}
}
class Son extends Father {
constructor(x,y) {
// 子类继承父类加法方法时,同时扩展减法方法
super(x,y)
// super必须在this前面(必须先调用父类的构造方法,再使用子类的方法)
this.x=x;
this.y=y;
}
substract() {
console.log(this.x-this.y);
}
}
var son = new Son(5,3);
son.substract();
son.sum();
(5)三个注意点
①在ES6中没有变量提升,所以必须先定义类,才能通过类实例化对象
②类里面共有的属性和方法一定要加this使用
③类里面this的指向问题:constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者
<button>点击</button>
<script>
class Star {
constructor(uname,age) {// constructor的作用是创建一个实例,故里面的this指向创建的实例
console.log(this);
// 页面加载后this打印结果:Star {}age: undefinedbtn: buttonuname: "xiaozhan"__proto__: Object
this.uname = uname;
this.age = age;
this.btn = document.querySelector('button')
this.btn.onclick = this.sing; // 函数this.sing不能加(),若加了会立即执行而不是点击按钮后再执行
// 点击按钮后打印:<button>点击</button>
this.dance()
}
sing() {
console.log(this);
// 此时打印的this,指向button,因为是button调用的
console.log(this.uname);
// 此时打印的this.uname是undefined,因为button没有uname属性
}
dance() {
// 此时打印的this,谁调用指向谁(此时指向xz,因为是xz调用dance()方法)
console.log(this)
}
}
var xz = new Star('xiaozhan')
xz.dance() // 打印结果:Star {uname: "xiaozhan", age: undefined, btn: button}
</script>
若想点击按钮时调用constructor中的this,可增加一个that变量
<button>点击</button>
<script>
var that
class Star {
constructor(uname,age) {// constructor的作用是创建一个实例,故里面的this指向创建的实例
console.log(this);
// 页面加载后this打印结果:Star {}age: undefinedbtn: buttonuname: "xiaozhan"__proto__: Object
that = this
this.uname = uname;
this.age = age;
this.btn = document.querySelector('button')
this.btn.onclick = this.sing; // 函数this.sing不能加(),若加了会立即执行而不是点击按钮后再执行
// 点击按钮后打印:<button>点击</button>
this.dance()
}
sing() {
console.log(this);
// 此时打印的this,指向button,因为是button调用的
console.log(that.uname); // that存储的是constructor里面的this
// 此时打印的this.uname是undefined,因为button没有uname属性
}
dance() {
// 此时打印的this,谁调用指向谁(此时指向xz,因为是xz调用dance()方法)
console.log(this)
}
}
var xz = new Star('xiaozhan')
xz.dance() // 打印结果:Star {uname: "xiaozhan", age: undefined, btn: button}
</script>