js中原型和原型链的理解(透彻)

1、前言

1.1 什么是函数对象

函数对象就是我们平时称呼的函数,构造函数也包含其中,在这里我们说一说构造函数与普通函数的区别。

构造函数也是一个普通函数,和普通函数的主要区别在于调用方式不一样,作用也不一样(构造函数用来构建实例对象)。

普通函数:

function person(){
  console.log(this);  // Window
}
person()

构造函数:

function Person() {
  console.log(this);  // Person{}
}
let p = new Person();

1.2 什么是实例对象

ES5:

// ES5:生成实例对象的传统方法是通过构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}
 
Person.prototype.sayName = function () {
    alert(this.name);
};
 
var person1 = new Person("张三", 29);
var person2 = new Person("李四", 27);

ES6:

// ES6可通过class关键字定义类
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {
        alert(this.name);
    }
}
 
// ES6使用类时也是直接对类使用new命令
var person1 = new Person("Nicholas", 29);
var person2 = new Person("Greg", 27);

person1和person2都是Person类的实例对象(实例都是对象)

1.3 什么是原型对象

我们在创建函数的同时,浏览器会在内存中创建一个对象。这个函数中默认有一个prototype属性,指向了该对象。这个对象就是函数的原型对象,简称函数的原型。

每个构造函数都有一个原型对象存在,这个原型对象通过构造函数的prototype属性来访问。原型对象默认会有一个constructor属性指向构造函数。

1.4 构造函数、原型对象、实例对象的关系

构造函数、原型对象、实例对象的关系:

①每个构造函数都有一个prototype属性,这个属性指向了原型对象。

②每个实例对象都有一个__proto__属性,这个属性指向了对象的原型对象。

③在原型对象里有一个constructor属性,该属性指向了构造函数。

在这里插入图片描述

2、原型

在 JavaScript中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype属性,这个属性指向函数的原型对象,使用原型对象的好处是所有对象实例共享它所包含的属性和方法。

3、原型链

每个对象拥有一个原型对象,通过 proto 指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向
null(Object.proptotype.__proto__指向的是null)。这种关系被称为原型链(prototype chain),通过原型链一个对象可以拥有定义在其他对象中的属性和方法。

4、原型的相关属性及方法

三个属性:prototype、proto、constructor;
一个方法:hasOwnProperty()

★ prototype:每个函数都有一个prototype属性,这个属性指向函数的原型对象。
★ proto:每个对象(除null外)都会有的属性,叫做__proto__,这个属性会指向该对象的原型。
★ constructor:每一个原型都有一个constructor属性,指向该关联的构造函数。
prototype 和 proto 区别是什么?
● prototype是构造函数的属性
● __proto__是每个实例都有的属性,可以访问 [[prototype]] 属性
● 实例对象的__proto__与其构造函数的prototype指向的是同一个对象
★ hasOwnProperty() 方法
它的作用是判断函数的原型所在位置。一个对象本身添加的属性返回true,在原型中的属性和不存在的属性都会返回false。

//创建构造函数
function Person(){

}
//使用Person.prototype直接访问到原型对象
// 给Person函数的原型对象中添加一个属性name,值是"小明"
// Person.prototype.name = "小明";
Person.prototype = {
    constructor:Person,   //让constructor重新指向Person函数
    /*如果直接给Person的原型指定对象字面量(没有上面这一行),
      这个对象的constructor属性不再指向Person函数*/
    name: "小明"
};
 
//创建一个实例对象p1
var p1 = new Person();
 
//访问p1对象的属性name
p1.age = 18;
 
console.log(Person.prototype.constructor === Person);  // true
 
//如果在Person.prototype中没有constructor:Person这一行代码则输出flase
//Person.prototype.name = "小明";这种写法输出也是true
 
console.log(p1.__proto__ === Person.prototype);  // true
 
//如果在Person.prototype中没有constructor:Person这一行代码则输出flase
//Person.prototype.name = "小明";这种写法输出也是true
 
console.log(p1.name);  // 小明  
 
/*虽然在p1对象中没有明确的添加属性name,但是仍然可以成功打印的原因是:
  p1的__proto__属性指向的原型中有name属性,所以可以访问到属性name值。*/
 
console.log(p1.hasOwnProperty("age"));  // true
//因为age属性是直接在p1属性中添加的

console.log(p1.hasOwnProperty("name"));  // false 
//因为name属性是在原型中添加的

console.log(p1.hasOwnProperty("sex"));  // false 
//因为sex属性不存在

5、总结

  • JavaScript中的对象,都有一个内置属性[Prototype],指向这个对象的原型对象。当查找一个属性或方法时,如果在当前对象中找不到,会继续在当前对象的原型对象中查找;如果原型对象中依然没有找到,会继续在原型对象的原型中查找(原型也是对象,也有它自己的原型);直到找到为止,或者查找到最顶层的原型对象中也没有找到,就结束查找,返回undefined。这个查找过程是一个链式的查找,每个对象都有一个到它自身原型对象的链接,这些链接组建的整个链条就是原型链。拥有相同原型的多个对象,他们的共同特征正是通过这种查找模式体现出来的。
  • 在上面的查找过程,我们提到了最顶层的原型对象,这个对象就是Object.prototype,这个对象中保存了最常用的方法,如toString、valueOf、hasOwnProperty等,因此我们才能在任何对象中使用这些方法。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端~初学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值