浅析js中的原型和原型链

前言

一、JS中的原型与原型链

1.1 原型与构造函数

1.2 __proto__ 和 prototype 的相等关系

二、原型链的构造与延伸

2.1 原型链的层级结构

2.2 图解原型链结构

三、原型与原型链的意义

3.1 共享属性与方法

3.2 覆盖原型上的属性和方法

四、结语


前言

理解JS中的原型原型链,是深入学习JavaScript的必要步骤之一。无论是实现JS继承、掌握new关键字的原理,甚至在封装组件、优化代码时,搞清楚这些概念都是前提条件。本文会用通俗易懂的语言代码实例,帮助大家快速掌握原型和原型链,并理解其存在的意义。

一、JS中的原型与原型链

首先,我们明确两个重要概念

  1. JS的对象分为两类:函数对象和普通对象。
  2. 所有对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性。

这两个概念理解起来并不难。我们一起来看看JS中的构造函数、__proto__prototype 是如何关联的。

1.1 原型与构造函数

在JS中,构造函数通过 new 关键字创建实例对象,而这些实例对象都可以访问构造函数的原型对象上的属性和方法。

        function Person(name, age) {
            this.name = name; 
            this.age = age;
        }
        Person.prototype.motherland = 'China';
        let person1 = new Person('小明', 18);
        console.log(person1.motherland); // 输出: China

上面代码中,Person.prototype 是构造函数 Person 的原型对象,它存放了所有实例共享的属性 motherland。通过构造函数创建的实例对象 person1 可以通过原型链访问到 motherland 属性。

1.2 __proto__prototype 的相等关系

我们接下来理解两个重要的准则:

  1. 准则1:原型对象(Person.prototype)的 constructor 指向构造函数本身。
  2. 准则2:实例对象的 __proto__ 属性指向构造函数的原型对象。
console.log(Person.prototype.constructor === Person); // true 
console.log(person1.__proto__ === Person.prototype); // true
  • Person.prototype.constructor 表示原型对象的 constructor 属性指向 Person
  • person1.__proto__ 指向的是 Person.prototype,即 person1 的原型对象是 Person 的原型对象。

二、原型链的构造与延伸

2.1 原型链的层级结构

原型链的本质就是当我们访问一个对象的属性时,JS引擎会先查找该对象自身的属性。如果找不到,则会顺着 __proto__ 向上查找,直到找到属性或到达 null(表示原型链的尽头)。

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

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

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

上面的代码展示了原型链的层级结构:

  1. person1.__proto__ 指向 Person.prototype,即实例的原型是构造函数的原型。
  2. Person.prototype.__proto__ 指向 Object.prototype,即所有构造函数的原型对象最终都会连接到 Object.prototype
  3. Object.prototype.__proto__ 指向 null,代表原型链的尽头。
2.2 图解原型链结构

图中的箭头表示原型链的查找方向,随着层层查找,最终原型链在 Object.prototype 终结。


三、原型与原型链的意义

3.1 共享属性与方法

原型的存在使得多个实例对象可以共享属性和方法,从而节省内存开销。我们再看一个例子:

Person.prototype.hairColor = 'black';
Person.prototype.eat = function() { 
    console.log('We usually eat three meals a day.'); 
}; 
let person2 = new Person('小花', 20); 
console.log(person1.hairColor); // 输出: black 
console.log(person2.eat()); // 输出: We usually eat three meals a day.

此时,person1person2 都通过原型链继承了 hairColoreat 方法,且这些属性方法只存在于 Person.prototype 中。这样,我们不需要为每个实例单独声明这些属性和方法,达到了代码复用的效果。

3.2 覆盖原型上的属性和方法

如果我们在实例对象上定义了与原型对象相同的属性或方法,实例会优先使用自己的属性或方法,而不会访问原型上的同名属性。这种机制称为属性遮蔽

person1.hairColor = 'yellow'; 
console.log(person1.hairColor); // 输出: yellow
console.log(person2.hairColor); // 输出: black

上面的代码中,person1 重新定义了 hairColor 属性,但这不会影响到 person2,因为 person2 仍然继承了原型对象上的 hairColor。实例上的同名属性不会修改原型上的属性。


四、结语

通过本篇文章,我们通俗地讲解了JS中的原型和原型链,重点理解了 __proto__prototype 的相等关系,以及原型链的层次结构。原型和原型链的存在意义在于:通过共享属性和方法,节省内存,并且动态扩展实例对象的功能。掌握这些基础知识后,你将更容易理解JS的继承机制和构造函数的实现原理。

如果你对本文的解释有任何疑问或建议,欢迎在评论区讨论,

最后附上一张神图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值