稀土掘金技术社区 2024年12月20日
【建议收藏】一文搞懂JS原型与原型链!!!!!
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了JavaScript中对象的创建方式,包括对象字面量、new Object()、构造函数等,探讨了原型、隐式原型__proto__、原型链及相关概念,如基本类型和引用类型等。

对象是复合数据类型,可通过多种方式创建,最常用字面量方式。

构造函数可创建多个对象,避免重复代码,但new出的对象属性和方法独立,可能浪费内存。

每个对象都有原型prototype,可将公共方法放到原型对象上共享,通过__proto__构成原型链。

Object.prototype是原型链顶点,其__proto__指向null,原型链通过__proto__查找属性和方法。

原创 狂炫一碗大米饭 2024-12-20 08:30 重庆

点击关注公众号,“技术干货” 及时达!

作为一个初入前端的小白,原型链的概念太多,一直觉得难以理解,对整个原型链的了解非常模糊。理解原型链是深入学习js的一小步,在参考诸多大佬文章后,整理笔记如下:

一,原型

原型与原型链,首先我们要知道什么是原型。但在开始了解原型之前,先认识下js中的对象。

对象

对象(Object)是一种复合数据类型,它是一种无序的「键值对」集合。对象用于存储和传递多个值,每个值都有一个键(key)与之关联。

「对象的特点」

创建对象

有多种方式可以创建对象:

对象字面量方式

这是最常见、最简洁的方式,用 {} 包裹键值对:

 let person = {      name: 'John',      age: 30,      greet: function() {        console.log('Hello ' + this.name);      }    };

使用 new Object() 创建对象

你也可以使用 new Object() 来创建对象:

   let person = new Object();    person.name = 'John';    person.age = 30;    person.greet = function() {      console.log('Hello ' + this.name);    };

这种方式与字面量方式相比少用了几行代码,但不常用,因为字面量语法简洁直观。

使用构造函数创建对象

你可以通过定义构造函数来创建对象。构造函数是一个用于初始化对象的特殊函数:


function Person(name, age) { this.name = name; this.age = age; this.greet = function() { console.log('Hello ' + this.name); }; }
let person1 = new Person('John', 30); let person2 = new Person('Alice', 25);

日常中我们最常用的应该是字面量方式,那为什么会出现第三种构造函数方式呢?

想想看我们需要多个obj1,obj2的时候,字面量方式我们需要重复代码去创建对象;而使用构造函数的方式只需要写一遍属性和方法,我们就可以通过new关键字,new出多个不同的对象。

试试在控制台打印如下person1.greet === person.greet,false  看起来调用的是同一个函数方法,实际并不相等。因为他们的内存地址是不同的,每个new出来的person1 和 person2 都包含一份独立的属性和方法(可能导致浪费内存)

  function Test(){        this.name = 'rose'        this.say = function(){            console.log('我能说话')        }    }    var obj3 = new Test()    var obj4 = new Test()         obj3.say === obj4.say // false    obj3.name === obj4.name // true

你可能会问为什么obj3.name和obj4.name是相等的呢?刚才不是说的内存不同独立的属性和方法吗?

为什么是 true

比较:「基本类型」「引用类型」

那就是说每实例化一个对象都会创建一个新的say这未免太浪费内存了吧!有没有什么解决方法?

原型

上面的例子中, obj3 和 obj4 都需要调用 Test 中的say()方法,我们有没有办法将公共方法放到一个公共的地方呢?这时候有请公共的原型(prototype)登场

在js中,每一个对象(函数也是对象)都有一个特殊的属性叫做原型(prototype),它指向另一个对象,这个对象(Test.prototype)被称为原型对象, 原型对象是用来共享属性和方法的

原型对象:

(1)原型对象有一个constructor属性指向构造函数本身(Test)。

(2),原型对象是一个普通的对象,它包含属性和方法。

(3),原型对象的属性和方法会被继承到所有通过原型链与它相连的对象。

简单来说,原型对象是一个普通对象,属性和方法会被继承到其他对象,而每个对象都有一个原型(prototype),用来继承其他对象的属性和方法此时,我们就可以把say方法放到这个原型对象上, obj3 和 obj4 就可以访问这个方法,再不用写到Test中去重复占用内存,所有new出来的实例都可以使用此方法

  function Test() {     this.name = 'rose';    }    // 将 say 方法添加到原型上    Test.prototype.say = function() {     console.log('我能说话');    };    var obj3 = new Test();    var obj4 = new Test();    console.log(obj3.say === obj4.say);  // 输出: true

在这种情况下,obj3.sayobj4.say 都指向同一个函数实例,因此 obj3.say === obj4.say 返回 true

构造函数和实例之间就初步构成了这样一个关系,如图:

二,隐式原型__proto__

「proto」

在js中,每个对象都有一个“ 「proto」 ”属性(左右两边两个短下划线),这个__proto__就被称为隐式原型。(记住这点)

实例对象当然也是对象,也存在__proto__属性

console.log(obj3.「proto」 === Test.prototype)// true打印以上obj3.「proto」 === Test.prototype结果为true,所以:

(1)每个js对象都有一个隐藏的原型对象属性__proto__,它指向创建它的构造函数的原型对象(Test.prototype)

(2)「proto__存在的意义在于为原型链查找提供方向,原型链查找靠的是__proto」,而不是prototype(画重点,后面要考!!!)

实例对象obj3通过__proto__指向了Test的原型对象(Test.prototype),如图:

来我们练习一下

   function Test(name, age){        this.name = name        this.age = age    }    Test.prototype.say = function(){        console.log('我能说话')    }    var obj3 = new Test('Jack', 26)         1, 构造函数是? 实例是?    2, obj3.constructor === Test   true or false?    3, obj3.__proto__ === Test ?    4, Test.prototype === obj3.__proto__ ?    5, obj3.__proto__.constructor === Test ?         // 1, Test  obj3  2,true  3,false  4,true  5,true

怎么样,都答对了吗?

三,原型链

Object.prototype

在上面第二点中,每个js对象都有一个隐藏的原型对象属性__proto__

那Test的原型对象Test.prototype会不会也有一个隐式原型__proto__呢?控制台输出如下:

Test.prototype当然也存在一个属性__proto__,而这个Test.prototype.__proto__到底是谁呢?

   Test.prototype.__proto__ === Object.prototype    // true

(1) Test.prototype的隐式原型(「proto」)就是「Object.prototype」

(2) 所有的对象,包括构造函数的原型对象,最终都继承自 「Object.prototype」,这是「js原型链的顶点」

(3) 由于所以的”普通“对象都”源于“这个「Object.prototype」对象,所以它包含JavaScript中许多通用的功能。

比如:.toString()和.valueOf(),.hasOwnProperty(..)等

「Object.prototype是从哪里来的呢?」当然是由Object的属性prototype指向来的。Object.prototype同样也会存在属性 constructor指回Object(【目录 2,原型 原型对象】中提到)

此时的关系图:

在控制台打印Object.prototype,会发现 Object.prototype也是一个对象

那么也就是说它也存在隐式属性__proto__。想想看,如果Object.prototype.__proto__再去指向某个对象的原型(prototype),那整条线就显得无穷无尽,一直找下去让我们来试试呢

js代码的开发者当然考虑到了,「Object.prototype作为原型链的顶端,位于原型链的最末端」「因此,它不再有自己的原型,所以Object.prototype.「proto」 指向null,表示原型链的终点」

「原型链的终点是null」

「Object.prototype.「proto」 === null」

这时候我们的关系图就变成了:

每个对象都有一个原型(prototype),它指向另外一个对象,而指向的对象又存在属性(proto)指向另外一个对象。当我们访问对象(obj3)的属性时,会先在对象定义的属性中进行查找,没找到就会沿着__proto__一路向上查找,最终形成一个链式结构,这整个链式结构就叫做原型链

如果在原型链中找到了这个属性,就返回找到的属性值;如果整个原型链都没找到这个属性值,则返回 undefined,没找到方法直接报错(not a function)

总结

原型和原型链是 JavaScript 继承机制的核心,对于理解 JavaScript 的对象模型和继承特性至关重要。如果你有更多问题,或者想深入了解某个部分,可以在评论区进行讨论哦!

点击关注公众号,“技术干货” 及时达!

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

JavaScript 原型链 对象创建 原型对象
相关文章