Bootstrap

JS面向对象ES6

学习JS面向对象ES6的第一天

1、两大编程思想

//面向过程(POP)
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候在一个一个的依次调用
举个例子:将大象装进冰箱,面向过程的做法
(1)打开冰箱门
(2)大象装进去
(3)关上冰箱门

面向过程,就是按照我们分析好了的步骤,按照步骤解决问题

//面向对象(OOP)
面向对象是把事务分解成为一个个对象,然后由对象之间分工和合作
举个例子:将大象装进冰箱,面向对象的做法
先找出对象,并写出这些对象的功能:
1、大象对象
进去
2、冰箱对象
打开
关闭
3、使用大象和冰箱的功能

面向对象是以对象功能来划分问题,而不是步骤

在面向对象程序开发思想中,每一个对象就是功能中心,具有明确分工
面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目
面向对象的特性:
(1)封装性
(2)继承性
(3)多态性

//面向过程和面向对象的对比
面向过程
优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程
缺点:没有面向对象易维护、易复用、易扩展

面向对象
优点:易维护、易封装、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更易于维护
缺点:性能比面向过程低
用面向过程的发放写出来的程序是一份蛋炒饭,而用面向对象写出来的程序是一份盖浇饭

2、面向对象

面向对象更贴近我们的实际生活,可以使用面向对象描述现实世界事物,但是事物分为具体的事物和抽象的事物
举个例子:
手机    抽象的
小米手机 具体的(特指的)

面向对象的思维特点:
1、抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
2、对类进行实例化,获取类的对象

面向对象编程我们考虑的是有哪些对象,按照面向对象的思维特点,不断地创建对象,使用对象,指挥对象做事情

3、对象

现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库,一个网页,一个与远程服务器的连接也可以是“对象”。

在Javascript,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等

对象是由属性和方法组成的:
属性:事物的特征,在对象中用属性来表示(常用名词)
方法:事物的行为,在对象中用方法来表示(常用动词)

4、类

ES6中新增加了类的概念,可以使用class关键字声明一个类,之后以这个类来实例化对象

类抽象了对象的公共部分,它泛指某一大类(class)
对象特指某一个,通过类实例化一个具体的对象

5、创建类

//语法:
class name {
    // class body
}
//创建实例:
var xx=new name();

//注意:类必须使用new实例化对象

6、类constructor构造函数

//constructor()方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor()
//1、创建类 class 创建一个明星类
class Star {
    constructor(uname) {
        this.uname = uname;
    }
}
//2、利用类创建对象 new
var ldh = new Star('刘德华');
console.log(ldh.uname);

//'刘德华'作为参数,当用new创建对象时,传递给constructor中的uname,然后uname转递给this.uname,同时返回实例指向ldh 

//(1)通过class关键字创建类,类名我们还是习惯性定义首字母大写
//(2)类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象
//(3)constructor函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
//(4)生成实例 new 不能省略
//(5)最后注意语法规范,创建类 类名后面不要加小括号,生成实例 类名后面加小括号,构造函数不需要加function

7、类中添加方法

//语法
class Person {
    constructor(name,age) {
        this.name=name;
        this.age=age;
    }
    say() {
        console.log(this.name + '你好');
    }
}

//1、创建类 class 创建一个明星类
class Star {
    constructor(uname,age) {
        this.uname = uname;
        this.age=age;
    }
    sing(song) {
        //console.log('我唱歌');
        console.log(this.name + song);
    }
}
//2、利用类创建对象 new
var ldh = new Star('刘德华',18);

console.log(ldh);

ldh.sing('冰雨');

//(1)我们类里面所有的函数不需要写function
//(2)多个函数方法之间不需要添加逗号分隔

8、类的继承

//继承
现实中的继承:子承父业,比如我们都继承了父亲的姓
程序中的继承:子类可以继承父类的一些属性和方法

//语法
class Father {    //父类
}
class Son extends Father {    //子类继承父类
}

//1、类的继承
//例子1
class Father {
    constructor() {
        
    }
    money() {
        console.log(100);
    }
}
class Son extends Father {
    
}

var son = new Son();
son.money();

9、super关键字

//例子
class Father {
    constructor(x,y){
        this.x=x;
        this.y=y;
    }
    sum() {
        console.log(this.x+this.y);
        
    }
}
class Son extends Father {
    constrcutor(x,y) {
        this.x=x;
        this.y=y;
    }
}
var son=new Son(1,2);

//例子现目前开始运行,会报错,报错的原因在于,传入的(1,2)传进了子类的this,但是没有传入父类的this

//super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数
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);                 //调用了父类中的构造函数
    }
}
var son=new Son(1,2);
son.sum();
add:构造函数与普通函数的区别
1、命名规则
构造函数名首字母一般大写,普通函数名首字母一般小写
2、调用规则
通过new操作符调用的则是构造函数,否则是普通函数
// 普通函数声明
 function run () {
   console.log('I can run!!');
 }
 // 普通函数的调用
 console.log(run());
 // 构造函数声明
 function Person(name,height) {
   this.name = name;
   this.height =height;
   this.say = function (){
     console.log('hello');
   };
 }
 // 构造函数的调用
 console.log(new Person('mrgao','170'));
3、调用时返回内容不同
上述代码中执行控制台的结果为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PIz4c2Wx-1662564375952)(JS面向对象ES6.assets/20200829113515302.png)]

从结果中可以看到,假如普通函数中没有设置return的内容,调用后,返回的是undefined。而构造函数则是返回了一个叫Person的对象,至于这个Person对象里面的属性跟方法是怎么来的呢?下面我来为大家一一讲解。
说到这里,不得不说的就是构造函数内部的原理。
执行构造函数的时候,其内部会有一些“隐式”操作,以下是执行构造函数内部时的原理步骤:
1、立刻在堆内存中创建一个新的对象
2、将新建的对象设置为函数中的this
3、逐个执行函数中的代码
4、将新建的对象作为返回值
super关键字调用父类普通函数
//super关键字调用父类普通函数
class Father {
    say() {
        return '我是爸爸';
    }
}
class Son extends Father{
    say() {
        console.log('我是儿子');
    }
}
var son = new Son();
son.say();

//当Son继承Father后,此时上述代码还是输出'我是儿子'(就近原则)
//1、继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
//2、继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)

//super关键字
//super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数
//语法
class Father {
    say() {
        return '我是爸爸';
    }
}
class Son extends Father {
    say() {
        console.log(super.say() + '的儿子');
    }
}
var son = new Son();
son.say();
super必须放到子类this之前(子类继承父类方法同时扩展自己的方法)
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) {
        this.x=x;
        this.y=y;
        super(x,y);            //注意super与this的书写顺序!!!
    }
    subtract() {
        console.log(this.x-this.y);
    }
}
var son=new Son(5,3);
son.subtract();
//以上程序执行完后会报错,super必须在子类的this前调用

10、类和对象三个注意点

第一个注意点
//1、在ES6中类没有变量提升,必须先定义类,才能通过类实例化对象
第二个注意点
//2、类里面的共有属性和方法一定要加this使用
//例子:
class Star {
    constructor(uname,age) {
        this.uname=uname;
        this.age=age;
    }
    sing() {
        console.log(uname);
    }
}
var ldh = new Star('刘德华');
ldh.sing();
//此时上述程序执行后会报错,修改程序如下:
class Star {
    constructor(uname,age) {
        this.uname=uname;
        this.age=age;
    }
    sing() {
        console.log(this.uname);            //注意看uname前加了this!!!
    }
}
var ldh = new Star('刘德华');
ldh.sing();


//例子:
class Star {
    constructor(uname,age) {
        this.uname=uname;
        this.age=age;
        sing();                    
    }
    sing() {
        console.log(this.uname);    
    }
}
var ldh = new Star('刘德华');
//这次我们不通过ldh.sing()来调用sing方法,而是在constructor中调用
//上述程序会报错,修改如下:
class Star {
    constructor(uname,age) {
        this.uname=uname;
        this.age=age;
        this.sing();                //注意看sing()方法前加上了this                    
    }
    sing() {
        console.log(this.uname);    
    }
}
var ldh = new Star('刘德华');

//例子:
<button>点击</button>
class Star {
    constructor(uname,age) {
        this.uname=uname;
        this.age=age;
        this.btn = document.querySelector('button');            //注意:this.btn
        this.btn.onclick = this.sing;//注意:this.sing注意不要加(),因为如果加了()那么就会立马调用,不能实现点击的效果
    }
    sing() {
        console.log(this.uname);    
    }
}
var ldh = new Star('刘德华');
第三个注意点
//3、类里面的this指向问题
<button>点击</button>
class Star {
    constructor(uname,age) {
        //constructor里面的this指向的是 创建的实例对象Star
        this.uname=uname;
        this.age=age;
        this.btn = document.querySelector('button');           
        this.btn.onclick = this.sing;
    }
    sing() { 
        //这个sing方法里面的this 指向的是btn这个按钮,因为这个按钮调用了这个函数
        console.log(this.uname);    //输出undefined,因为button中没有uname的定义
    }
    dance() {
        //这个dance里面的this 指向的是实例对象 ldh 因为ldh 调用了这个函数
        console.log(this);
    }
}
var ldh = new Star('刘德华');

//如果想在sing里面调用constructor里面的this,则需要设置一个全局变量,同时这个全局变量存储constructor里面的this,再将sing里面对this的引用替换成这个全局变量即可
//例如对上述程序进行修改:
<button>点击</button>
var that;                  //定义全局变量
class Star {
    constructor(uname,age) {
        that = this ;        //将constructor中的this赋值给全局变量that
        this.uname=uname;
        this.age=age;
        this.btn = document.querySelector('button');           
        this.btn.onclick = this.sing;
    }
    sing() { 
        //console.log(this.uname);
        console.log(that.uname);      //将这里的this替换成that
    }
    dance() {
        console.log(this);
    }
}
var ldh = new Star('刘德华');

11、构造函数和原型

概述
在典型的OOP的语言中(java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS中没有引入类的概念
创建对象可以通过以下三种方式:
1、对象字面量
var obj1 = new Object();
2new Object()
var obj2 = {};
3、自定义构造函数
function Star(uname,age) {
    this.uname = uname;
    this.age = age;
    this.sing = function() {
        console.log('我会唱歌');
    }
}
var ldh = new Star('刘德华',18);
console.log(ldh);
构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面

在JS中,使用构造函数时要注意以下两点:
1、构造函数用于创建某一类对象,其首字母要大写
2、构造函数要和new一起使用才有意义

new在执行时会做四件事情:
1、在内存中创建一个新的空对象
2、让this指向这个新的对象
3、执行构造函数里面的代码,给这个新对象添加属性和方法
4、返回这个新对象(所以构造函数里面不需要return
静态成员和实例成员
静态成员:在构造函数本上添加的成员称为静态成员,只能由构造函数本身来访问
实例成员:在构造函数内部创建的对象成员称为实例成员,只能有实例化的对象来访问
;