原型与原型链

原型与原型链

首先,可以肯定的是,原型,是一个对象(这是一句废话)。

一、原型与构造函数

《JavaScript高级程序设计》是这样说的:

无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。默认情况下,所有原型对象自动获得一个 constructor(构造函数)属性,这属性是一个指向 prototype 属性所在函数的指针(构造函数.prototype.constructor = 构造函数)。

当然,这个描述是有一点问题的,因为这本书描述的 js 是ES5版本及以下的,没有包括 ES6。

ES6 里的箭头函数和使用对象字面量创建对象时的简写方法,都没有 prototype 属性

let obj = {
	fun1() {},
	fun2: ()=>{},
	fun3: function() {}
};
console.log(
	obj.fun1.prototype, // undefined
	obj.fun2.prototype, // undefined
	obj.fun3.prototype  // {constructor: f} 
);

所以,prototype 这个属性,可以理解为是针对构造函数的。虽然上例的 obj.fun1 依旧能够作为一个构造函数。

二、原型与实例

既然有原型和构造函数有关系,那么原型自然和构造函数的实例也有关系。

类似的,用构造函数的创造出的实例,也有一个指针指向原型,这个指针是一个内部属性 [[Prototype]],正常说我们是访问不到的,但是浏览器都把它具现了出来,这个指针就是__proto__,ES6 也把它写入了标准,但是不推荐使用。

现在就是:

let obj = new Object();
console.log(obj.__proto__ == Object.prototype)

那么原型有什么用呢?《JavaScript高级程序设计》是这样说的:

每当代码读取某个对象的某个属性时,都会指向一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值。如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性中的值。

说白了就是,自己没有的属性,看看原型有没有

PS: 不是所有对象都有原型,比如 Object.create(null) 创造出来的就是一个纯粹的空对象,没有原型没有 __proto__ 指针。

还有,Object.prototype 也是一个没有原型的对象,或者说浏览器将它的 __proto__ 指针设为访问器属性进行了拦截,一访问就为 null,可以视作它没有原型

拓展:Function 与 Object 的关系

  1. Function 作为一个实例,是 Function 的实例。所以 Function.__proto__ == Function.prototype
  2. Object 作为一个实例,是 Function 的实例。所以 Object.__proto__ == Function.prototype
  3. Function 作为一个构造函数,它的原型是 Object 的实例。所以 Function.prototype.__proto__ == Object.prototype

分析以下代码:

// Function 作为一个实例,是 Fuction 的实例
console.log (Function.__proto__ == Function.prototype)  // true
// Object 作为一个实例,是 Fcuntion 的实例
console.log (Object.__proto__ == Function.prototype)  // true
// Fucntion 作为一个构造函数,它的原型是 Object 的实例
console.log (Function.prototype.__proto__ == Object.prototype) // true

三、原型链

如果,A 的原型是 B 的实例,那么 A.原型.原型 = B.原型,一个链关系就出来了。

根据原型的作用,当访问 A 上的一个属性,没找到,就去 A 的原型上找,在 A 的原型找不到,又因为 A 的原型还有原型,所以可以继续去 A 的原型的原型上找。

所以,原型链就成了一个类似作用域链不断往上寻找标识符的东西了

注意:因为所有的对象都是 Object 的实例,所以原型链的尽头就是 Object 的原型 Object.prototype。因为Object.prototype.__proto__ == null,所以也可以说原型链的尽头是 null 。

四、继承

说了原型链,那么自然要说原型链的核心作用——继承。

我们通过将 A 的原型指定为 B,那么 A 就可以通过原型链来继承 B 的属性与方法。

不过这种继承存在一些问题:

  1. 继承到的属性是全部实例共享
  2. 创造子类型实例时,不能向超类型的构造函数中传递参数(子类继承父类时,不能特例化父类)

1. 借用构造函数继承

let superType = function(a) {
	this.a = a;
}
let subType = function(a, b) {
	superType.call(this, a);
	this.b = b;
}
let obj = new subType(1, 2);

简介:在子类型的构造函数内部用 call 去调用超类型的构造函数
优点:可以在子类型构造函数中向超类型函数传递参数
缺点:无法做到函数复用

2. 组合继承

let superType = function(a) {
	this.a = a;
}
superType.prototype.fun1 = function(){};

let subType = function(a, b) {
	// 继承属性
	superType.call(this, a);
	this.b = b;
}
// 继承方法
subType.prototype = new superType();
subType.prototype.constructor = subType;
subType.prototype.fun2 = function(){};


let obj = new subType(1, 2);

简介:原型链继承 实现方法继承,借用构造函数继承 实现属性继承
优点:完美结合了 原型链继承 和 构造函数继承 的优点
缺点:调用了两次超类型构造函数

3. 寄生组合式继承

let superType = function(a) {
	this.a = a;
}
superType.prototype.fun1 = function(){};

let subType = function(a, b) {
	// 继承属性
	superType.call(this, a);
	this.b = b;
}
// 继承方法
subType.prototype = Object.create(superType.prototype)
subType.prototype.constructor = subType;
subType.prototype.fun2 = function(){};

简介:组合式继承的改进。原型链继承方法时,只对原型进行操作继承。
优点:开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

4. ES6 class 继承

最后的呈现效果和 寄生组合式继承 比较相似:

  1. 子类实例继承的父类实例的属性
  2. 子类实例的原型指向了一个用父类实例原型创建的空对象,实现方法继承
4.1 class 继承和 寄生组合式继承 的不同
  1. 实例属性继承机制的不同:
    • 寄生组合式继承:先创建子类实例 this,再执行父类构造函数来修改 this
    • class 继承:先创建父类实例 this,再执行子类构造函数来修改 this
  2. 静态属性、方法的继承:
    • class 通过将 子类.__proto__ = 父类 实现父类静态属性、方法的继承
  3. 原生构造函数的继承的不同:
    • ES5 无法继承原生构造函数,比如 Array、Object
    • class 可以继承原生构造函数来定义子类
4.2 class 的两条继承链

class 的继承有一点特殊,就是它有两条继承链:

子类.__proto__ = 父类(子类继承父类静态属性
方法)
子类.prototype.__proto__ = 父类.prototype(子类实例继承父类实例)

这两条继承链可以这样理解:

  1. 作为一个对象,子类的原型是父类
  2. 作为一个构造函数,子类的原型是父类的实例
4.3 super 关键字

这个关键字比较特殊,既可以当构造函数用,也可当对象使用。

  1. 构造函数:在子类的构造函数里,作为父类的构造函数被调用
  2. 对象:
    2.1:在子类方法里用 super 调用父类方法时,super 指向父类原型
    2.2:在子类静态方法里用 super 调用父类静态方法时,super 指向父类
    2.3:在子类方法里通过 super 对某个属性赋值,super 指向子类实例本身(super 就是 this
posted @ 2020-09-13 17:35  树干  阅读(248)  评论(0)    收藏  举报