JS面向对象基础知识

112 阅读2分钟

创建对象

工厂模式

function createPerson(name){
    let person = {};
    person.name = name;
    person.sayName = function(){
        console.log(this.name)
    }
    return person;
}
let person1 = createPerson("john");
let person2 = createPerson("amy");

每次调用createPerson,都会生成一个包含一个属性和一个方法的对象。但这样新建出的对象无法判断是何种类型,只能判断为Object实例。

构造函数模式

function Person(name){
    this.name = name;
    this.sayName = function (){
        console.log(this.name);
    }
}
let person1 = new Person("mike");
let person2 = new Person("james");

这样比工厂模式的好处是,新建出的对象可以确定为特定类型。

image.png

但是使用构造函数模式仍然存在一个问题,就是每次new一个对象出来以后,所有的对象方法都是新的Function实例,导致性能损耗。

image.png

new操作符做了什么

  1. 创建一个新对象
  2. 新对象的[[Prototype]]特性被赋值为构造函数的prototype
  3. 即this指向新对象
  4. 执行构造函数内部的代码
  5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。

简单的new操作符模拟:

function myNew(myConstructor, ...args) {
  let obj = {};
  obj.__proto__ = myConstructor.prototype;
  let res = myConstructor.apply(obj, args);
  let isObject = typeof res === "object";
  let isFunction = typeof res === "function";
  return isObject || isFunction ? res : obj;
}

原型模式

function Person() {}
Person.prototype.name = "jordan";
Person.prototype.sayName = function () {
  console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();

image.png

继承

原型链继承

function Parent() {
  this.name = "mike";
}

Parent.prototype.sayName = function () {
  console.log(this.name);
};

function Child() {}
Child.prototype = new Parent();

let child = new Child();
child.sayName();

但此方式有两种问题:一,原型中的引用值会被所有实例共享;二,子类型在实例化时不能给父类型的构造函数传参。

经典继承

function Parent() {
  this.names = ["john", "jackson"];
}

function Child() {
  Parent.call(this);
}

let parent = new Parent();
parent.names.push("luke");
console.log(parent.names);

let child = new Child();
console.log(child.names);

经典继承存在的问题和用构造函数方式新建对象一样,每次新创建实例都会创建新的方法实例。

组合继承

function Parent(name) {
  this.name = name;
}
Parent.prototype.sayName = function () {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name); // 二,第二次执行Parent构造函数
  this.age = age;
}
Child.prototype = new Parent(); // 一,第一次执行Parent构造函数
Child.prototype.constructor = Child;

let child1 = new Child("jackson", 12);
let child2 = new Child("john", 16);
console.log(child1.name);
console.log(child1.sayName === child2.sayName);

组合继承的方式是使用最为普遍的一种方式。但还存在一个问题,即会调用两次父类的构造函数。

寄生式继承

function createObj(o) {
  var clone = Object.create(o);
  clone.sayName = function () {
    console.log("hi");
  };
  return clone;
}

寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。但和经典继承一样,每次创建对象都会创建一遍方法。

寄生组合式继承

function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

function inheritPrototype(child, parent) {
  var prototype = object(parent.prototype);
  prototype.constructor = child;
  child.prototype = prototype;
}

function Parent(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

Parent.prototype.sayName = function () {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

inheritPrototype(Child, Parent);

Child.prototype.sayAge = function () {
  console.log(this.age);
};