关于js原型的一些理解

简介:通俗整理归纳一下prototype和__proto__的相关特点。

目录

  1. prototype属性
  2. __proto__属性
  3. __proto__和prototype的联系
  4. prototype属性的使用

1.prototype

这个属性,是函数所特有的。prototype这个变量的类型是一个对象。这个对象之所以取名为原型对象,是因为这个对象里放的都是其父类的属性。比如:

function Father() {} 
function Son() {}
// 让"子类"构造函数的原型被"父类"实例赋值,这样Son这个构造函数就相当于可以在Father的基础上进一步添加功能。
Son.prototype = new Father();
// 通过一下测试,可以看出son1会被认为是Father构造函数的一个实例。     
var son1 = new Son();
console.log(son1 instanceof Father) // true
console.log(son1 instanceof Son) // true

关于这个属性的具体用法,会在后面举例。现在只需先知道:prototype属性实际上就是一个对象,这个对象就是其"父类"的实例对象,如果没有手动写父类,那么这个对象就是Object构造函数的实例。

2.proto

这个属性是所有的对象都自带的一个属性。它也是一个对象(或者说它是一个对象类型的指针),那么现在的问题就在于弄清它究竟指向了哪一个对象。
在js中,所有的对象都是由构造函数创建出来的,那么这个__proto__,其实就是指向了这个对象的构造函数的prototype属性。比如下图中的stu1对象的__proto__属性,指向的就是stu1的构造函数——Student()的prototype属性。

function Student(){}
var stu1 = new Student();
console.log(stu1.__proto__ === Student.prototype);// true

3.__proto__和prototype的联系

对象是由构造函数构造出来的,而构造函数里面有一个原型对象属性(prototype),我们对象的__proto__对象指针,指向的正是这个构造函数中的prototype对象。
在这里插入图片描述
需要注意的是,prototype也是一个对象(父类的实例对象),那么这个对象就也具有__proto__属性,这个属性因为是父类的实例所拥有的,所以它自然会指向父类的原型对象(因为是父类构造了它),以此类推,就形成了原型链,如图:
在这里插入图片描述
还需要注意的是,函数也是一种对象,所以构造函数除了prototype对象有__proto__属性,其自身也有__proto__属性,而这个属性按照我们前面的说法,就应该指向它的构造函数的prototype属性。那么构造函数的构造函数是什么呢?答案是js自带的本地函数——Function()。也就是说构造函数的__proto__属性指向的都是Function()的prototype(包括Function自己的__proto__)。如下图所示:
在这里插入图片描述
还要再提一点的就是,prototype除了__proto__属性,还都自带一个叫constructor的属性,这个属性指向构造函数自身:
在这里插入图片描述
所以整体看起来,就像是这个样子:
在这里插入图片描述

4.prototype属性的使用

了解了prototype属性的基本概念以后,再来看一下具体的应用。
首先最直接的就是用来存储构造函数实例的共享变量。比如:

function Student(name){
	this.name = name;
}
Student.prototype.schoolName = "清华";
var stu1 = new Student("A");
var stu2 = new Student("B");

console.log(stu1.schoolName); // 清华
console.log(stu2.schoolName); // 清华

Student.prototype.schoolName = "北大";
console.log(stu1.schoolName); // 北大
console.log(stu2.schoolName); // 北大

此时的A同学和B同学其实都是没有"schoolName"这个属性的,访问的其实都是原型中(即父类实例中)的schoolName,所以原型的schoolName进行修改,大家就都改变了。但要注意:如果实例对象自己想重新命名一个一样的属性,那么就会优先自己设定的而不会去使用原型的,比如:

function Student(name){
	this.name = name;
}
Student.prototype.schoolName = "清华";
var stu1 = new Student("A");
var stu2 = new Student("B");
stu1.schoolName = "斯坦福";
console.log(stu1.schoolName); // 斯坦福
console.log(stu2.schoolName); // 清华

这样的话,A同学就拥有了自己的新属性——schoolName,同时这也是和B同学无关的,B同学还是在用原型的schoolName。我们从控制台的展示也可以看出两者的此处差异:
在这里插入图片描述
既然可以手动给原型加属性,我们自然也可以给原型直接赋一个对象,从而达到"继承这个对象"的目的。

function Father() {
	this.a = 1;
	this.f = function () {
		console.log(this.a);
	}
}
Father.prototype.b = 2;
var father = new Father();
father.c = 3;
        
function Son() {}
Son.prototype = father;

var s1 = new Son();
// 这里的s1,就自带了a,b,c,f属性

当然,使用这种方式存在很多的缺点,所以在实际实现继承时,还需要一些改进,这里不详细展开了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值