Skip to content

原型

JS 中所有的对象都有一个内置属性,称为它的 prototype(原型)

它本身是一个对象,故原型对象也会有它自己的原型,逐渐构成了原型链

原型链终止于拥有 null 作为其原型的对象上。

WARNING

指向对象原型的属性并不是 prototype。 它的名字不是标准的,但实际上所有浏览器都使用 __proto__。 访问对象原型的标准方法是 Object.getPrototypeOf()

js
const proto = {};
console.log(proto.prototype); // undefined
console.log(Object.getPrototypeOf(proto) === proto.__proto__); // true
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Object)));
const proto = {};
console.log(proto.prototype); // undefined
console.log(Object.getPrototypeOf(proto) === proto.__proto__); // true
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Object)));

Function.prototype

prototype 是函数的属性, 不是对象的属性

js
// Number Function Object 等等都是函数(构造函数)
console.log({}.prototype); // undefined
console.log((0).prototype); // undefined
console.log(Number()); // 0
console.log(new Number()); // Number {0}
console.log(Number.prototype); // Number {0, constructor: ƒ, ...}

function fn() {}
console.log(fn.prototype); // {constructor: ƒ}
console.log(fn.__proto__); // ƒ () { [native code] }
console.log(Function.prototype); // ƒ () { [native code] }
console.log(Function.__proto__); // ƒ () { [native code] }
// Number Function Object 等等都是函数(构造函数)
console.log({}.prototype); // undefined
console.log((0).prototype); // undefined
console.log(Number()); // 0
console.log(new Number()); // Number {0}
console.log(Number.prototype); // Number {0, constructor: ƒ, ...}

function fn() {}
console.log(fn.prototype); // {constructor: ƒ}
console.log(fn.__proto__); // ƒ () { [native code] }
console.log(Function.prototype); // ƒ () { [native code] }
console.log(Function.__proto__); // ƒ () { [native code] }

Object.getPrototypeOf()

  • 在 ES5 中,如果 obj 参数不是对象,则会抛出 TypeError 异常。
  • 在 ES2015 中,该参数将被强制转换为 Object (__proto__: 俺也一样会强制转换)。
js
Object.getPrototypeOf("foo"); // TypeError: "foo" is not an object (ES5 code)

// __proto__ 等同于 Object.getPrototypeOf()
"foo".__proto__ === String.prototype; // true (ES2015 code)
Object.getPrototypeOf("foo") === String.prototype; // true (ES2015 code)
Object.getPrototypeOf("foo"); // TypeError: "foo" is not an object (ES5 code)

// __proto__ 等同于 Object.getPrototypeOf()
"foo".__proto__ === String.prototype; // true (ES2015 code)
Object.getPrototypeOf("foo") === String.prototype; // true (ES2015 code)

instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

js
// 非实例对象的基本数据类型 ?
console.log(1 instanceof Number); // false
console.log(NaN instanceof Number); // false
console.log(Number(1) instanceof Number); // false

// 实例对象
console.log(Object instanceof Number); // false

console.log(new Number(1) instanceof Number); // true
console.log(Number instanceof Object); // true
console.log(Symbol instanceof Object); // true
console.log(Bigint instanceof Object); // true
// 非实例对象的基本数据类型 ?
console.log(1 instanceof Number); // false
console.log(NaN instanceof Number); // false
console.log(Number(1) instanceof Number); // false

// 实例对象
console.log(Object instanceof Number); // false

console.log(new Number(1) instanceof Number); // true
console.log(Number instanceof Object); // true
console.log(Symbol instanceof Object); // true
console.log(Bigint instanceof Object); // true

创建实例对象

js
// 使用 new 和 函数 创建实例对象,该函数则被视为构造函数
// 构造函数的 `prototype` 属性将成为实例对象的原型
function Ctor() {}
const instance = new Ctor();
console.log(Object.getPrototypeOf(instance) === Ctor.prototype); // true

// 使用 Object.create 和 对象 创建实例对象,该对象将成为实例对象的原型
const proto = {};
const obj = Object.create(proto);
console.log(Object.getPrototypeOf(obj) === proto); // true

const nu = Object.create(null);
console.log(Object.getPrototypeOf(nu) === null); // true

function Ctor() {}
const obj2 = Object.create(Ctor);
console.log(Object.getPrototypeOf(obj2) === Ctor); // true
// 使用 new 和 函数 创建实例对象,该函数则被视为构造函数
// 构造函数的 `prototype` 属性将成为实例对象的原型
function Ctor() {}
const instance = new Ctor();
console.log(Object.getPrototypeOf(instance) === Ctor.prototype); // true

// 使用 Object.create 和 对象 创建实例对象,该对象将成为实例对象的原型
const proto = {};
const obj = Object.create(proto);
console.log(Object.getPrototypeOf(obj) === proto); // true

const nu = Object.create(null);
console.log(Object.getPrototypeOf(nu) === null); // true

function Ctor() {}
const obj2 = Object.create(Ctor);
console.log(Object.getPrototypeOf(obj2) === Ctor); // true

new

在使用 new 运算符调用函数时,构造函数的 prototype 属性将成为新对象的原型。

一个具有 prototype 属性的函数也并不代表其有资格作为构造函数。

js
// 都不能使用 new,但拥有 prototype 属性
async function* asyncGeneratorFunction() {}
function* generatorFunction() {}
Symbol(1);
BigInt(1);

// 下列的函数不具有 prototype 属性,
// 因此不能成为构造函数,即便后续手动赋予了 prototype 属性:
const method = { foo() {} }.foo;
const arrowFunction = () => {};
async function asyncFunction() {}
// 都不能使用 new,但拥有 prototype 属性
async function* asyncGeneratorFunction() {}
function* generatorFunction() {}
Symbol(1);
BigInt(1);

// 下列的函数不具有 prototype 属性,
// 因此不能成为构造函数,即便后续手动赋予了 prototype 属性:
const method = { foo() {} }.foo;
const arrowFunction = () => {};
async function asyncFunction() {}

constructor

默认情况下,函数的 prototype 是一个普通的对象。 这个对象具有一个属性:constructor。它是对这个函数本身的一个引用。 constructor 属性是可编辑、可配置但不可枚举的。

如果函数的 prototype 被赋予了 Object 以外的值, 则当它被 new 运算符调用时,返回对象的原型将会指向 Object.prototype。 (换句话说,new 运算符会忽略它的 prototype 属性并构造一个普通对象。)

js
function Ctor() {}
Ctor.prototype = 3;
console.log(Object.getPrototypeOf(new Ctor()) === Object.prototype); // true
function Ctor() {}
Ctor.prototype = 3;
console.log(Object.getPrototypeOf(new Ctor()) === Object.prototype); // true

༼ つ/̵͇̿̿/’̿’̿ ̿ ̿̿ ̿̿◕ _◕ ༽つ/̵͇̿̿/’̿’̿ ̿ ̿̿ ̿̿ ̿̿