JS系列 - 面向对象

创建对象的方式:

 

  • 字面量: var a = {};
  • new: function F() {}; var a = new F();
  • Object方法:var a = Object.create({a: 1}); 意思是a的原型是{a: 1}

 

 

对象的属性说明(对象没有prototyte属性):

例子: function F() {}; var a = new F();

  • a.constructor = F:对象、构造函数实例,都有constructor 属性,默认指向:它构造函数.prototyte.constrctor ; 构造函数的 prototype 对象 constrctor 属性指向本身;
  • a.__proto__ = F.prototyte: 对象有一个隐藏的__proto__属性(原型链),指向它的构造函数的prototype属性 
  • F.prototyte.constrctor = F; 构造函数的 prototype 的 constrctor 指向本身

 

 

函数对象:

 

  • 有原型对象prototype(普通对象没有prototype) 函数实例的__proto__属性指向构造函数的prototype;
  • 构造函数的 prototype 属性的 constructor  属性指向本身;
  • 有__proto__ 隐藏属性,  Array.__proto__ 指向 Function.prototype (Function.prototype 比较特殊,是函数)

 

new一个函数做了什么:

例子: function F ( name ) { this.name = name ; } new F();

1:创建一个新的空对象a

2:将函数的this指向a对象

3:将a对象的__proto__成员指向了构造函数F的prototype对象

3:运行该F函数

4:如果F函数没有返回值,或者返回的不是对象, 那么就返回a对象

 

 

JS继承的方式 

1. 原型链继承

原理:

1.子类的原型指向父类的实例;

2.将子类原型上的constructor属性指向本身;

3.错误的做法:Child.prototype = Parent.prototype;这样只能继承Parent.prototype上的属性,Parent方法本身的属性无法继承;

缺点:

1.无法多继承;

2.原型对象所有属性都是共享,不小心修改后会影响所有子类;

3.创建子类,无法向父类构造器传参

父类: 

function Parent () {this.name = 'Parent';  this.sex = 'boy';}; 

Parent.prototype.getName = function () { return this.name; }

子类:

function Child () {  this.name = 'child'  }

继承:

Child.prototype = new Parent();

Cat.prototype.constructor = Cat;(不加这一行:Cat.prototype.constructor 是 Animal,导致所有子类的constructor都指向Animal

var c = new Child();

child1.getName() // child 

解析:

c1通过构造函数Child生成的对象,就有属性name,并且属性值也是自己的child

Child的原型被指向了父类构造函数Parent创建出来实例,就可以使用实例的所有方法以及属性,child1.getName()就生效;

 

2. 构造函数继承(call或者apply)

原理:

在子类构造函数内部使用call或apply来调用父类构造函数;

例子:

function Parent (name) { this.name = name }
function Child () {
this.sex = 'boy'
Parent.call(this, ...arguments)
}

优缺点:

1. 可以解决原型链继承的3个缺点(无法多继承,无法向父类构造函数传参,子类共享原型对象属性)

2. 但是无法继承父类原型上的方法及属性(原型链继承可以)

 

3.组合继承

概念:组合继承就是将原型链继承与构造函数继承组合在一起,从而发挥两者之长的一种继承模式 

实现:

  1. 通过call/apply在子类构造函数内部调用父类构造函数

  2. 将子类构造函数的原型对象指向父类构造函数创建的一个匿名实例

  3. 修正子类构造函数原型对象的constructor属性,将它指向子类构造函数

例子:

function Parent (name) {

this.name = name 

}

function Child (name, sex) {

this.sex = 'boy'

Parent.apply(this, [name])

}

Child.prototype = new Parent()

Child.prototype.constructor = Child

优点:

  • 可以继承父类实例属性和方法、父类原型属性和方法

  • 解决原型链继承中引用属性的共享的问题(父级的原型链Parent.prototype上的属性还是共享

  • 可向父类构造器传参

缺点:

  • 父类构造函数会被调用两次
  • 生成了两个实例,浪费内存

 

4. 寄生组合继承 - 最屌的继承方式

与组合继承区别就在于:

组合:Child.prototype = new Parent();Child.prototype.constructor = Child

寄生:Child.prototype = Object.create(Parent.prototype)

 

 

解释Object.create:

使用了Object.create(Parent.prototype)创建了一个空的对象,并且这个对象的__proto__属性是指向Parent.prototype

Object.create(null)创建的对象,它的__proto__属性设置为null,相当于是没有原型链了,连Object.prototype上的方法它都不能用了(toString、hasProperty)

 

自己实现Object.create:

function create(proto) {

function F(){}

F.prototype = proto;

F.prototype.constructor = F;

return new F();

}

 

优点:

只调用了一次父类构造函数

 

 

5. es6 class 继承

class 中继承主要是依靠两个东西:
  • extends
  • super
效果和之前我们介绍过的寄生组合继承方式一样(就是那个最屌的继承方式)

 

6. 多继承

原理:

  • 使用Object.create继承多个父类原型属性
  • 构造函数内部使用apply调用多个父类的实例方法

 

例子:

function Parent() { }

function OtherParent() { }

function Child(...arg) {

Parent.apply(this, arg)

OtherParent.apply(this, arg)

}

Child.prototype = Object.create(Parent.prototype)

Object.assign(Child.prototype, OtherParent.prototype)

Child.prototype.constructor = Child

 

参考:https://juejin.im/post/5e75e22951882549027687f9#heading-36

 

posted @ 2017-05-09 10:49  vs1435  阅读(176)  评论(0编辑  收藏  举报