面试官:聊聊js原型

一分钟了解原型对象

js分为函数对象和普通对象 ,每个对象都有__proto__属性,但是只有函数对象才有prototype属性,prototype属性就是函数的原型对象。

比如说 构造函数通过new 实化一个实例对象,实例对象的__proto__ 指向原型对象 ,同时构造函数prototype也指向原型对象。

比如:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHello = function() {
  console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old.");
}

var person1 = new Person("Alice", 25);
var person2 = new Person("Bob", 30);

person1.sayHello(); // 输出:Hello, my name is Alice and I am 25 years old.
person2.sayHello(); // 输出:Hello, my name is Bob and I am 30 years old.

Person是一个函数,也是构造函数,他的prototype就是指向原型对象。我们使用 Person.prototype 对象来为每个 Person 对象定义了一个共同的方法 sayHello。这个方法输出了一个字符串,表示人物的名字和年龄。

我们使用 new 关键字来创建了两个 Person 对象,并且分别调用了 sayHello 方法,输出了不同的字符串。这是因为 person1 和 person2 都继承了 Person.prototype 对象上的 sayHello 方法,但是它们的属性值是不同的。

原型对象到底是什么?

可以将原型对象比做是一个模板,这个模板包含了一些常用属性和方法,然后我们可以用它来创建新的对象。就像是我们可以用一个模板来快速制作许多同样的产品一样,每个产品的具体细节可能不同,但是它们的基本结构和特征都是相同的。

比如说,我们可以有一个“车”的原型对象,它包含了一些常用的属性和方法,比如车型、品牌、最高时速、加速度等等。然后我们可以用这个原型对象来创建许多不同的车型,如轿车、卡车、摩托车等等。由于这些车型都基于同一个原型对象创建,因此它们都具有相同的基本特征和结构,但是具体的细节可能略有不同,比如轿车和卡车的尺寸、载重等等。

同样地,在JavaScript中,我们可以使用原型对象来快速创建多个对象,并且这些对象都具有相同的基本特征和方法。这种基于原型对象的方式可以实现代码的重用,提高开发效率。

原型对象能干什么

上面的例子已经说明,我们可以在函数的prototype熟悉,也就是原型对象上添加熟悉和方法,这些都会被将来根据这个函数new出来的对象继承。

原型对象在JavaScript中有着重要的作用,常常用于以下几个方面:

实现继承:通过原型对象,我们可以创建新的对象,并且继承原型对象上的属性和方法。这种基于原型的继承方式可以让我们更方便地实现面向对象编程。

共享属性和方法:通过将属性和方法定义在原型对象上,我们可以让多个对象共享这些属性和方法。这样可以减少内存占用,提高代码的效率。

扩展对象:通过修改原型对象,我们可以在运行时扩展对象的属性和方法。这种动态扩展对象的方式可以让我们更加灵活地处理对象。

实现一些常用的方法和功能:原型对象上已经包含了一些常用的方法和功能,比如 toStringvalueOf 等等。通过在原型对象上定义相应的方法,我们可以为对象实现这些常用方法,以便在开发过程中更加便捷地使用。

举个栗子:

function Person() {}

Person.prototype.sayHello = function() {
  console.log("Hello, I'm a person.");
}

function Student() {}

Student.prototype = Object.create(Person.prototype);

const alice = new Student();

alice.sayHello(); // 输出:Hello, I'm a person.

在这个例子中,我们通过原型链访问了alice对象的原型对象,然后通过它的原型对象调用了sayHello方法。由于Student函数的原型对象继承自Person函数的原型对象,因此alice对象也继承了Person函数的原型对象上的方法和属性。

proto

__proto__是JavaScript中的一个内置属性,它指向对象的原型。在JavaScript中,每个对象都有一个原型对象,这个原型对象包含了对象的所有属性和方法,用来定义对象的共同属性和方法。__proto__属性就是用来表示对象的原型的。

例如,我们可以使用以下代码来创建一个简单的对象,然后查看其__proto__属性:

const person = {
  name: "Alice",
  age: 25
};

console.log(person.__proto__); // 输出:Object {}

上面的代码中,我们定义了一个名为person的对象,它有两个属性:name和age。然后,我们使用console.log()方法输出了person.__proto__属性,这个属性指向的是这个对象的原型对象,也就是Object对象。

需要注意的是,__proto__是一个非标准的属性,在ECMAScript 2015 标准中已经不推荐使用。应该使用Object.getPrototypeOf()方法或Object.setPrototypeOf()方法来操作对象的原型。例如,我们可以使用以下方法来获取一个对象的原型:

const person = {
  name: "Alice",
  age: 25
};

console.log(Object.getPrototypeOf(person)); // 输出:Object {}

上面的代码中,我们使用Object.getPrototypeOf()方法来获取person对象的原型,它返回的是一个Object对象。

__proto__有什么作用

查看对象的原型

我们可以使用__proto__访问一个对象的原型,以便了解它的继承关系。例如:

function Person() {}

const alice = new Person();

console.log(alice.__proto__); // 输出:Person {}

在这个例子中,我们创建了一个Person函数并使用它创建了一个alice对象。然后,通过alice.__proto__访问了alice对象的原型,这个原型就是Person函数的原型对象。

创建对象的原型链

我们可以使用__proto__来创建对象的原型链。例如:

function Person() {}

const alice = {
  name: "Alice"
};

alice.__proto__ = Person.prototype;

console.log(alice instanceof Person); // 输出:true

在这个例子中,我们定义了一个空对象alice,然后将它的原型链指向了Person函数的原型对象。由于alice对象的原型链继承了Person函数的原型,因此它可以被认为是Person类型的实例。

修改对象的原型

我们可以使用__proto__来动态地修改一个对象的原型对象。例如:

function Person() {}

const alice = new Person();

alice.__proto__ = {
  sayHello() {
    console.log("Hello, I'm a new object.");
  }
};

alice.sayHello(); // 输出:Hello, I'm a new object.

在这个例子中,我们创建了一个Person对象alice,然后通过alice.__proto__动态地修改了它的原型对象,将它的原型对象替换成一个新的对象。最后,调用alice.sayHello()方法时输出了Hello, I'm a new object.,说明alice对象的原型已经被成功替换。

原型链

当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它的’_ _ proto_ _'属性(也就是它的构造函数的’prototype’属性)中去寻找。这个寻找的链路就是原型链。

看一个例子:

  // 构造函数
  function Foo(name,age){
    this.name=name;
    this.age=age;
  }
  Object.prototype.toString=function(){
   //this是什么要看执行的时候谁调用了这个函数。
   console.log("I'm "+this.name+" And I'm "+this.age);
  }
  var fn=new Foo('小明',19);
  fn.toString(); //I'm 小明 And I'm 19
  console.log(fn.toString===Foo.prototype.__proto__.toString); //true
  
  console.log(fn.__proto__ ===Foo.prototype)//true
  console.log(Foo.prototype.__proto__===Object.prototype)//true
  console.log(Object.prototype.__proto__===null)//true

来个图帮助理解。

总结

1、所有的引用类型(数组、函数、对象)都可以通过原型可以自由扩展属性(除null以外)。

2、所有的引用类型都有一个’_ _ proto_ _'属性(也叫隐式原型,它是一个普通的对象)。

3、所有的函数都有一个’prototype’属性(这也叫显式原型,它也是一个普通的对象)。

4、所有引用类型,它的’_ _ proto_ _'属性指向它的构造函数的’prototype’属性。(结合2和3)

5、当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它的’_ _ proto_ _'属性(也就是它的构造函数的’prototype’属性)中去寻找。这就是原型链。

 

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>