js 面向对象 oop、及继承方式实现、对比

  • 要理解js面向对象,首先得了解原型,对象,构造函数之间的关系,如下面示例为例
function Person(name) { //构造函数
    this.name = name;
}

Person.prototype.printName = function () { //原型对象
    alert(this.name);
}

var person1 = new Person('Byron'); //实例化对象
console.log(person1.__proto__); //Person
console.log(person1.constructor); //本身试试看会是什么吧
console.log(Person.prototype); //指向原型对象Person
var person2 = new Person('Frank');
复制代码
  • 构造函数:用来在建立对象时初始化对象。特色:构造函数名通常为大写字母开头;与new运算符一块儿使用来实例化对象。
  • 原型:构造函数在建立的过程当中,系统自动建立出来与构造函数相关联的一个空的对象。能够由构造函数.prototype来访问到。
    image
  • 原型:构造函数在建立的过程当中,系统自动建立出来与构造函数相关联的一个空的对象。能够由构造函数.prototype来访问到。
  • 在实例化对象p的过程当中,系统就自动建立出了构造函数的原型,即Person.prototype.
  • 注意:每一个对象的__proto__属性指向自身构造函数的prototype;
  • constructor属性是原型对象的属性,指向这个原型对象所对应的构造函数。
  • 在实例化对象p的过程当中,系统就自动建立出了构造函数的原型,即Person.prototype(每一个对象的__proto__属性指向自身构造函数的prototype)
  • constructor属性是原型对象的属性,指向这个原型对象所对应的构造函数
  • 完整原型链图片示意图
    完整原型链图片示意图

new 关键字

// 构造函数表示方法1
// 不须要new生成实例
function Animal (name, energy) {
  let animal = Object.create(Animal.prototype)
  animal.name = name
  animal.energy = energy
  return animal
}

// 构造函数表示方法2
// 须要new生成实例
function Animal (name, energy) {
  // const this = Object.create(Animal.prototype)
  this.name = name
  this.energy = energy
  // return this
}
const leo = new Animal('Leo', 7)
const snoop = new Animal('Snoop', 10)

复制代码
  • 一般咱们使用构造函数2的方法来写一个构造函数,当new 一个关键字时,let this = Object.create(Animal.prototype)、 return this这两句代码,会在引擎会隐式调用,而且建立的对象称为thisjavascript

  • 调用构造函数,忘记new实例防错html

// 警告
function Animal (name, energy) {
  if (this instanceof Animal === false) {
    console.warn('Forgot to call Animal with the new keyword')
  }
  this.name = name
  this.energy = energy
}

// 生成实例
function Animal (name, energy) {
  if (this instanceof Animal === false) {
    return new Animal(name, energy)
  }
  this.name = name
  this.energy = energy
}
复制代码

for ... in

function Animal (name, energy) {
  this.name = name
  this.energy = energy
}
Animal.prototype.eat = function (amount) {
  console.log(`${this.name} is eating.`)
  this.energy += amount
}
Animal.prototype.sleep = function (length) {
  console.log(`${this.name} is sleeping.`)
  this.energy += length
}
Animal.prototype.play = function (length) {
  console.log(`${this.name} is playing.`)
  this.energy -= length
}
const leo = new Animal('Leo', 7)
for(let key in leo) {
  console.log(`Key: ${key}. Value: ${leo[key]}`)
}
复制代码
  • 遍历循环:循环遍历对象自己以及它委托给的原型的全部可枚举属性。由于默认状况下,您添加到函数原型的任何属性都是可枚举的,咱们不只会看到名称和能量,还会看到原型上的全部方法 - 吃,睡,玩等方法。
  • 修改只输出原型方法
for(let key in leo) {
  if (leo.hasOwnProperty(key)) {
    console.log(`Key: ${key}. Value: ${leo[key]}`)
  }
}

复制代码

hasOwnProperty

function Animal (name, energy) {
  this.name = name
  this.energy = energy
}
Animal.prototype.eat = function (amount) {
  console.log(`${this.name} is eating.`)
  this.energy += amount
}

Animal.prototype.sleep = function (length) {
  console.log(`${this.name} is sleeping.`)
  this.energy += length
}
Animal.prototype.play = function (length) {
  console.log(`${this.name} is playing.`)
  this.energy -= length
}
const leo = new Animal('Leo', 7)
leo.hasOwnProperty('name') // true
leo.hasOwnProperty('energy') // true
leo.hasOwnProperty('eat') // false
leo.hasOwnProperty('sleep') // false
leo.hasOwnProperty('play') // false
复制代码

instanceof

function Animal (name, energy) {
  this.name = name
  this.energy = energy
}
function User () {}
const leo = new Animal('Leo', 7)
leo instanceof Animal // true
leo instanceof User // false
复制代码

从新建立Object.create

  1. 它接受一个对象的参数。
Object.create = function (objToDelegateTo) { }
复制代码
  1. 它建立一个对象,该对象在失败的查找中委托给参数对象。
Object.create = function (objToDelegateTo) {
  function Fn(){}
  Fn.prototype = objToDelegateTo
  return new Fn()
}
复制代码
  • 在Object.create实现的主体内部,咱们将建立一个空函数。而后,咱们将该空函数的原型设置为等于参数对象。而后,为了建立一个新对象,咱们将使用new关键字调用咱们的空函数。若是咱们返回新建立的对象
  • 注意对于Array、Object等引用类型的数据子实例会共用
var obj = {a: 1, b: ['red', 'blue']}
var objInstance1 = Object.create(obj)
var objInstance2 = Object.create(obj)
objInstance1.b.push('green')
objInstance2.b // ['red', 'blue', 'green']

// 对于基本类型会从新赋值
如:objInstance1.a = 3
console.log(objInstance1) // {a: 3}
复制代码
  1. 它返回新建立的对象。

继承实现的方式

  • prototype模式实现继承
function Animal() {
    this.species = "动物";
}

function Cat(name, color) {
    // this 指向Cat
    Animal.apply(this, arguments);
    this.name = name;
    this.color = color;
}
// 将Cat的prototype对象指向一个Animal的实例,至关于彻底删除了prototype 对象原先的值,而后赋予一个新值
Cat.prototype = new Animal();
// 任何一个prototype对象都有一个constructor属性,指向它的构造函数。
// 若是没有"Cat.prototype = new Animal();"这一行,Cat.prototype.constructor是指向Cat的;
 // 加了这一行之后,Cat.prototype.constructor指向Animal。 
// 显然会致使继承链的紊乱(cat1明明是用构造函数Cat生成的),
// 所以咱们必须手动纠正,将Cat.prototype对象的constructor值改成Cat。
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛", "黄色");
// console.log(cat1.species)
console.log(cat1)
复制代码
  • 利用空对象做为中介(寄生组合式继承)
function extend(Child, Parent) {

    var F = function(){};
  F.prototype = Parent.prototype;

  Child.prototype = new F();

  Child.prototype.constructor = Child;

  Child.uber = Parent.prototype;
}
  
extend(Cat,Animal);

var cat1 = new Cat("大毛","黄色");

alert(cat1.species); // 动物
复制代码
  • 这种实现继承的方式与上一种比较效率要高些,避免了new Animal两次,其添加没必要要的属性。

原型链

  • 原型链:每个对象都有本身的原型对象,原型对象自己也是对象,原型对象也有本身的原型对象,这样就造成了一个链式结构,叫作原型链。
  • 以Person举例:这个例子中的p对象的原型链结构图以下:p对象----->Person.prototype------->Object.prototype--------->null
  • 对这个实例化对象而言,访问对象的属性,是首先在对象自己去找,若是没有,就会去他的原型对象中找,一直找到原型链的终点;若是是修改对象的属性,若是这个实例化对象中有这个属性,就修改,没有这个属性就添加这个属性。

__proto__属性和prototype属性的区别

  • prototype是function对象中专有的属性。
  • __proto__是普通对象的隐式属性,在new的时候,会指向prototype所指的对象;
  • __ptoto__其实是某个实体对象的属性,而prototype则是属于构造函数的属性。__ptoto__只能在学习或调试的环境下使用。

参考连接

相关文章
相关标签/搜索