3. 面向对象高级

3.1 对象创建模式

new运算符

当函数用new运算符调用,会被当做构造函数调用;new会有如下操作:

  1. 创建一个普通的空对象{}
  2. 让这个空对象的__proto__指向构造函数的prototype
  3. 将this指向新创建的这个对象
  4. 如果构造函数返回值是非原始值,这个值将作为新对象的值返回;如果没返回值或返回值是原始值,新对象将作为返回值。(正常情况下,不会写返回值)
  5. Object构造函数方式:先创建Object空对象,再动态添加属性/方法 适用于:起始不确定对象内部数据 缺点:语句多
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var p =new Object();
    p.name = "Tom";
    p.age = 18;
    p.setName = function(name){
    this.name = name;
    };
    p.setAge = function(age){
    this.age = age;
    };
  6. 对象字面量形式:使用{}创建对象,同时指定属性/方法 适用于:起始确定对象内部数据 缺点:创建多个相似对象,代码重复
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var p = {
    name:"Tom",
    age:18,
    setName:function(name){
    this.name = name;
    },
    setAge:function(age){
    this.age = age;
    }
    };
  7. 工厂模式:定义一个工厂函数,内部以字面量形式创建对象,并返回这个对象。 工厂函数:返回一个对象的函数 适用场景:需要创建多个Object对象 缺点:对象没有具体类型,都是Object
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function creatPerson(name,age){
    var obj = {
    name:name,
    age:age,
    setName:function(name){
    this.name = name;
    },
    setAge:function(age){
    this.age = age;
    }
    };
    return obj;
    }

    var p1 = creatPerson("Tom",18);
    var p2 = creatPerson("Bob",15);
  8. 自定义构造函数:定义一个构造函数,通过new创建对象 适用于:需要创建多个类型确定的对象 缺点:每个对象都有相同的数据, 浪费内存(下例中的setName和setAge重复放入p1 p2)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function Person(name,age){
    this.name = name;
    this.age = age;
    this.setName = function(name){
    this.name = name;
    };
    this.setAge = function(age){
    this.age = age;
    };
    }
    var p1 = new Person("Tom",18);
    var p2 = new Person("Bob",25);

    function Student(name,money){
    ……
    }
    var s1 = new Student("Danny",12)

    //由此,我们就区分了Person与Student两种不同类型
    console.log(p1 instanceof Person);
    console.log(s1 instanceof Student);
  9. 构造函数+原型:自定义构造函数, 属性在函数中初始化, 方法添加到原型上
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function Person(name,age){
    this.name = name;
    this.age = age;
    }
    Person.prototype.setName = function(name){
    this.name = name;
    };
    Person.prototype.setAge = function(age){
    this.age = age;
    };

3.2 继承模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Father(name,age){     //this指向Father实例
this.name = name;
this.age = age;
}
Father.prototype.money = function(){

};

function Son(name,age,score){ //this指向Son实例
Father.call(this,name,age); //改变this指向,使其指向Son实例
this.score = score;
}

//使子构造函数原型对象指向Father实例对象
//由于Father实例对象__proto__了Father原型对象,Son实例也可以访问到Father原型对象中的方法
Son.prototype = new Father();
//目前Son.prototype.constructor返回结果为Father,将其修正
Son.prototype.constructor = Son;
//为子构造函数添加新方法
Son.prototype.exam = function(){

};

let son = new Son("liu",18,100);

3.3 面向对象的特点

  • 封装:对象是存储不同属性的容器,并且负责数据的安全 如何确保数据安全:
    1. 私有化属性,在属性名前加#,私有属性只能在对象内部访问;
    2. 提供setter和getter方法,暴露属性。灵活控制读写权限,同时还可以进行数据验证。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Person{
    #name
    #age
    #gender
    construstor(name,age,gender){
    this.#name = name
    this.#age = age
    this.#gender = gender
    }

    set name(name){
    this.#name = name
    }
    get name(){
    return this.#name
    }
    }

  • 继承:extends关键字 super引用父类的属性、方法
  • 多态:JS并不会检查参数的类型,意味着任何类型的参数都可以传入

4. 线程机制与事件机制

4.1 进程与线程

进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。

总结:

进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。

线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位

4.2 JS执行队列

HTML5提出了web worker标准,允许JS脚本创建多个线程。于是,JS出现了同步和异步。

JS的异步是通过回调函数实现的。常见的异步任务:

普通事件,如click、resize等

加载资源,如load、error等

定时器,如setInterval、setTimeout等

同步任务在主线程执行栈中,异步任务在任务队列中。

执行机制:

  1. 先执行执行栈中的同步任务
  2. 遇到异步任务,将异步任务提交给对应的异步线程处理
  3. 当事件发生,将其推入任务队列
  4. 执行栈中的任务执行完毕,在按次序读取任务队列中中的异步任务,被读取的异步任务进入执行栈,开始执行
  5. 重复step4