首页 > 编程知识 正文

原型和原型链的理解JS,js原型和原型链的概念

时间:2023-05-06 21:09:04 阅读:228901 作者:397

1.为什么要设计JS原型呢?

知道设计者“为什么这样做”远比“怎么做的”重要。
这一个问题我是通过看fzdst老师的随笔所了解清楚的,有兴趣的可以看一下——Javascript继承机制的设计思想
以下我整理了fzdst老师的理解:
如果单纯使用new 构造函数生成对象的话,有一个很明显的缺点,即属性和方法不能共享,且浪费大量的内存。

function Star(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log('I can sing!'); }}var ldh = new Star('刘德华', 20);var zxy = new Star('张学友', 21);

使用new 构造函数的方法生成对象很好用,但是在内存中,其实是这样的。

每实例化一个对象,就会给该对象分配内存,所有的属性和方法都需要重新开辟空间,如果我们需要一百个实例化对象,就需要开辟一百个相同的内存空间,这造成了内存的严重浪费。
与此同时,我们还在有一些时候需要对所有的对象都有相同的属性和方法,但是构造函数的方式只能对每一个对象分别赋属性和方法,每一个对象的属性和方法都是独立的,不会互相影响,这就无法做到数据共享。
因此,JS创始人Brendan Eich决定为构造函数设置一个prototype属性。

2.什么是prototype呢?

prototype是一个属性,每个函数都有一个prototype属性,它默认指向一个Object空对象,prototype中包含函数实例共享的方法和属性,即实例对象一旦创建,就会自动引用prototype对象的属性和方法。
实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。

function Star(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log('I can sing!'); }}Star.sex = '男';Star.prototype.money = 1000000000;var ldh = new Star('刘德华', 20);console.log(ldh.sex); // undefine 说明实例只能继承构造函数原型上的属性和方法console.log(ldh.money); // 1000000000 3.构造器(constructor)

原型对象中还有一个属性constructor,其保存了指向该函数的一个引用。

function Star(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log('I can sing!'); } } Star.sex = '男'; Star.prototype.money = 1000000000; var ldh = new Star('刘德华', 20); console.log(Star.prototype.constructor); console.log(Star.prototype.constructor === Star); // 结果为 true,说明Star原型对象的constructor指向Star本身 console.log(ldh.constructor); console.log(ldh.constructor === Star); // 结果为true,说明Star的实例对象的constructor也指向构造函数Star本身 5.隐式原型(proto

每个函数都有一个prototype,即显式原型。
每个实例对象都有一个__proto__,即隐式原型。
对象的隐式原型的值指向其对应构造函数的显式原型的值。

function Star(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log('I can sing!'); } }var ldh = new Person('刘德华', 18);console.log(ldh.__proto__ === Star.prototype); // true,说明对象的隐式原型的值指向其对应构造函数的显式原型的值

它们的关系如下:

6.构造函数、实例、原型三者之间的关系

7.原型链

原型链就是根据prototype和__proto__连接起来的一个原型链条。

function Fun() { this.test1 = function() { console.log('test1'); } } Fun.prototype.test2 = function() { console.log('test2'); } var fn = new Fun(); fn.test1(); // 结果为:test1 fn.test2(); // 结果为:test2 console.log(fn.toString()); // 结果为:[object Object] fn.test3(); // 报错,未定义

这段代码对应这四种访问:

fn.test1() : fn直接从自身属性中查找,找到test1()方法,返回fn.test2() : fn先从自身属性中查找,没找到test2()方法,沿着它的__proto__这个链条向上查找,找到构造函数Fun的原型对象,找到test2()方法,返回console.log(fn.toString()) : fn先从自身属性中查找,没找到oString()方法,沿着它的__proto__这个链条向上查找,找到构造函数Fun的原型对象,没找到toString()方法,继续沿着它的__proto__这个链条向上查找,找到Object的原型对象,找到toString()方法,返回fn.test3() :fn先从自身属性中查找,没找到,沿着它的__proto__这个链条向上查找,找到构造函数Fun的原型对象,没找到test3()方法,继续沿它的__proto__这个链条向上查找,找到Object的原型对象,还是没找到test3()方法,于是返回undefined

通过原型链访问一个对象的属性的方法:
先在自身属性中查找,找到则返回,如果没有,再沿着__proto__这个链向上查找,找到则返回,没有则继续沿着__proto__这个链向上查找,如果最终找到Object的原型对象还是没有找到,则返回undefined。

8.使用原型链实现继承

借用父构造函数继承属性,利用原型对象继承方法

function Father(name, age) { this.name = name; this.age = age; } function Son(name, age) { Father.call(this, name, age); } Father.prototype.money = function() { console.log('I have 100000$!'); } Son.prototype = new Father(); Son.prototype.constructor = Son; Son.prototype.habbit = function() { console.log('I like playing!'); } var son = new Son('mmm', 17); console.log(son); son.money(); // I have 100000$! son调用father的方法 son.habbit(); // I like playing! son调用自己的方法

(如果有什么错误,欢迎大佬在评论区指正,thank you~)

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。