[JavaScript] ProtoType 原型链

tldr[1]
// 以下均相等
console.log(Function.constructor === Function);
console.log(Function.__proto__ === Object.__proto__);

console.log(Object.__proto__ === Function.prototype);
console.log(Function.__proto__ === Function.prototype);
console.log(Function.prototype.__proto__ === Object.prototype);

console.log(Object.prototype.__proto__ === null);
// 等同于
Object.getPrototypeOf(Object.prototype) === null

function Constructor() {}

const obj = new Constructor();
// obj ---> Constructor.prototype ---> Object.prototype ---> null

Object instanceof Function; // true
Function instanceof Object; // true

Function.prototype

  • prototype是函数独有的
  • prototype属性可以看作成一块特殊的存储空间,提供了子类使用的方法和属性

Object.__proto__

  • 使用__proto__是有争议的,也不鼓励使用它
  • __proto__属性是对象(包括函数)独有的,指向该对象的原型对象,相当于通往prototype的指针

Object.constructor

  • constructor是对象才有的属性,指向该对象的构造函数

new一个新对象的过程,发生了什么?

  1. 创建一个空对象 son {}
  2. 为 son 准备原型链连接 son.__proto__ = Father.prototype
  3. 重新绑定this,使构造函数的this指向新对象 Father.call(this)
  4. 为新对象属性赋值 son.name
  5. return this,此时的新对象就拥有了构造函数的方法和属性了
// Function 对象伪代码
function Function() {
this.constructor = Function; // 是其本身
this.__proto__  = Function.prototype;
this.prototype = Function.prototype;
}
Function.prototype = {
__proto__: Object.prototype,
}

// Object 伪代码
function Object() {
this.constructor = Function;
this.__proto__ = Function.prototype;
this.prototype = Object.prototype;
}
Object.prototype = {
__proto__: null,
constructor: Object,
}
// 构建函数伪代码
function FooBar() {
this.constructor = FooBar;
}

// 构造函数
function FooBar(foo, bar) {
// instance fields
this.foo = foo;
this.bar = bar;
// 实例化,不共享
this.instanceMethod = () => {};
}

// static fields/methods
FooBar.foobar = 'foobar';
FooBar.method = function() {};

// instance methods
FooBar.prototype.instanceMethod = () => {
console.log(this);
};
原型重新赋值注意事项
// 若给原型重新赋值,实例的__proto__会丢失构造器
FooBar.prototype = {
method: function() {}
}
ins.__proto__; // { method: [Function: method] }
ins.__proto__.constructor; // [Function: Object]

//需要重新指向构造函数
FooBar.prototype.constructor == FooBar;
ins.__proto__.constructor; // [Function: FooBar]
一般不允许直接改变 prototype 的指向,会使原生的方法都没了,如Array、String修改原型指向的时候直接报错 fields
const instance = new FooBar('foo', 'bar'); console.log(instance); // FooBar {foo: "foo", bar: "bar"}
console.log(instance.foobar); // undefined 实例无法访问 static fields
console.log(FooBar.foobar); // "foobar"
console.log(FooBar.foo); // undefined

构造器

FooBar.prototype.constructor === FooBar); // true
instance.__proto__.constructor === FooBar; // true

原型链

instance.__proto__ === FooBar.prototype; // true
FooBar.__proto__ = Function.prototype; // true;FooBar 本质上是 Function 的一个实例
FooBar.prototype.__proto__ === Object.prototype; //true;Parent.prototype 是 Object 的一个实例
Object.prototype.__proto__ === null; // true;
继承的三种写法[2]
// es5
function Son(params) {
Father.call(this, params);
// rest constructor define
// ...
}
// bad,通过改变子类原型的指向到父类,修改子类等于修改父类
Son.prototype = Father.prototyp;

// good,子类原型指向父类的实例
Son.prototype = new Father();

// even better,类似于类写法中的 extends
Object.setPrototypeOf(Son.prototype, Father.prototype);


// 修改原型
Son.prototype.foobar = "whatever";


类本质上是一个函数,是构造函数的另一种写法;

添加方法

Object.assign(Son.prototype, { foobar() {} };

与构造函数的区别

(1) 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。 (2) 类的所有实例共享一个原型对象。 (3) 类的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。

Date:
Words:
943
Time to read:
4 mins