浅谈js面向对象的写法

浅谈js面向对象

/**
 * 浅谈js面向对象
 * author: Mr Lee (James Lee)
 */

/* 一、创建一个类  
创建一个类(具有相同属性和行为的对象的集合 */

const User = function (id, name, age) {
    this.id = id
    this.name = name
    this.age = age
}

// User.prototype:原型对象相当于一个存储公共方法的仓库
User.prototype.getName = function () {
    return this.name
}

User.prototype.setName = function (name) {
    this.name = name
    // 链式编程
    return this
}

User.prototype.getAge = function () {
    return this.age
}

User.prototype.setAge = function (age) {
    this.age = age
    return this
}

User.prototype.getId = function () {
    return this.age
}
// 创建一个空对象,并改变this指向
var James = new User(520, ‘Mr Lee‘, 18)
console.log(James.getName(), James.getAge())

// 链式编程
James.setName(‘Miss Lee‘).setAge(16)
console.log(James)
console.log(‘*******************************************************‘)

/* 二、封装属性与方法 */

const UserA = function (id, name, age, hobby) {
    this.id = id
    this.name = name
    this.age = age
    // 私有属性 无法继承   私有静态属性:使用闭包
    var hobby = hobby
    // 私有方法
    function setHobby(hobby) {
        hobby = hobby
    }

    function getHobby() {
        return hobby
    }

    this.getName = function () {
        return name
    }

    this.setName = function (name) {
        this.name = name
        return this
    }

    this.getAge = function () {
        return age
    }

    this.setAge = function (age) {
        this.age = age
        return this
    }
}

// 静态公有属性
UserA.isChinese = true
// 静态公有方法
UserA.eat = function (food) {
    console.log(‘I am eating‘ + food)
}
// 静态公有属性、方法存储区
UserA.prototype = {
    // 新创建对象可以通过原型链访问到这些属性和方法
}

const JamesA = new UserA(520, ‘JamesA‘, 18, ‘code‘)
console.log(JamesA)
console.log(JamesA.getName(), JamesA.getAge())

JamesA.setName(‘Miss JamesA‘)
JamesA.setAge(16)
console.log(JamesA.getName(), JamesA.getAge())

// 静态公有属性,必须通过类名访问     
console.log(JamesA.isChinese, UserA.eat(‘apple‘))

console.log(‘*******************************************************‘)

/* 三、使用闭包实现类的静态变量
在C语言中,静态变量 static, 存储在RAM的静态存储区 */

/* 立即执行函数:一个闭包
给老项目添加新功能时,可以使用闭包,避免对项目其他功能产生副作用
注意:闭包引用不释放,会导致内存泄漏;使用完UserB后置为null  UserB = null */
const UserB = (function () {
    // 静态私有变量
    const isChinese = true
    // 静态私有方法
    function eat(food) {
        console.log(‘I am eating‘ + food)
    }
    return function () {
        this.id = id
        this.name = name
        this.age = age
        /* ...... */
    }
})()

/* ************************************************************** */

/* 四、通过一个安全的方式创建对象
通过类来创建对象时,可能会忘记使用new */

// new的作用:纠正this的指向,指向新创建的对象;否则就是——谁用谁知道
const UserC = function (id, name, age) {
    this.id = id,
        this.name = name,
        this.age = age
}

const JamesC = UserC(110, ‘JamesC‘, 18)
// undefined     this,谁用谁知道   严格模式执行undefined  否则指向window
console.log(JamesC)

// 怎样避免没有使用new 导致this指向异常?
const UserD = function (id, name, age) {
    // 检测this指向是否异常,如果没使用new,那么this指向undefined或window
    var hasNew = this instanceof UserD
    if (hasNew) {
        this.id = id
        this.name = name
        this.age = age
    } else {
        // 否则就New一个
        return new UserD(id, name, age)
    }
}

const JamesD = UserD(110, ‘JamesD‘, 18)

console.log(JamesD)

console.log(‘*******************************************************‘)

/* ************************************************************** */

/* 
封装:隐藏底层的复杂性,提供一个统一的接口,降低模块之间的耦合性;隐藏数据,提高安全性
继承:方便代码复用,提高程序的可拓展性
多态性:接口复用  
*/

/* 五、实现继承 */

/* 通过类来继承 */
// 子类的原型对象是父类的实例对象
// 构造函数自身没有的属性或方法会在原型链上向上查找
function Human() {
    this.sleepTime = ‘8h‘
    this.nature = [‘sleep‘, ‘eat‘, ‘lazy‘]
    this.ability = function () {
        console.log(‘make some tools‘)
    }
}

const UserE = function (id, name, age) {
    this.id = id
    this.name = name
    this.age = age
}

UserE.prototype = new Human()

const JamesE = new UserE(110, ‘JamesE‘, 18)
// 修改复杂类型产生副作用
JamesE.nature.push(‘handsome‘)

const BrainE = new UserE(110, ‘BrainE‘, 18)
// 通过原型链对象继承的属性与方法
console.log(JamesE.sleepTime)
JamesE.ability()

/* 
注意:使用类继承,子类对父类中的复杂类型数据采用的是引用访问,
如果其中一个子类修改了引用类型的数据 
那么将会对另外一个对象产生副作用
*/
console.log(BrainE.nature)

console.log(‘-------------------------------------------------------‘)

// 类继承的另一种形式
function inheritObj(obj) {
    // 创建一个空函数对象
    function Func() { }
    Func.prototype = obj
    return new Func()
}

var Person = {
    nickname: ‘‘,
    name: ‘‘,
    age: 0,
    hobby: [],
    getHobby: function () {
        console.log(this.hobby)
    }
}

const personA = inheritObj(Person)
const PersonB = inheritObj(Person)

personA.nickname = ‘James‘
personA.name = ‘Mr Lee‘
personA.age = 20
var arr = [‘games‘, ‘music‘, ‘sports‘]
arr.forEach((item) => {
    personA.hobby.push(item)
})

PersonB.name = ‘Brain‘

personA.getHobby()
// PersonB继承的复杂类型属性hobby跟PersonA的一致
PersonB.getHobby()
console.log(PersonB)

console.log(‘*******************************************************‘)


/* 通过构造函数继承 */
// 精华:call、apply
function HumanA() {
    this.sleepTime = ‘8h‘
    this.nature = [‘sleep‘, ‘eat‘, ‘lazy‘]
}

HumanA.prototype.ability = function () {
    console.log(‘make some tools‘)
}

const UserF = function (id, name, age) {
    // 在子类的构造函数环境中执行一次父类的构造函数实现
    HumanA.call(this)
    this.id = id
    this.name = name
    this.age = age
}

const JamesF = new UserF(112, ‘JamesF‘, 20)
JamesF.nature.push(‘handsome‘)

const BrainF = new UserF(112, ‘BrainF‘, 20)

console.log(JamesF)
console.log(BrainF)

console.log(‘*******************************************************‘)

/* error : 该继承方式没有涉及原型链prototype,所以子类无法继承父类原型方法
如果要继承该方法,就必须将方法放在构造函数中;但是这样创建出来的对象实例都会单独拥有一份方法,而不是共用 */
/* JamesF.ability() */

/* 为了解决上面遇到的问题,可以将类继承与构造器继承结合在一起 */
/* 组合继承  类继承 + 构造函数继承 */

function HumanB() {
    this.sleepTime = ‘8h‘
    this.nature = [‘sleep‘, ‘eat‘, ‘lazy‘]
}

HumanB.prototype.getNature = function () {
    console.log(this.nature)
}

const UserG = function (id, name, age) {
    // 构造函数继承   避免了继承父类复杂类型属性时,继承的是引用
    HumanB.call(this)
    this.id = id
    this.name = name
    this.age = age
}
// 类继承  可以访问到父类原型链上的方法
// 注意:这里会再次执行一遍父类构造
UserG.prototype = new HumanB()

UserG.prototype.ability = function () {
    console.log(‘make some tools‘)
}

const JamesG = new UserG(10, ‘JamesG‘, 20)
const BrainG = new UserG(10, ‘BrainG‘, 20)
JamesG.nature.push(‘handsome‘)
// 可以访问父类原型链上的方法,同时修改继承下来的复杂类型属性,不会对其他实例对象造成影响
JamesG.getNature()
BrainG.getNature()

JamesG.ability()
/* 缺点:
1. 在使用构造函数继承的时候,执行了一遍父类构造函数;当实现子类的类继承时,又会再次执行一遍父类构造 
2. 子类不是父类的实例,但是子类的原型是父类的实例
*/

console.log(‘*******************************************************‘)

/* 寄生式继承 */

// function inheritObj(obj) {
//     // 创建一个空函数对象
//     function Func() { }
//     Func.prototype = obj
//     return new Func()
// }

const PersonA = {
    name: ‘‘,
    age: 0,
    gender: ‘male‘,
    hobby: []
}

function createPerson(obj) {
    // new一个新对象
    var newObj = new inheritObj(obj)
    newObj.getName = function () {
        console.log(this.name)
        return this
    }
    newObj.getAge = function () {
        console.log(this.age)
        return this
    }
    newObj.getHobby = function () {
        console.log(this.hobby)
        return this
    }
    return newObj
}

const JamesLee = createPerson(PersonA)
JamesLee.name = ‘JamesLee‘
JamesLee.hobby = [‘music‘, ‘sports‘]

const BrainChen = createPerson(PersonA)
BrainChen.name = ‘BrainChen‘
// 子类实例对象继承自父类的复杂类型属性不会相互影响
JamesLee.getName().getAge().getHobby()
BrainChen.getName().getAge().getHobby()

console.log(‘*******************************************************‘)


/* 终极继承方式:寄生 + 组合 */

const UserH = function (id, name, age) {
    this.id = id
    this.name = name
    this.age = age
}
// 父类原型方法
UserH.prototype.getName = function () {
    console.log(this.name)
}

const PersonH = function (id, name, age) {
    // 构造函数继承   避免复杂类型属性继承的是引用  
    UserH.call(this, id, name, age)
    // 子类拓展属性
    this.hobby = []
}

/* 
通过组合继承,无法继承父类的原型对象,毕竟不是通过prototype 
而且组合继承有个缺点:就是父类的构造函数在子类的构造函数中执行一次外,还需要在类继承的时候再次调用一次
但是只需要 继承父类的原型对象 即可,没必要去调用两次
*/
function inheritPrototype(sub, sup) {
    // 创建一个临时对象,继承父类的原型对象
    var temp = inheritObj(sup.prototype)
    // 纠正临时对象构造器指向
    temp.constructor = sub
    // 设置子类的原型
    sub.prototype = temp
}
// 子类继承父类的原型对象   
inheritPrototype(PersonH, UserH)
// 子类在原型对象上新增方法
PersonH.prototype.getHobby = function () {
    console.log(this.hobby)
    return this
}

const JamesH = new PersonH(111, ‘JamesH‘, 18)
const BrainH = new PersonH(110, ‘BrainH‘, 16)

JamesH.hobby = [‘music‘, ‘game‘]
JamesH.getHobby().getName()

BrainH.hobby = [‘sports‘]
BrainH.getHobby().getName()

console.log(‘*******************************************************‘)


/* 六、实现多继承   java不支持,c++支持*/
// javascript仅一条原型链,理论上不支持多继承,但是可以向java一样通过多重继承来实现多继承

// 实现一个单继承 ------属性复制(浅拷贝)
// Object.assign(target, source)
const extend = function (target, source) {
    for (let property in source) {
        target[property] = source[property]
    }
    return target
}

const JamesI = {
    name: ‘james‘,
    age: 18,
    gender: ‘male‘
}

const BrainI = {}

const res = extend(BrainI, JamesI)
console.log(res)
// 这种复制仅对简单类型有效,复杂类型拷贝的是引用
console.log(‘*******************************************************‘)

// 单继承------属性复制(深拷贝)
const extendX = function (target, source) {
    for (let property in source) {
        // 判断属性类型
        const type = Object.prototype.toString.call(source[property])

        if (type === ‘[object Array]‘) {
            target[property] = []
            source[property].forEach(i => target[property].push(i))
        } else if (type === ‘[object Object]‘) {
            target[property] = {}
            extendX(target[property], source[property])
        }
        target[property] = source[property]
    }
    return target
}

const JamesJ = {
    name: ‘Mr Lee‘,
    nickname: ‘James Lee‘,
    hobby: [‘music‘, ‘sports‘],
    grade: {
        math: 100,
        english: 100,
    }
}

const BrainJ = {}

const resX = extendX(BrainJ, JamesJ)
console.log(resX)

console.log(‘*******************************************************‘)

/* 实现多继承--------拷贝多个对象属性 */

const multiInherit = function() {
    let length = arguments.length
    let target = arguments[0]
    let source
    for(let i=1; i<length; i++) {
        source = arguments[i]
        extendX(target, source)
    }
    return target
}

// Object.prototype.multiInherit = multiInherit

const brand = {
    brandName: ‘‘,
    brandValue: ‘‘,
}

const appearance = {
    height: ‘‘,
    width: ‘‘,
    color: [‘red‘,‘green‘,‘pink‘]
}

const car = {
    price: ‘‘,
    manufacturer: ‘‘
}

const tesla = {}
multiInherit(tesla,car,appearance,brand)

console.log(tesla)

console.log(‘*******************************************************‘)

/* 实现多态性-------一个方法的多种调用方式*/

const add = function() {
    var num = arguments.length
    const pickUp = {
        0: zero,
        1: one,
        2: two
    }

    function zero() {
        return 0
    }

    function one() {
        return 1
    }

    function two() {
        return 2
    }
    
    return pickUp[num]()
}
// 参数类型或数量不同,返回的结果就不同
console.log(add(),add(‘‘),add(‘‘,‘‘))

浅谈js面向对象的写法

上一篇:html基本结构


下一篇:盒子模型 CSS样式和列表