JavaScript面向对象

Posted 还是不会呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript面向对象相关的知识,希望对你有一定的参考价值。

原型赋值继承

在上篇文章中,借用构造函数方法实现继承还是存在弊端:父构造函数多次调用,而且在将父类实例传给子类原型对象时,里面的变量是会额外存在一份的。

那么可以这样:

function Person(name) {
  this.name = name;
}
Person.prototype.sleeping = function () {
  console.log(this.name + "在睡觉~");
};

function Student(sno, name) {
  Person.call(this, name);
  this.sno = sno;
}
// 直接子类的原型对象指向父类的原型对象
Student.prototype = Person.prototype;
Object.defineProperty(Student.prototype, "constructor", {
  configurable: true,
  enumerable: false,
  writable: true,
  value: Student,
});

Student.prototype.studying = function () {
  console.log(this.name + "在学习~");
};

var stu = new Student(2021, "fzb");
console.log(stu); // Student { name: 'fzb', sno: 2021 }
stu.sleeping(); // fzb在睡觉~
stu.studying(); // fzb在学习~

表面上看上去没用问题,但是如果许多子类继承自同一个父类,那么在子类原型上加的方法或属性,将会被其他子类所共享,这样父类的原型对象会越来越大,这样也显然是不可以的。

那接着往下看~~~

寄生组合式继承

原理就是子类的原型指向另一个新的对象,但是这个对象的原型指向的是父类的原型对象,这样子类以后再其原型上加方法,继承自相同父类的子类,是无法共享这个方法的。

在得出最终的方案之前,先来认识一下 原型式继承和寄生式寂继承

原型式继承

这个方法由道格拉斯提出并推广。

创建一个新的对象,使这个对象的原型指向指定的对象。

道格拉斯提出的原始方案:

var obj = {
  name: "fzb",
  age: 21,
};
// 问题: info是一个对象,但是这个对象的原型要指向obj
function createObjectrue(o) {
  function Foo() {}
  Foo.prototype = o;
  return new Foo();
}

var info = createObjectrue(obj);
console.log(info.age); // 21

随着JS发展,有了更加直接的操作方法:

Object.setPrototypeOf

function createObjectrue(o) {
  var newObj = {};
  Object.setPrototypeOf(newObj, o);
  return newObj;
}

Object.create

function createObjectrue(o) {
  var newObj = Object.create(o);
  return newObj;
}

寄生式继承

也是由道格拉斯提出,寄生式继承融合了原型式继承和工厂模式

因为由原型式继承创造出来的对象,若想给创建出来的对象,批量添加方法或者属性,那是非常麻烦的,能不能再创建时就给加上去呢?

function createObject(o) {
  function Foo() {}
  Foo.prototype = o;
  return new Foo();
}
// 批量创建对象时,加入studying方法
function createStudent(o) {
  var newObj = createObject(o);
  newObj.studying = function () {
    console.log("在学习~");
  };
  return newObj;
}

缺陷:因为具有工厂模式,也就有着工厂模式的缺点。

  • 创建出来的对象是不知道具体的类型的
  • 批量创建的对象,添加的方法是在每个对象本身上的,创建对象过多,会大量消耗内存

原型式继承和寄生式继承是在对象层面的,借用其中的思想,拓展到类上。

寄生组合式继承的实现

例子

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

Person.prototype.sleeping = function () {
  console.log("在睡觉~");
};
// 子类构造函数的prototype指向创建的新对象,新对象的隐式原型指向父构造函数的原型对象
// 一个子类继承父类就调用次方法,使子类在原型对象上添加方法不会相互共享
function inhertPrototype(subType, supType) {
  // 也可以用文章上方提到的方法,这里我直接使用 Object.create 了
  subType.prototype = Object.create(supType);
  Object.defineProperty(subType.prototype, "constructor", {
    configurable: true,
    enumerable: false,
    writable: true,
    value: subType,
  });
}

function Teacher(subject, name) {
  Person.call(this, name);
  this.subject = subject;
}

inhertPrototype(Teacher, Person);
Teacher.prototype.teaching = function () {
  console.log("在教书~");
};

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

inhertPrototype(Student, Person);

Student.prototype.studying = function () {
  console.log("在学习~");
};
// 测试
const teacher = new Teacher("前端", "teacher");
const student = new Student(2021, "fzb");
teacher.teaching();
student.studying();
console.log(teacher.__proto__ === student.__proto__); // false
console.log(teacher.__proto__.__proto__ === student.__proto__.__proto__); // true
// student.teaching();  // 报错
// teacher.studying(); // 报错

内存图

简便方法

但是思想却是一样的,

Object.setPrototypeOf(Teacher.prototype, Person.prototype);
Object.setPrototypeOf(Student.prototype, Person.prototype);

利用原本的原型对象,手动改掉原型对象上隐式原型的指向,也可以。

原型判断方法补充

hasOwnProperty

判断一个属性是否在对象本身上。

var info = {
  age: 21,
};

// obj.hasOwnProperty("address") true
var obj = {
  address: "长沙",
};

//  obj.hasOwnProperty("score") true
Object.defineProperty(obj, "score", {
  configurable: true,
  enumerable: false,
  writable: false,
  value: 100,
});
// obj.hasOwnProperty("age") false
obj.__proto__ = info;

in 操作符

判断键key是否出现在对象所在的原型链上。

var info = {
  age: 21,
};

// console.log("address" in obj); true
var obj = {
  address: "长沙",
};

//  console.log("score" in obj); true
Object.defineProperty(obj, "score", {
  configurable: true,
  enumerable: false,
  writable: false,
  value: 100,
});
// console.log("age" in obj); true
obj.__proto__ = info;

instanceof

检测某一个构造函数prototype是否出现在某个对象的原型链上。

function Foo() {}
var foo = new Foo();

var obj = {};
obj.__proto__ = foo;
console.log(obj instanceof Foo); // true

isPrototypeOf

检测某一个对象是否出现在某个对象的原型链上

var obj = {};
var info = {};

info.__proto__ = {};
info.__proto__.__proto__ = obj;

console.log(obj.isPrototypeOf(info));

以上是关于JavaScript面向对象的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段12——JavaScript的Promise对象

VSCode自定义代码片段12——JavaScript的Promise对象

常用Javascript代码片段集锦

JavaScript单行代码,也就是代码片段

javascript 仿面向对象编程实例代码(私有,公共变量。。。)

JavaScript对象原型链继承闭包