javascript面向对象编程

1.封装

// 面向对象编程-类的封装
// 类中的属性和方法:
//      私有属性
//      私有方法
//      公有属性
//      特权方法
//      原型属性(位于原型中)
//      公有方法(位于原型中)

// 寄生组合式继承
// 父类
function Person(name, age) {
    // 公有属性
    this.name = name;
    this.age = age;

    // 特权方法
    // 特权方法可以访问私有属性、私有方法、特权属性、特权方法
    // 特权方法不能放在prototype内,因此无法复用

    // 访问私有属性sexOrientation的特权方法
    this.getSexOrientation = function () {
        return sexOrientation;
    };

    // 访问私有方法setSexOrientation的特权方法
    this.admittedByBUPT = function () {
        console.log("I'm admitted by BUPT!");
        setSexOrientation('同性恋');
    };

    // 私有属性
    var sexOrientation = '异性恋';

    // 私有方法
    // 私有方法可以访问私有属性(闭包)、私有方法(闭包)
    // 因为在私有方法中this指向的是全局对象window(不信可以打印this看一下),所以无法通过this访问到公有属性和特权方法
    var setSexOrientation = function (sex) {
        sexOrientation = sex;
        // console.log(this);
    };
}

// 原型属性
Person.prototype.className = 'Person';

// 原型方法
// 原型方法可以访问原型属性、原型方法、公有属性、特权方法,但不可以访问私有属性和私有方法
Person.prototype.getName = function () {
    return this.name;
};
Person.prototype.getAge = function () {
    return this.age;
};

// 静态属性
Person.country = 'Chinese';
// 静态方法
Person.setCountry = function (country) {
    this.country = country;
};
Person.getCountry = function () {
    return this.country;
};

// 寄生组合式继承
// 继承公有属性、特权方法、原型属性、公有方法
// 本写法是纯净的组合式继承
// 组合式继承指的是“原型链继承”+“构造函数式继承”
function Teacher(name, age, school) {
    // 构造函数式继承
    Person.call(this, name, age);

    // 子类新增公有属性
    this.school = school;
}

// 子类新增公有方法
Teacher.prototype.getSchool = function () {
    return this.school
};

// 实现继承
// 可以将这段实现继承的代码封装成一个inherit函数
var F = function () {};
F.prototype = Person.prototype;
var o = new F();
o.constructor = Teacher;
Teacher.prototype = o;

2.原型链继承

// 原型链继承
// 父类
function Person(name,age) {
    this.name = name;
    this.age = age;
}

Person.prototype.getName = function () {
    return this.name;
}
Person.prototype.getAge = function () {
    return this.age;
}

// 子类
function Teacher(school) {
    this.school = school;
}

// 注意
// 创建继承关系时无法向父类传递参数(因为传什么参数都会被所有子类实例共享,因此最好不传递任何参数),因此无法对父类中的属性进行实例化
// 这是原型链继承的第一个缺点
Teacher.prototype = new Person();
Teacher.prototype.getSchool = function () {
    return this.school;
}

var t1 = new Teacher('BUPT');
var t2 = new Teacher('BUPT');

console.log(t1 instanceof Teacher);
console.log(t1 instanceof Person);

console.log(t2 instanceof Teacher);
console.log(t2 instanceof Person);

console.log(Teacher.prototype.isPrototypeOf(t1));
console.log(Person.prototype.isPrototypeOf(t1));

console.log(Teacher.prototype.isPrototypeOf(t2));
console.log(Person.prototype.isPrototypeOf(t2));

console.log(t1.getSchool());
console.log(t1.getName());
console.log(t1.getAge());


console.log(t2.getSchool());
console.log(t2.getName());
console.log(t2.getAge());

t1.school = 'BNU';

// 注意
// 如果采用第一种设置属性的方式,那么在对象t1和其__proto__里面有相同的两份属性,只不过属性值不一样
// 如果采用第二种设置属性的方式,那么很显然,对象t2也将获得与对象t1相同的属性值
// 这是原型链继承的第2个缺点

// 第一种
// t1.name = 'Tom';
// t1.age = 20;

// 第二种
t1.__proto__.name = 'Tom';
t1.__proto__.age = 20;

console.log(t1.getSchool());
console.log(t1.getName());
console.log(t1.getAge());


console.log(t2.getSchool());
console.log(t2.getName());
console.log(t2.getAge());

3.构造函数式继承

// 构造函数式继承
// 父类
function Person(name,age) {
    this.name = name;
    this.age = age;
}

Person.prototype.getName = function () {
    return this.name;
}
Person.prototype.getAge = function () {
    return this.age;
}

// 子类
function Teacher(name, age, school) {
    Person.apply(this, arguments);
    this.school = school;
}

Teacher.prototype.getSchool = function () {
    return this.school;
}

var t1 = new Teacher('Tom', 20, 'BUPT');
var t2 = new Teacher('Joy', 18, 'BNU');

console.log(t1 instanceof Teacher);
console.log(t1 instanceof Person);// false

console.log(t2 instanceof Teacher);
console.log(t2 instanceof Person);// false

console.log(Teacher.prototype.isPrototypeOf(t1));
console.log(Person.prototype.isPrototypeOf(t1));

console.log(Teacher.prototype.isPrototypeOf(t2));
console.log(Person.prototype.isPrototypeOf(t2));

// 注意
// 由于这种构造函数式继承没有涉及到父类的原型,因此子类实例无法访问父类prototype中定义的属性和方法
// 这是构造函数式继承的明显缺点

// console.log(t1.getName());
console.log(t1.name);
console.log(t1.getSchool());

// console.log(t2.getName())
console.log(t2.name);
console.log(t2.getSchool());

4.组合式继承:原型链继承+构造函数式继承

// 原型链继承+构造函数式继承=组合式继承
// 父类
function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.getName = function () {
    return this.name;
}

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

// 子类
function Teacher(name, age, school) {
    // 构造函数式继承
    Person.apply(this, arguments);
    this.school = school;
}

// 原型链继承
Teacher.prototype = new Person();
Teacher.prototype.getSchool = function () {
    return this.school;
}

var t1 = new Teacher('Tom', 20, 'BUPT');
var t2 = new Teacher('Joy', 18, 'BNU');

console.log(t1 instanceof Teacher);
console.log(t1 instanceof Person);

console.log(t2 instanceof Teacher);
console.log(t2 instanceof Person);

console.log(Teacher.prototype.isPrototypeOf(t1));
console.log(Person.prototype.isPrototypeOf(t1));

console.log(Teacher.prototype.isPrototypeOf(t2));
console.log(Person.prototype.isPrototypeOf(t2));

console.log(t1.getName());
console.log(t1.name);
console.log(t1.getSchool());

console.log(t2.getName())
console.log(t2.name);
console.log(t2.getSchool());

// 注意
// 这种组合式继承将父类构造函数执行了(n+1)遍,n为实例化的对象个数。
// 如果n远大于1的话倒是没什么问题,但是如果n比较小(一般情况应该也不会很大吧),比如n=1,即只实例化一次,那么这一次额外的父类构造函数调用(添加原型时的那次调用)将不能忽略。
// 并且,这次构造函数的调用将在子类的原型添加父类的属性,可以通过下面查看__proto__来看到。
// 因此这种继承方式存在两个缺点:多调用一次父类构造函数造成额外开销,继承冗余不纯净。
console.log(t1.__proto__);
console.log(t2.__proto__);

5.寄生组合式继承

  请移步1封装。

原文地址:https://www.cnblogs.com/iamswf/p/5229093.html