前一阵子去某度面试,发现自己在技术上还有很多不足,其中之一就是js的面相对象编程,故特此整理一下js中的类与继承。
一、创建对象
1.工厂模式
简单来讲,工厂模式就是一种返回了新对象的方法。
function createPerson (name, age) {
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function () {
console.log(this.name)
}
return o;
}
var person = createPerson('lyx', 24)
特点:简单,可以无数次的调用函数来创建多个类似的对象。
缺点:无法识别对象的类型。
2.构造函数模式
function Person (name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name)
}
}
var person = new Person('lyx', 24);
与工厂模式的区别:
1、没有显式的创建对象
2、将属性和方法赋给this对象
3、没有return语句
new操作符做了什么:
1、创建一个新对象
2、让新对象指向构造函数的作用域(this)
3、执行构造函数
4、返回新对象
特点:类的每个实例都有一个constructor属性,这个constructor属性都指向构造函数。可以用 instanceof 操作符来验证实例的类型。
缺点:每创建一个对象的实例,都会创建实例方法,这样相同的方法会被创建多次
3.原型模式
原型:每个对象都包含一个prototype(原型)属性,这个属性包含类的实例的所有可共享的属性和方法。
function Person () {}
Person.name = 'lyx';
Person.age = 24;
Person.sayName = function () {
console.log(this.name)
}
var person = new Person
特点:被所有实例共享的属性和方法只会被创建一次
缺点:省略了构造函数的初始化环节,导致所有实例默认都有相同的属性值。引用类型的值会被所有实例修改
4.组合模式
即使用构造函数模式创建实例属性和方法,使用原型模式创建原型属性和方法
function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
console.log(this.name)
}
var person1 = new Person('lyx1', 24);
var person2 = new Person('lyx2', 22);
person1.sayName(); //lyx1
person2.sayName(); //lyx2
特点:结合了构造函数模式和原型模式的优点,既有实例间独立的属性和方法,也有所有实例共享的属性和方法
5.动态原型模式
将初始化原型的工作放在构造函数中进行
function Person(name, age) {
this.name = name;
this.age = age;
if (Person.prototype.sayName === undefined) {
Person.prototype.sayName = function () {
console.log(this.name)
}
}
}
var person = new Person('lyx', 24)
特点:与其他面向对象语言保持一致,在构造函数中初始化原型(而不是在构造函数外面)。在初次调用构造函数时才会初始化原型。
二、继承
1.原型链
子类的原型指向父类的实例,即:实例>原型>实例>原型…
function SuperType () {
}
SuperType.prototype.sayHi = function () {
console.log('Hi');
}
function SubType () {
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
var sub = new SubType();
sub.sayHi(); //'Hi'
缺点:不能给父类的构造函数传参。父类的实例属性,会被当做子类的原型属性,被所有子类共享。很少单独使用
2.借用构造函数
function SuperType (name) {
this.name = name
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType (name, age) {
SuperType.call(this, name);
this.age = age;
}
SubType.prototype.sayAge = function () {
console.log(this.age)
}
var sub = new SubType('lyx', 24)
console.log(sub.name); // 'lyx'
缺点:虽然能给父类构造函数传参,但是不能继承原型属性和方法。很少单独使用
3.组合继承
function SuperType(name) {
this.name = name
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function () {
console.log(this.age)
}
var sub = new SubType('lyx', 24)
sub.sayName() //'lyx'
sub.sayAge() //24
特点:结合了原型链继承和借用构造函数继承的有点,即可以给父类构造函数传参,也可以继承原型属性和方法
缺点:会调用两次父类构造函数(继承原型时调用了一次,子类构造函数中调用了一次)
4.原型式继承
function object(prototype) {
function f () {}
f.prototype = prototype;
return new f();
}
var person = {
name: 'lyx',
age: 24
}
var person1 = object(person)
console.log(person1.name) //'lyx'
与只传一个参数的Object.create方法作用 相同,Object.create方法的第二个参数与Object.defineProperties方法的参数一样
5.寄生式继承
创建一个用于封装继承的函数,在函数内部对对象进行增强,然后返回新对象
function create(prototype) {
var o = object(prototype);
o.sayName = function () {
console.log(this.name)
}
return o;
}
6.寄生组合式继承
使用借用构造函数模式继承实例属性和方法,使用原型模式继承原型属性和方法
function create(sub, super) {
var p = object(super);
p.constructor = sub;
sub.prototype = p
}
function SubType (name, age) {
SuperType.call(this, name);
this.age = age;
}
create(SubType, SuperType)
var sub = new SubType('lyx', 24);
over