JavaScript面向对象概述

217 阅读2分钟

在最初的JavaScript中没有面向对象的概念,在其他语言的开发者转向JavaScript后,由于习惯了面向对象的开发,在JavaScript中模拟了面向对象的几个核心概念。

封装

function Person(name) {
  this.name = name
}

Person.prototype.eat = function() {
  console.log(this.name + "正在吃饭")
}

const p1 = new Person("张三")

p1.name // 张三
p1.eat() // 张三正在吃饭

每个函数都有prototype属性,被函数new出来的对象会有一个__proto__属性,链接到函数prototype

p1.__proto__ === Person.prototype //true

继承

原型链继承

做法: 把当前构造函数的prototype 链接到由父构造函数创造的对象的__proto__上

function Student(name, sno) {
  this.sno = sno
}

// 为学生构造函数的原型上添加父构造函数的原型上的方法

Student.prototype = new Person()

Student.prototype.study = function() {

  console.log(this.sno + "正在学习")

}
缺点
  1. 通过直接打印对象是看不到这个属性的
  2. 这个属性会被多个对象共享,如果这个对象是一个引用类型,那么就会造成问题 
  3. 不能给Person传递参数,因为这个对象是一次性创建的 

借用构造函数继承

    做法: 在构造函数中调用父构造函数,传入父构造函数需要的参数,即可在当前实例中添加属性。

function Student(name, sno) {
  // 复用父构造器函数,让父构造器函数在当前学生实例中加入name属性
  Person.call(this, name) // 第一次调用父构造函数
  this.sno = sno
}

Student.prototype = new Person() // 第二次调用父构造函数

缺点

  1. 会调用两次父构造函数
  2. 实例中有两份父构造函数的属性:一次是用call调用父构造函数直接添加到实例中;一次是赋值到子构造函数的prototype中(Student.prototype = new Person())

最终解决方案:寄生组合式继承

// 寄生组合式继承

function Person(name) {
  this.name = name;
}


Person.prototype.eat = function () {
  console.log(this.name + "正在吃饭");
};

function Student(name, sno) {
  Person.call(this, name);
  this.sno = sno;
}

// 把Student的显式原型指向Person显式原型的隐式原型

Student.prototype = Object.create(Person.prototype);

Student.prototype.study = function () {

  console.log(this.sno + "正在学习");

};

/**

 * 改变Student实例的类型 
 * Student的实例的类型是Student.prototype.constructor
 * 由于Student.prototype是创建出来的对象 没有constructor属
 * 性,会找到Person.prototype.constructor
 * 需要手动添加construcor属性
*/

Student.prototype.constructor = Student;
const stu = new Student("小明", 112);
// console.log(stu.name);
// stu.eat();
// stu.study();
console.log(stu);
// 让子构造函数的prototype的__proto__指向父构造函数的prototype
// 完成后的效果
// 子构造函数创建出的实例 const stu = new Student("小明", 122)
// stu的__proto__指向Student.prototype
// Student.prototype的__proto__指向Person.prototype
// 调用stu的eat方法,找到Student.prototype => 
// Student.prototype.__proto__ => Person.prototype => 
// Person.prototype.eat

通用继承函数

function inherit(Child, Parent) {
  // 原型寄生
  Child.prototype = Object.create(Parent.prototype);
  // 定义constructor
  Object.defineProperty(Child.prototype, "constructor", {
    configurable: true,
    enumerable: false,
    writable: true,
    value: Child,
  });
}

Object.create 的polyfill

function create(proto) {
   var Fn = function() {}
  Fn.prototype = proto
  return new Fn
}