一、原型
每个JavaScript对象(除了null)在创建的时候都会与之关联的另一个对象,就是我们说的原型(prototype),每一个对象都会从原型上继承属性。
function Person() {
}
const person = new Person();
// Person的原型
console.log(Person.prototype) // {constructor: ƒ}
// person实例与原型的关联
console.log(person.__proto__ === Person.prototype) // true
// 通过原型.construtor来获取构造函数
console.log(Person.prototype.constructor === Person) // true
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
由上面的例子,我们可以知道,构造函数的原型可以通过 构造函数.prototype 来获取, 而实例与原型的关系可以通过 实例.__proto__ 来获取,但值得注意的是,__proto__ 并不是ECMAS标准,仅仅是各大浏览器厂商约定成俗的规范,原型转换为构造函数可以通过 constrator 获取
二、原型链
当我们读取实例上的某个属性时,若找不到,则会继续在与之关联的原型上查找,若还找不到,则会在原型的原型上进行查找,一直找到顶点null为止
function Person() {}
const person = new Person();
console.log(person.__proto__.__proto__ === Object.prototype); // true
console.log(person.__proto__.__proto__.__proto__); // null
console.log(Object.prototype === new Object().__proto__); // true
console.log(Object.prototype.__proto__); // null
由上面可以知道,Person的原型链往上是Object.prototype,Object.prototype往上是null,即顶点就是null
function Person() {
this.age = 10;
}
const person = new Person();
console.log(person.age); // 10
delete person.age; // 删掉person实例的age属性
Person.prototype.age = 20;
console.log(person.age); // 20
delete Person.prototype.age; // 删掉Person原型上的age属性
Object.prototype.age = 30;
console.log(person.age); // 30
console.log(person.hobbit); // undefined
由上可以知道,读取属性会沿着原型链进行查找,若找不到,则返回undefined,这种一层一层的关联,组合起来就是我们说的原型链
三、不存在原型的情况
function Person() {}
// 参数1:指定原型,若为null,则没有原型
// 参数2:子集(即内部的属性)
const Test = Object.create(null,{
age:{
value:22
}
})
console.dir(Test); // 只有age属性,没有对应的原型prototype
四、构造函数上的prototype和__proto__
在js中,一切皆对象,即构造函数既可以是Function,也可以是对象,当他作为函数(构造函数)时,他有prototype属性可以使用,作为对象时,他可以调用__proto__来使用
function Person() {}
console.log(Person.prototype); // ƒ Person()
console.log(Person.__proto__); // ƒ () { [native code] }
// 定义原型上的属性
Person.prototype.show = function () {
console.log('原型prototype上的属性');
}
const person = new Person();
person.show(); // 原型prototype上的属性
Person.__proto__.show2 = function () {
console.log('__proto__上的属性');
}
Person.show2(); // 当作为对象时,可以直接调用__proto__上的属性,无需new 构造函数
4.1 构造函数的父级的父级和Object原型的关系
Object.prototype.sayHi = function () {}
function Person() {}
console.log(Person.__proto__.__proto__); // 可以看到sayHi属性
console.log(Person.__proto__.__proto__ === Object.prototype); // true
console.log(Person.sayHi());
由上可以知道,构造函数的父级的父级等于对象的原型