(JS)原型对象prototype与原型链

原型对象prototype

大部分面向对象的变成语言,都是通过(类)class来实现对象的继承,而Javascript语言的继承则是通过“原型对象”(prototype)来实现对象的继承。

ES6引入了class语法,这里暂不涉及。

1.原型对象概述

1.1构造函数的缺点

JavaScript通过构造函数生成新对象,因此构造函数可以视为一个模版,用于生成实例对象以及该实例对象的属性与方法。

function Animal(type,name){
    this.type=type;
    this.name=name;
}
var pp=new Animal("pig","pp");
pp.type;// pig
pp.name;// pp

上面代码中,构造函数Animal定义了typename属性,所有由函数Animal构造的实例对象,都具有这两个属性。

缺点

通过构造函数为定义实例对象的属性,虽然很方便,但是,同一个构造函数构建的实例对象之间,无法共享属性,会造成资源的浪费

function Animal(type,name){
    this.type=type;
    this.name=name;
    this.voice=function(){
        console.log("hanghang");
    };
}
var pp=new Animal("pig","pp");
var uu=new Animal("pig","uu");
pp.voice===uu.voice ;//false

上面代码表示,ppuu是构造函数Animal的两个实例,他们有着相同的voice方法,但是该方法是生成在每个实例对象上,有几个实例对象就要生成几次,这样就导致了资源的浪费。
因为该方法在不同实例对象间,执行结果都是一样的

在此背景下,JavaScript引出了一个解决方法,那就是原型对象(prototype)

1.2 prototype属性的作用

简单来说,当属性和方法定义在原型(prototype)上时,那么所有的实例对象都能共享,不仅节省了内存,还体现了实例对象之间的联系。

如何为对象指定原型?

JavaScript规定,每个函数都有一个prototype属性,指向一个对象。

function f(){}
type of f.prototype; //object

上述代码中,函数f默认有prototype属性,指向一个对象。

对于普通函数来说,这个属性可有可无,但是当这个属性定义在构造函数上时,该属性会自动成为实例对象的原型。

function Animal(type,name){
    this.type=type;
    this.name=name;
    
    };
}
Animal.prototype.voice=function(){
              console.log("hanghang");
 }
var pp=new Animal("pig","pp");
var uu=new Animal("pig","uu");
pp.voice(); //hanghang
uu.voice(); //hanghang

上面代码中,voice是定义在构造函数Animal原型上的方法,可以被uupp两个实例对象共享。

原型对象上的方法不是实例对象自身的方法,如果对其进行修改,变动会体现在所有的实例对象上。

Animal.prototype.voice=4;
pp.voice; //4
uu.voice; //4

上述代码表示,改变对构造函数原型上的方法,变动会体现在所有的实例对象上

只有当该实例对象没有某个属性或方法时,它才会去原型上找,否则将直接使用实例对象自带的

pp.voice="hanghang";

上面代码中,实例对象ppvoice属性改为了"hanghang",使得它不再去原型对象读取voice属性。

总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。

Animal.prototype.walk = function () {
  console.log(this.name + ' is walking');
};

上面代码中,Animal.prototype对象上面定义了一个walk方法,这个方法将可以在所有Animal实例对象上面调用。

1.3原型链

产生原因

JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……

按照这个思路,所有对象的原型最终都可以追溯到Object.prototype。也就是说,所有的对象都可以继承定义在Object.prototype下的方法如valueOftoString等。

Object.prototype对象的原型是null,本文不详细介绍

读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined

如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding

注意,一级级向上,在整个原型链上寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

举例来说,如果让构造函数的prototype属性指向一个数组,就意味着实例对象可以调用数组方法。

var MyArray = function () {};

MyArray.prototype = new Array();
MyArray.prototype.constructor = MyArray;

var mine = new MyArray();
mine.push(1, 2, 3);
mine.length // 3
mine instanceof Array // true

上面代码中,mine是构造函数MyArray的实例对象,由于MyArray.prototype指向一个数组实例,使得mine可以调用数组方法(这些方法定义在数组实例的prototype对象上面)。

最后那行instanceof表达式,用来比较一个对象是否为某个构造函数的实例,结果就是证明mineArray的实例。

上面代码还出现了原型对象的constructor属性,这个属性的含义留到下一节就再来解释。

晚安~

参考链接

JavaScript Modules: A Beginner’s Guide-----Preethi Kasireddy


最后,希望能和大家一起进步!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值