原型

每个实例对象都有一个私有属性 (proto) 指向它的构造函数的原型对象 (prototype) ,所有可以使用原型对象身上的方法.
该原型 prototype 也有一个自己的私有原型(proto)指向上一级。层层向上直到为null

原型链

栗子

  1. "string".__proto__ === String.prototype // true
  2. String.prototype.__proto__ === Object.prototype //true
  3. Object.prototype.__proto__ === null
  4. let obj = {};
  5. obj.__proto__ === object.prototype;
  6. let arr = [];
  7. arr.__proto__ === Array.prototype;
  8. let str = '字符串'
  9. str.__proto__ === String.prototype;
  10. arr.__proto__ === Array.prototype //true
  11. Array.prototype.__proto__ === Object.prototype //true
  12. arr.__proto__.__proto__ === Object.prototype //true
  13. // 原型链的终点
  14. Object.prototype.__proto__ === null //true
  • 每一个原型自低向上连接起来就是原型链

image.png

原型链继承

  1. function Super(age) {
  2. this.age = age;
  3. this.list = [10, 20, 40];
  4. }
  5. function Person(name) {
  6. this.name = name;
  7. }
  8. Person.prototype = new Super(10);
  9. let p1 = new Person("三")
  10. let p2 = new Person("四")
  11. p1.age === p2.age // true
  12. p1.list.push("add");
  13. p2.list === [10, 20, 40,"add"];
  • 优点
    • 父类的方法可以复用,比如age = 10;
    • 父类新增的原型方法和原型属性,子类都能访问到
  • 缺点
    • 父类的引用属性会被所有实例共享
    • 子类构建实例时不能向父类传递参数。比如p2.age不能变成30

      构造函数继承

      ```javascript function Super(age) { this.age = age; }

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

function Person (name,age) { Super.call(this,age) this.name = name; }

let p1 = new Person(“三”,10) let p2 = new Person(“四”)

p1.name === “三”// true p1.age === 10 // true p2.name = “四”; p2.age === undefined // true

p1.getName() // TypeError

  1. - 优点
  2. - 父类的引用属性不会被共享,也就是不会被修改
  3. - 子类构造实例时,可以向父类传递参数
  4. - 缺点
  5. - 父类的方法不能复用
  6. - 子类实例的方法每次都是单独创建的
  7. <a name="sXVKB"></a>
  8. # 合并,组合继承
  9. 顾名思义,就是把前面两种方法组合起来
  10. ```javascript
  11. function Parent() {
  12. this.bo = 10;
  13. this.list = [1, 2, 3];
  14. }
  15. Parent.prototype.func = function () {
  16. console.log(this.bo)
  17. }
  18. function Crr() {
  19. Parent.call(this)
  20. }
  21. Crr.prototype = new Parent(); // 继承方法
  22. Crr.proptotype.constructor = Crr; // 修改构造函数的指向
  23. let c1 = new Crr();
  24. let c2 = new Crr();
  25. c1.bo = 1000
  26. c1.func() // 1000
  27. c1.func = "修改原型方法"
  28. c2.func() // 10
  29. c1.list.push("add")
  30. c2.list // [1, 2, 3];
  • 优点
    • 父类的方法和属性可以被复用;
    • 子类对于父类的引用属性不会被共享。// 叫多态?对吗
    • 子类构建实例时还可以自定义参数
  • 缺点
    • 调用了两次父类的构造函数第一次call它。第二次nwe它
    • Parent 被执行了两次

      解决上述的两次执行

function Crr() {
  Parent.call(this)
}
Crr.prototype =  Parent.prototype; // 继承方法
  • 实际上,子类的继承就是源于父类的原型。是同一个
  • 缺点
    • 构造函数指向不对,实例的构造函数执行仍是父类

原型式继承

通过es5的create方法实现


var person = {
    name: 'kevin',
    friends: ['daisy', 'kelly']
}

var person1 = Object.create(person);
var person2 = Object.create(person);

person1.name = 'person1';
console.log(person2.name); // kevin

person1.firends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]
  • 优点
    • 父类的的方法可以复用
  • 缺点
    • 子类构建实例时不能向父类传参
    • 多个实例的引用类型属性共同指向相同的内存,既父级的属性

寄生式继承

使用原型式继承可以获得一份目标对象的浅拷贝,然会利用这个浅拷贝的能力再进行增强,比如添加一些方法

function createObj(obj) {
  let clone = Object.create(obj);
  clone.sayName = function () {
    console.log("name", "this.name -> ", this.name)
  }
  return clone;
}

var person = {
  name: "ok",
  age: 10
};

let n1 = createObj(person);
let n2 = createObj(person);
n1.sayName();// ok
n1.name = "不ok"
n1.sayName(); //不ok
n2.sayName() // ok
  • 使用create方法,依赖于传入的对象
  • 调用了两次父类的构造函数。

    合并,寄生组合式继承

    在前几种继承的基础上进行优化,得到了寄生组合式继承。也是所有继承方式里面相对最优的继承方式 ```javascript

function createProto(ChildClass, SuperClass) { ChildClass.prototype = Object.create(SuperClass.prototype); ChildClass.prototype.constructor = ChildClass; ChildClass.proto = SuperClass; }

function Super() { this.age = 10; this.list = [1, 3, 4]; this.func = function () { console.log(this.list); } } Super.prototype.printAge = function () { console.log(this.age); }

function Child() { Super.call(this); this.print = function () { console.log(“Child 给的方法”); } }

createProto(Child, Super);

let s1 = new Child(); s1.print(); s1.printAge(); console.log(s1.constructor); // —> child


- 我还能说什么呢,JavaScript真烦人
- 这是一种完美的继承方式,extends 也是基于此方式
<a name="LVMG7"></a>
# ES6 ,extends
```javascript
class Parent {
    constructor(name) {
      this.name = name;
    }
    printName() {
      console.log(this.name);
    }
  }

  class Child1 extends Parent {
    constructor() {
      super("child1")
    }
  }

  class Child2 extends Parent {
    constructor() {
      super("child2")
    }
  }

  const c1 = new Child1();
  const c2 = new Child2();


  c1.printName();
  c2.printName();

上面的代码是通过es6的 extends 实现的继承

function _inheritsLoose(subClass, superClass) {
  subClass.prototype = Object.create(superClass.prototype);
  subClass.prototype.constructor = subClass;
  subClass.__proto__ = superClass;
}

var Parent = function () {
  function Parent(name) {
    this.name = name;
  }

  var _proto = Parent.prototype;

  _proto.printName = function printName() {
    console.log(this.name);
  };

  return Parent;
}();

var Child1 = function (_Parent) {
  _inheritsLoose(Child1, _Parent);

  function Child1() {
    return _Parent.call(this, "child1") || this;
  }

  return Child1;
}(Parent);

var Child2 = function (_Parent2) {
  _inheritsLoose(Child2, _Parent2);

  function Child2() {
    return _Parent2.call(this, "child2") || this;
  }

  return Child2;
}(Parent);

var c1 = new Child1();
var c2 = new Child2();
c1.printName();
c2.printName();

可以看到, extends 实现的继承,经过babel编译之后还是基于寄生组合式继承来实现的

ES6 extends - prototype

class A { }
class B extends A { }

const a = new A();
const b = new B();

a.__proto__ === A.prototype
b.__proto__ === B.prototype
B.__proto__ === A
B.__proto__ === A
b.__proto__.__proto__ === B.prototype.__proto__=== A.prototype

Object.create

const superObjects = {
  bool: false,
  kos: '吃啥'
};

const s1 = Object.create(superObjects, {
  newName: {
    enumerable: false,  // 不可以被枚举
    configurable: false, // 不可以被删除
    writable: false, // 不可以被修改
    value: "defaultValue"
  }
});

new 一下

new 关键字执行之后总是返回一个对象,要么是实例对象,要么是return语句提供的对象

  • new 关键字发生以下操作
    • 创建一个空的简单对象,既({})
    • 链接该对象,设置对象的constructor到另一个对象
    • 将新创建的对象作为this的上下文
    • 如果该函数没有返回对象,则返回this ```javascript function Persion(a, b) { let obj = {}; obj.a = a; obj.b = b; obj.proto.constructor = Persion; return obj } let p = Persion(“a”, “b”); console.log(p);

function newCopy(base,…args) { let obj = Object.create(base.prototype); let r = base.call(obj,…args) return r; } ```