1 Star 0 Fork 0

chuan / jsES6面向对象学习归档

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
1面向对象编程.md 10.74 KB
一键复制 编辑 原始数据 按行查看 历史

面向对象编程 OOP(Object Oriented Programming)

面向过程编程 POP(Process-oriented programming):分析出解决问题需要的步骤,然后用函数把这些步骤一步步实现,使用的时候在一个一个依次调用就行了。

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

面向对象编程:把事务分解成一个个对象,然后由对象间分工合作实现事务。

  • 优点:易维护,易复用,易扩展,由于面向对象由封装,继承,多态性的特征,可以设计出低耦合的系统,使系统 更加灵活,更加易于维护。
  • 缺点:性能比面向过程低。

面向对象的思维特点:

  1. 抽取(抽象)对象共有的属性和行为组织(封装)成一个类(模板)
  2. 对类进行实例化,获取类的对象。

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


ES6 class的创建

javaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,如:字符串,数值,数组,函数等。

对象是由属性和方法组成的:

  • 属性:事物的特征
  • 方法:事物的行为

创建类:

  1. 通过 class关键字创建类,类名规范首字母大写
  2. 类里面 constructor方法,接受实例化时传递的参数并返回实例对象。
  3. 通过 new命令生成实例对象时,会自动调用 constructor方法。
  4. 语法规范:创建类时 类名不要加小括号。生成实例时 类名后面加括号。构造函数(方法)不需要加 function.

类的继承

  1. 通过 extends实现类的继承。class Son extends Father {}
  2. 通过 super关键字调用访问父类的函数(包括构造函数。
  3. 对于继承中的类属性和方法的查找原则:就近原则

注意:constructor中 super关键字 必须在子类 this前调用

let that

class Father {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
    sum() {
        return this.x + this.y
    }
}

class Son extends Father {
    constructor(x, y) {
        super(x, y)     // constructor中 super关键字 必须在子类 this前调用
        this.x = x
        this.y = y         
        this.start()       

        that = this  // 验证this 指向
    }
    sum(){
        console.log('对于重名方法属性,就近调用,并调用原父类方法的值:', super.sum())
    }
    start(){
        console.log('Son实例化了一个类对象');
    }
}

const test = new Son(3, 9)
test.sum()
console.log(that === this)  // constructor 里面的this 指向的是 创建的实例对象。 

Tips:

  • ES6中类没有变量提升,必须先定义类,才能实例化该类的对象
  • constructor 里面的this 指向的是 创建的实例对象。
  • 类方法里面的 this 指向的是该方法的调用者。(箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值)
案例:通过一个类进行dom操作

构造函数和原型(ES6 之前通过此来模拟类的实现

实现类

在典型的OOP的语言中(如Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS中并没用引入类的概念

  • ES6,全称 Ecmascript6.0,2015.06发版。但是目前浏览器的 Javascript是ES5版本,大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能。
  • 在ES6之前,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。

创建对象可以通过以下三种方式:

  1. 对象字面量
  2. new Object()
  3. 自定义构造函数
// new Object()创建对象
let obj1 = new Object()

// 对象字面量创建对象
let obj2 = {}

// 构造函数
function Star(name, age) {
    this.uname = name   // 类默认有 ClassName.name 方法,返回对象名,。请不要使用name 命名
    this.age = age
    this.sing = function () {
        console.log("构造函数创建对象")
    }
}
// 构造函数创建对象
let obj3 = new Star("obj", 13)

console.log(obj1)
console.log(obj3)

new 在执行时会做的事情:

  1. 在内存中创建一个新的空对象
  2. 让this指向这个新的对象
  3. 执行构造函数里面的代码,给这个新对象添加属性和方法。
  4. 返回这个新对象(所以构造函数里面不需要 return
静态成员和实例成员

实例成员:就是构造函数内部通过this添加的成员,实例成员只能通过实例化的对象来访问

静态成员:在构造函数本身上添加的成员。静态成员只能通过构造函数来访问

function Star2(name) {
    this.uname = name
}

let obj4 = new Star2('name-test')
// 访问实例成员
console.log(Star2.uname); // undefined
// 访问静态成员
Star2.sex = '1'
console.log(Star2.sex);
console.log(obj4.sex);  // undefined

Tips: 构造函数很好用,但会存在浪费内存的问题。实例化对象时会将其中的复杂数据类型(函数,)单独开辟内存空间存放,如果有多个实例化对象将会造成不必要的资源浪费。引入构造函数原型解决

构造函数原型 prototype

构造函数通过原型分配的函数是所有对象共享的。Javascript规定,每一个构造函数都有个 prototype属性,指向另一个对象。注意这个 prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

通过把那些不变的方法,直接定义在 prototype对象上,实现所有的对象实例共享这些方法。

function Star2(name) {
    this.uname = name
}

Star2.prototype.sing = function () {
    console.log('test');
}

let obj4 = new Star2('name-test')
const obj5 = new Star2('name-test2')

console.log(obj4.sing === obj5.sing) // true
// 对象身上系统会自己添加一个 __proto__指向构造函数的原型对象 prototype
console.log(obj4.__proto__ === Star2.prototype) 

Tips:

  • 一般公共属性定义到构造函数里面,公共方法放到原型对象上
  • 对象方法(obj4.sing)的执行顺序:先查看自身是否有sing方法,有则执行。没有则通过 __proto__去构造函数原型对象 prototype身上去査找 sing这个方法

构造函数 constructor

对象原型( proto)和构造函数( prototype)原型对象里面都有个属性 constructor属性, constructor我们称为构造函数,因为它指回构造函数本身。

Constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数

function Star2(name) {
    this.uname = name
}

Star2.prototype = {
    // constructor 指回原来的构造函数
    constructor: Star2,
    sing: function(){
        // ..code
    }
}
let obj4 = new Star2('name-test')
console.log(Star2.prototype.constructor === obj4.__proto__.constructor)

如果修改了原来的原型对象,给原型对象赋值的是另一个对象,则必须手动的利用 constructor 指回原来的构造函数。否则类的实体将会是新赋值的这个对象(不会继承原有方法)


构造函数,实例,原型对象三者之间的关系: 图片

原型链

function Star2(name) {
    this.uname = name
}

Star2.prototype = {
    // constructor 指回原来的构造函数
    constructor: Star2,
    sing: function(){
        // ..code
    }
}

const obj = new Star2('name')
// 只要是对象就有 __proto__ 原型,指向原型对象
console.log(Star2.prototype)
// Star2原型对象里面的 __proto__原型指向的是 Object.prototype
console.log(Star2.prototype.__proto__ ==== Object.prototype)
// Object.prototype.__proto__ 指向 null
console.log(Object.prototype.__proto__)

js成员的查找机制是依照原型链从下至上的:原型链

扩展内置对象的方法

console.log(Array.prototype)

// 扩展内置对象方法。
Array.prototype.sum = function () {
    let sum = 0
    this.forEach((num) => {
        sum += num
    })
    return sum
}
// let arr = new Array(1, 2, 3, 4, 'a')
let arr = [1, 2, 3, 4, "a"]
console.log(arr.sum())

构造函数实现类的继承

ES6之前并没有提供 extends继承。可以通过构造函数+原型对象模拟实现继承,被称为组合继承

call()

调用这个函数,修改函数运行时的 this指向 func.call(thisArg, arg1, ...)

  • thisArg: 函数运行时使用的 this 值。
  • args1, ...: 指定的参数列表(函数所需参数)
  • 相关函数:apply(), bind()
function fn() {
    console.log("fn-1")
    // 其中this 默认指向其调用者。
    console.log(this)
}

// 函数调用call 会被执行, this === window
fn.call()

// 改变this指向 this == Object
fn.call({ uname: "andy" })
继承实现
function Father(uname, age) {
    this.uname = uname
    this.age = age
}

Father.prototype.money = function () {
    console.log("1000")
}

function Son(uname, age, sex = 1) {
    // 父构造函数中的this原本指向其实例化对象。需要 将子类的 this传入父级,完成继承
    // 这里继承了构造函数,并没继承父的原型方法
    Father.call(this, uname, age)
    this.sex = sex
}
// 此种方式传递继承,如果修改了子原型对象则父级也会跟着变化
// Son.prototype = Father.prototype
// 通过一个 新实例化对象完成原型的继承
Son.prototype = new Father()
// 利用对象的形式修改了原型对象,需要利用 constructor 指回原构造函数
Son.prototype.constructor = Son

Son.prototype.call = function (num = null) {
    console.log(num)
}

let son = new Son("name", 17)
son.money()
console.log(Father.prototype)
console.log("\n")
console.log(Son.prototype)

结语

  • ES6 前通过 构造函数+原型 实现面向对象编程
  • ES6 通过类实现面向对象编程

类的本质还是一个函数

typeof className === function 类可以理解为构造函数的另一种写法

通过 class关键字定义的类, 同样拥有通过构造函数定义类的特点:

  1. 有原型对象 prototype
  2. 原型对象 prototype里面有 constructor指向构造函数本身
  3. 可以通过原型对象添加方法
  4. 创建的实例对象有 proto_原型指向构造函数的原型

ES6的类它的绝对大部分功能,ES5都可以做到,新的 class写法只是让对象原型的写法更加加清晰、更像面向对象编程的语法而已。

JavaScript
1
https://gitee.com/chuancode/js_es6.git
git@gitee.com:chuancode/js_es6.git
chuancode
js_es6
jsES6面向对象学习归档
master

搜索帮助