原型链 & 继承

原型链

1)定义对象的几种方法

结果打印:

2)原型链

① 只要是对象就是实例;
② new 函数 就叫做构造函数;
③ 函数在声明的时候都js会给一个属性prototype 指向原型对象

      M.prototype.construct === M
      obj3.__proto____ === M.prototype
      obj3.constructor === M
⑤ Object.prototype是整个原型链的顶端
⑥ 如果我想增加一个say方法,可以直接在M方法上 this.say=function(){}增加,这样每个实例都会拷贝一份say方法,如果嫌占内存没必要,也可以在M的原型链增加,继而实例也是可以找到这个方法的。也就是说,任何一个实例对象通过原型链找到它上面的原型对象,原型对象上面的方法和属性都是被实例所共享的(原理)。

⑦只有函数有prototype 实例对象有__proto______,但是函数也有__proto______(因为它也是一个对象) M.__proto____ === Function.prototype 就是说 M的构造函数是Function 也可以说M这个普通的函数是构造函数Function的一个实例对象

3)instanceof

①左边 实例对象 instanceof 右边 构造函数

② 原理:判断这个实例对象是不是构造函数的实例,其实是 判断左侧实例对象的__proto____ 和 构造函数的prototype指向的是不是同一个引用 在同一条原型链上就可以

所以也可以说obj3是Object的实例 同一条原型链上都可以;
通过 obj3.__proto____.constructor === M | obj3.constructor === M; 通过constructor可以来精确的判断是不是哪个实例

4)new 运算符

new 关键字会进行如下操作:

1、创建一个空的JavaScript对象(即{});
2、将函数的 prototype 赋值给对象的 proto属性 ;
3、调用函数,并将步骤1新创建的对象作为函数的this上下文 ;
4、如果该函数没有返回值或者返回值不是对象,则返回创建的对象,如果返回值是对象,则直接返回该对象。

JS引擎执行这句代码时,在内部做了很多工作,用伪代码模拟其工作流程如下:

1
2
3
4
5
6
new Person("John") = {
var obj = {};
obj.__proto__ = Person.prototype; // 此时便建立了obj对象的原型链: obj->Person.prototype->Object.prototype->null
var result = Person.call(obj,"John"); // 相当于obj.Person("John")
return typeof result === 'object' ? result : obj; // 如果无返回值或者返回一个非对象值,则将obj返回作为新对象
}

相关文章:
https://juejin.im/post/5d65e03b6fb9a06b2c32a00c
https://juejin.im/post/5b397b526fb9a00e5d7999a4

继承

1)构造函数继承

打印结果:

这里可以看到,没有继承父类的say方法

原理:通过构造函数实现继承(call)
缺点:父类上的属性在构造函数里的都可以继承,但父类原型对象上的方法Parent.prototype 继承不到。

2)原型链继承

打印结果:

缺点:

总结: 当实例多个对象时,改变其中一个 另一个也改变。原因 这2个实例引用的原型对象是共用的。

3)组合方式

打印结果:

缺点:上图红色框中构造函数执行了2次,执行一次父类的属性子类已经有了

4)Object.create

上面代码中,Object.create 方法以A对象为原型,生成了B对象。B继承了A的所有属性和方法。

实际上,Object.create 方法可以用下面的代码代替.

1
2
3
4
5
Object.create = function (obj) {
function F() {}
F.prototype = obj;
return new F();
};

上面代码表明,Object.create 方法的实质是新建一个空的构造函数 F ,然后让 F.prototype 属性指向参数对象 obj ,最后返回一个 F 的实例,从而实现让该实例继承 obj 的属性。

或者

1
2
3
4
5
Object.create = function(obj){
var B = {};
Object.setPrototypeOf(B, obj);
return B;
}

或者

1
2
3
4
5
Object.create = function(obj){
var B = {};
B.__proto__ = obj;
return B;
}

object.create方法生成的新对象,动态继承了原型。在原型上添加或修改任何方法,会立刻反映在新对象之上。

1
2
3
4
5
6
var obj1 = { p: 1 };
var obj2 = Object.create(obj1);

obj1.p = 2;
obj2; // {}
obj2.p // 2

上面代码中,修改对象原型obj1会影响到实例对象obj2。

Object.create 第一个参数

如果想要生成一个不继承任何属性(比如没有toString和valueOf方法)的对象,可以将 Object.create的参数设为 null。

1
var obj = Object.create(null);

Object.create 第二个参数

除了对象的原型,Object.create 方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var obj = Object.create({}, {
p1: {
value: 123,
enumerable: true,
configurable: true,
writable: true,
},
p2: {
value: 'abc',
enumerable: true,
configurable: true,
writable: true,
}
});

// 等同于
var obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';

Object.create 原文链接