Js 面向对象总结

Js 面向对象总结


JavaScript 中没有父类, 子类的概念, 也没有class 和 instance 的概念, 全靠 prototype chain来实现继承.

查找一个对象的属性时, JavaScript 会向上遍历 prototype chain, 直到找到对应的属性为止.

类方法和实例方法

这里要说的类方法其实就是静态方法。实例方法就是必须创建实例才能调用的方法

两者区别如下

  • 实例方法要用function这个对象中的prototype属性来定义
  • 静态方法通过A.直接定义
  • 静态方法可以直接通过A.来调用
  • 实例方法必须要new一个对象来调用。
function A(){}

A.sayMeS=function(){
	console.log("Hello World S!");
} // 定义静态方法
A.sayMeS();//输出Hello World S! 
var a = new A(); // 定义引用变量
a.sayMeS(); //a.sayMeS is not a function 引用变量尝试调用静态方法失败

//实例方法:
function A(){}
A.prototype.sayMeE=function(){
	console.log("Hello World E!");
}
var a=new A(); 
a.sayMeE();//输出Hello World E! //引用变量调用实例方法
A.sayMeE(); //A.satMeE is not a function

继承

prototype继承

function Parent1() {
  this.name = 'parent1';
  this.play = [1, 2, 3]
}
function Child1() {
  this.type = 'child2';
}
Child1.prototype = new Parent1();
console.log(new Child1());
//实现了继承

console.log(new Child2()) // 没有打印name,play

var s1 = new Child2();
var s2 = new Child2();
s1.play.push(4);
console.log(s1.play, s2.play); // 但是可以访问到name, play

缺点:

  • 父类的引用属性不被共享
  • 子类不能拿到父类属性值

call继承

function Parent1(){
  this.name = 'parent1';
}
Parent1.prototype.getName = function () {
  return this.name;
}
function Child1(){
  Parent1.call(this);
  this.type = 'child1'
}
let child = new Child1();
console.log(child);  // 没问题
console.log(child.getName());  // 会报错

优点:子类能够拿到父类的属性值
缺点:只能继承父类的实例属性和方法,不能继承原型属性或者方法。

组合继承

function Parent3 () {
   this.name = 'parent3';
   this.play = [1, 2, 3];
 }

 Parent3.prototype.getName = function () {
   return this.name;
 }
 function Child3() {
   // 第二次调用 Parent3()
   Parent3.call(this);
   this.type = 'child3';
 }

 // 第一次调用 Parent3()
 Child3.prototype = new Parent3();
 // 手动挂上构造器,指向自己的构造函数
 Child3.prototype.constructor = Child3;
 var s3 = new Child3();
 var s4 = new Child3();
 s3.play.push(4);
 console.log(s3.play, s4.play);  // 不互相影响
 console.log(s3.getName()); // 正常输出'parent3'
 console.log(s4.getName()); // 正常输出'parent3'

缺点:父类构造函数执行了两次,可能造成性能问题

Object.create继承

let parent4 = {
   name: "parent4",
   friends: ["p1", "p2", "p3"],
   getName: function() {
     return this.name;
   }
 };

 let person4 = Object.create(parent4);
 person4.name = "tom";
 person4.friends.push("jerry");

 let person5 = Object.create(parent4);
 person5.friends.push("lucy");

 console.log(person4.name); // tom
 console.log(person4.name === person4.getName()); // true
 console.log(person5.name); // parent4
 console.log(person4.friends); //[ 'p1', 'p2', 'p3', 'jerry', 'lucy' ]
 console.log(person5.friends); //[ 'p1', 'p2', 'p3', 'jerry', 'lucy' ]

缺点:多个实例的引用类型属性指向相同的内存

寄生组合式继承

 function clone (parent, child) {
  // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
  child.prototype = Object.create(parent.prototype);
  child.prototype.constructor = child;
}

function Parent6() {
  this.name = 'parent6';
  this.play = [1, 2, 3];
}
 Parent6.prototype.getName = function () {
  return this.name;
}
function Child6() {
  Parent6.call(this);
  this.friends = 'child5';
}

clone(Parent6, Child6);

Child6.prototype.getFriends = function () {
  return this.friends;
}

let person6 = new Child6();
console.log(person6);
console.log(person6.getName());
console.log(person6.getFriends());

优点:减少了构造次数,减少了性能的开销

ES6 Extends继承

本质上采用的是 寄生组合式继承, 效果与之相同

class Person {
  constructor(name) {
    this.name = name
  }
  // 原型方法
  // 即 Person.prototype.getName = function() { }
  // 下面可以简写为 getName() {...}
  getName = function () {
    console.log('Person:', this.name)
  }
}
class Gamer extends Person {
  constructor(name, age) {
    // 子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
    super(name)
    this.age = age
  }
}
const asuna = new Gamer('Asuna', 20)
asuna.getName() // 成功访问到父类的方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值