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一个新对象的过程,发生了什么?#
- 创建一个空对象 son {}
- 为 son 准备原型链连接 son.__proto__ = Father.prototype
- 重新绑定this,使构造函数的this指向新对象 Father.call(this)
- 为新对象属性赋值 son.name
- 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指定运行模式。