原型
每个实例对象都有一个私有属性 (proto) 指向它的构造函数的原型对象 (prototype) ,所有可以使用原型对象身上的方法.
该原型 prototype 也有一个自己的私有原型(proto)指向上一级。层层向上直到为null
原型链
栗子
"string".__proto__ === String.prototype // true
String.prototype.__proto__ === Object.prototype //true
Object.prototype.__proto__ === null
let obj = {};
obj.__proto__ === object.prototype;
let arr = [];
arr.__proto__ === Array.prototype;
let str = '字符串'
str.__proto__ === String.prototype;
arr.__proto__ === Array.prototype //true
Array.prototype.__proto__ === Object.prototype //true
arr.__proto__.__proto__ === Object.prototype //true
// 原型链的终点
Object.prototype.__proto__ === null //true
- 每一个原型自低向上连接起来就是原型链
原型链继承
function Super(age) {
this.age = age;
this.list = [10, 20, 40];
}
function Person(name) {
this.name = name;
}
Person.prototype = new Super(10);
let p1 = new Person("三")
let p2 = new Person("四")
p1.age === p2.age // true
p1.list.push("add");
p2.list === [10, 20, 40,"add"];
- 优点
- 父类的方法可以复用,比如age = 10;
- 父类新增的原型方法和原型属性,子类都能访问到
- 缺点
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
- 优点
- 父类的引用属性不会被共享,也就是不会被修改
- 子类构造实例时,可以向父类传递参数
- 缺点
- 父类的方法不能复用
- 子类实例的方法每次都是单独创建的
<a name="sXVKB"></a>
# 合并,组合继承
顾名思义,就是把前面两种方法组合起来
```javascript
function Parent() {
this.bo = 10;
this.list = [1, 2, 3];
}
Parent.prototype.func = function () {
console.log(this.bo)
}
function Crr() {
Parent.call(this)
}
Crr.prototype = new Parent(); // 继承方法
Crr.proptotype.constructor = Crr; // 修改构造函数的指向
let c1 = new Crr();
let c2 = new Crr();
c1.bo = 1000
c1.func() // 1000
c1.func = "修改原型方法"
c2.func() // 10
c1.list.push("add")
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; } ```