面试-js-原型和原型链

46 阅读1分钟

1、定义

JavaScript 中所有的对象都有一个内置属性,称为它的 prototype(原型)。它本身是一个对象,故原型对象也会有它自己的原型,逐渐构成了原型链。原型链终止于拥有 null 作为其原型的对象上。

const myObject = {
  city: "chengdu",
  greet(){
    console.log(`来自${this.city}的问候`)
  }
}
myObject.greet()
//这里greet()是myObject()的方法

除了city和greet的属性和方法之外,还有内置的其他属性

__defineGetter__
__defineSetter__
__lookupGetter__
__lookupSetter__
__proto__
city
constructor
greet
hasOwnProperty
isPrototypeOf
propertyIsEnumerable
toLocaleString
toString
valueOf

例如访问myObject.toString() myObject的原型使用Object.getPrototypeOf() 函数调出

Object.getPrototypeOf(myObject); // Object { }

有个对象叫 Object.prototype,它是最基础的原型,所有对象默认都拥有它。Object.prototype 的原型是 null,所以它位于原型链的终点:

一个对象的原型并不总是 Object.prototype,试试这段代码:

const myDate = new Date();
let object = myDate;

do {
  object = Object.getPrototypeOf(object);
  console.log(object);
} while (object);

// Date.prototype
// Object { }
// null

这段代码创建了 Date 对象,然后遍历了它的原型链,记录并输出了原型。从中我们知道 myDate 的原型是 Date.prototype 对象,Date.prototype)的原型是 Object.prototype

属性遮蔽

如果你在一个对象中定义了一个属性,而在该对象的原型中定义了一个同名的属性,会发生什么?我们来看看:

const myDate = new Date(1995, 11, 17);

console.log(myDate.getYear()); // 95

myDate.getYear = function () {
  console.log("别的东西!");
};

myDate.getYear(); // '别的东西!'

鉴于对原型链的描述,这应该是可以预测的。当我们调用 getYear() 时,浏览器首先在 myDate 中寻找具有该名称的属性,如果 myDate 没有定义该属性,才检查原型。因此,当我们给 myDate 添加 getYear() 时,就会调用 myDate 中的版本。

这叫做属性的“遮蔽”。

在 JavaScript 中,有多种设置对象原型的方法,这里我们将介绍两种:Object.create() 和构造函数。

2.创建原型

(1) 使用 Object.create Object.create() 方法创建一个新的对象,并允许你指定一个将被用作新对象原型的对象。

const personPrototype = {
  greet() {
    console.log("hello!");
  },
};

const carl = Object.create(personPrototype);
carl.greet(); // hello!

这里我们创建了一个 personPrototype 对象,它有一个 greet() 方法。然后我们使用 Object.create() 来创建一个以 personPrototype 为原型的新对象。现在我们可以在新对象上调用 greet(),而原型提供了它的实现。

(2)使用构造函数 在js中,所有函数都有一个名为prototype的属性。当你调用一个函数作为构造函数时,这个属性被设置为新构造对象的原型

//使用构造函数创建原型
const personPrototype1 = {
  greet(){
    console.log(`hello!!我是 ${this.name}`)
  }
}

function Person(name){
  this.name = name
}

//使用Object.assign()将personPrototype1的方法捆绑到Person中,成为Person的原型,自动包含greet()
//的方法
Object.assign(Person.prototype, personPrototype1)

const reuben = new Person("Reuben")
reuben.greet()

这里我们:

  • 创建了一个 personPrototype 对象,它具有 greet() 方法
  • 创建了一个 Person() 构造函数,它初始化了要创建人物对象的名字

然后我们使用 Object.assign 将 personPrototype 中定义的方法绑定到 Person 函数的 prototype 属性上。

在这段代码之后,使用 Person() 创建的对象将获得 Person.prototype 作为其原型,其中自动包含 greet 方法。