原型和原型链

104 阅读3分钟

前言

本次我复习原型和原型链,需要知道下面的知识点,为了防止后面的看不懂。

基本知识

构造函数

构造函数是用来创建特定类型对象的。

function Person(name,age,job){
    this.name = name
    this.age = age
    this.job = job
    this.sayName = function (){
        console.log(this.name)
    }
}
let person1 = new Person("jake",11,"前端工程师")
let person2 = new Person("jake",11,"前端工程师")
person1.sayName()//"jake"

构造函数有以下特点

  • 构造函数名称的首字母要大写,非构造函数要小写
  • 创建实例的时候,使用new操作符
  • 实例都保存着构造函数
console.log(person1.constructor === Person)

构造函数的问题: 在构造函数上面创建的方法会在每个实例上面创建一遍。就是上面的例子来说:person1和person2都有sayName()方法,但这个方法不是同一个Function实例

console.log(person1.sayName === person2.sayName)//false

因为他们都是做的同一件事,在每个实例上面重新创建会浪费内存,所以就出现了原型来解决上面的问题

原型

就是上面的示例中的sayName()方法可以直接添加到Person原型上,定义在原型上的属性和方法对于所有的实例来说是共享的,所以这样person1和person2访问的就是同一个sayName方法

const Person = function (){
    
}
Person.prototype.name = "nike"
Person.prototype.sayName=function (){
    console.log(this.name)
}
const person1 = new Person()
const person2 = new Person()
console.log(person1.sayName === person2.sayName)//true

好的这就是原型出现的意义,接下来具体来讲原型吧

原型

记住:当创建一个函数的时候,就会有prototype属性(指向原型对象)。然后这个原型对象有一个constructor属性,指回与之关联的构造函数。

function Person(){}
console.log(Person.prototype)
// {
//     constructor:f Person(),
//     __proto__:Object
// }
console.log(Person.prototype.constructor === Person)//true
const person1 = new Person()
const person2 = new Person()

__proto__:这个其实就是[[Prototype]],但是js中没有方位这个特征的标准,所以浏览器们决定给每个对象上暴露__proto__属性,通过这个属性可以访问对象的原型。创建的Person实例person1可以通过__proto__来指向Person原型

打印一下person1的原型对象

 console.log(person1.__proto__);
// {
//     constructor:f Person(),
//     __proto__:Object
// }

打印一下Person.prototype.__proto__,指向Object原型对象 原型 看下面的输出

console.log(Person.prototype.__proto__ === Object.prototype)//true
console.log(Person.prototype.__proto__.constructor === Object)//true
console.log(Person.prototype.__proto__.__proto__ === null)//true

console.log(person1.__proto__ === Person.prototype)//true
console.log(person1.__proto__.constructor === Person)//true
console.log(person1.__proto__ === person2.__proto__)//true

// 构造函数的__proto__指向:
console.log(Person.__proto__ === Function.prototype)//true

图片 Person的prototype和Person的实例person1和person2的__proto__指向原型对象,然后他们的原型对象的constructor指回了Person,原型对象的__proto__指向它的原型对象。

注意:原型对象是一个对象,然后这个constructor指向它的构造函数。

{
    constructor:,
    __proto__:
}

原型链

每个对象(object)都有一个私有属性指向另一个名为原型(prototype)的对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链(prototype chain)中的最后一个环节。

正常的原型链都会终止于Object的原型对象,Object原型的原型是null。

就是一层一层往上找。

Object和Function

//函数
const Person = new Function();
const person = new Person();

console.log(person.__proto__ === Person.prototype); // true,因为new Person()返回的是一个对象,所以在Object这条原型链上找
console.log(Person.__proto__ === Function.prototype); // true,因为是Function所以可以在Function和Object原型链上找
console.log(person.__proto__.__proto__ === Object.prototype)
console.log(Person.__proto__.__proto__ === Object.prototype)

//对象
const obj = new Object();
// 相当于 obj = {};
console.log(obj.__proto__ === Object.prototype); // true

面试题目

var F = function() {};

Object.prototype.a = function() {
  console.log('a');
};

Function.prototype.b = function() {
  console.log('b');
}

var f = new F();

f.a(); // 输出啥?
f.b(); // 输出啥?

F.a(); // 输出啥?
F.b(); // 输出啥?

知道

f.__proto__ = F.prototype

F.__proto__ = Function.prototype

f.__proto__.__proto__ = Object.prototype

所以当f.a()的时候,f身上没有就去往f.__proto__,f.__proto__.__proto__

f.b()的时候,再按原型链寻找的话找不到了,所以找不到

  1. 阐述原因
Function.prototype.a = 'a';
Object.prototype.b = 'b';
function Person();
var p = new Person();

console.log(p.a); // undefined
console.log(p.b); // b

Person 函数是 Function 对象的一个实例,所以可以访问 Function 和 Object 原型链上的内容。

而 new Person 返回的是一个对象,只能访问挂载到 Object 原型链上的内容。

所以只有 p.b