JavaScript学习笔记——通过继承原型来创建对象P2-原型:创建原型、原型是动态的、利用原型设置对象初始值

原型和原型式继承

  • JavaScript使用原型式继承,对象可从其他对象那里继承属性和行为
  • 原型也是对象:原型指的是其属性和行为被继承的对象
  • 新对象继承了原型对象后,便可访问其所有方法和属性,同时可以在新对象中添加额外的属性

利用继承原型创建大量对象,思路是:

  • 将所有对象中相同的属性和方法(希望重用的那部分)包含在原型
    ps. 另外,想要将所有对象的某个属性统一设置初始值,也可使用原型
  • 对于那些随对象而异的属性和方法,等到继承原型创建新对象时,针对不同对象添加其特有的属性

获取原型

对象构造函数有一个prototype属性,这是一个指向原型的引用

是的,函数也有属性,因为JavaScript中函数也是对象
实际上,在JavaScript中,几乎所有的东西都是对象,数组也是。但就目前而言,只需知道构造函数都包含属性prototype。后面将更深入地讨论函数以及其他对象

创建原型(和构造函数)

  • 原型也是对象:原型指的是其属性和行为被继承的对象

在创建一个原型时:

  • 构造函数用于将传入的参数赋给对象实例特有的属性和方法
  • 构造函数.prototype指向原型对象,设置原型对象的属性和方法,从而指定原型所包含的属性和方法
  1. 创建构造函数:添加每个对象实例特有的属性和方法(这些不同的部分需要单独用构造函数定制)
function Dog(name, breed, weight) {
	this.name = name;
	this.breed = breed;
	this.weight = weight;
}
  1. 设置原型:设置原型对象构造函数.prototype对象)的属性和方法,从而指定原型所包含的属性和方法
    ps. Dog.prototype 指向原型对象
Dog.prototype.species = "Canine";

Dog.prototype.bark = function() { 
	if (this.weight > 25) {
		console.log(this.name + " says Woof!"); 
	} else {
		console.log(this.name + " says Yip!"); 
	}
};

Dog.prototype.run = function() {
	console.log("Run!"); 
};

注意:位于原型中的bark方法仍然使用this.name
在没有使用原型的情况下(使用构造函数时),这很容易解释,因为this指的是方法被调用的对象。
调用原型中的方法bark时,可能误以为this指的是原型对象,但情况并非如此。
在任何情况下,this都指向原始对象,即方法被调用的对象,即便该方法位于原型中亦如此。因此,即便方法bark位于原型中,调用对象的bark方法时,this被设置为方法被调用的对象(即原始小狗对象),即使在对象中未找到该方法,而是在原型中找到了它,也不会修改this的值。得到的结果也是我们期望的:在哪个对象中调用了bark方法,则this就始终指向该对象。

  • 最后,如以前一样正常使用构造函数创建新的对象实例,即可实现继承原型
    (因为这里的构造函数与以前不同了:我们通过prototype属性设置了其原型
  • 另外还可以重写原型(不希望继承原型中已定义好的属性和方法,而是定制这些属性和方法)
  • 重写不会改变原型,也不会影响其他继承原型的对象实例

这之所以可行,源于继承的工作原理:优先在具体的对象实例中查找属性和方法;如果找不到,再在原型中查找
由此,在对象中重写属性和方法后,JavaScript调用它们时,直接在对象实例中找到所需的属性和方法,而不必再劳神去原型中查找

继承原型,并创建新的对象实例
var fido = new Dog("Fido", "Mixed", 38);

重写原型的bark方法
spot.bark = function() {
	console.log(this.name + " says WOOF!");
};

测试新的对象实例
fido.bark(); 使用的是 重写的定制bark方法
fido.run(); 使用的是 原型的run方法

修改原型:原型是动态的

已经知道,在创建构造函数和原型后,可以通过继承原型获取新的对象实例;
此后,可以随时给原型添加/修改属性和方法,继承该原型的所有对象实例能立即看到并使用这些属性和方法

这之所以可行,仍源于继承的工作原理:优先在具体的对象实例中查找属性和方法;如果找不到,再在原型中查找
因此,可以随时按需要修改原型,从而动态地更新原型,继承原型的对象也能立即看到原型的这些改动,并使用改动后的方法和属性(前提是对象实例没有重写这个方法或属性)

原型的另一个应用:设置初始值、用方法重写原型

再次运用原型的核心思想:消除重复,一旦有重复的部分,就将其包含在原型中(在这里,“重复部分”就是大量对象共同使用的初始值

  • 之前使用原型,目的是将所有对象中重复的属性和方法统一编写,实现重用
  • 想要将所有对象的某个属性统一设置初始值,也可使用原型

例如,统一将所有小狗的初始状态设置为站立,sit方法可以使小狗坐下

  • 在原型中新增sitting属性,其默认值为false;
  • 同时在原型中新增sit方法,如果调用sit方法,就对小狗实例重写原型的属性sitting,即在对象中添加属性sitting的值为true。

这样,我们能够给所有小狗对象指定默认值,并在需要时对各个小狗进行定制。

改写原型,为小狗设置初始状态为站立,而sit方法可以使小狗坐下
Dog.prototype.sitting = false;
Dog.prototype.sit = function() { 
	if (this.sitting) {
		console.log(this.name + " is already sitting"); 
	} else {
		this.sitting = true;
		console.log(this.name + " is now sitting"); 
	}
};

测试
var fido = new Dog("Fido", "Mixed", 38);
fido.sit();输出"fido is now sitting"
fido.sit();输出"fido is already sitting"

注意分析这里的原理:

Dog.prototype.sit = function() { 
	if (this.sitting) {
	...
  • 首次调用fido.sit时,fido对象中没有sitting属性,sitting的值从原型中获取;
	...
	else {
		this.sitting = true;
		console.log(this.name + " is now sitting"); 
	}
  • 但接下来将sitting设置为true时,是在对象实例中进行的(而非原型),相当于重写原型,在对象实例中添加了sitting属性,并设置为true。

为什么这里对象实例使用了原型中的sit方法,能够重写原型呢?
始终记住,在任何情况下,this都指向原始对象,即方法被调用的对象,即便该方法位于原型中亦如此。调用fido.sit(),即使因为fido没有sit方法而使用了原型的sit方法,该方法中的this仍始终指向fido

  • 第二次调用fido.sit时,fido对象实例中已经添加sitting属性,sitting的值从对象实例中获取

如何判断使用的属性包含在实例中还是原型中?

对象实例都包含方法Obj.hasOwnProperty("property")

  • 传入一个字符串,以查询某个属性是否在该对象中有定义
  • 如果属性是在对象实例中定义的,这个方法将返回true
  • 如果能够访问某属性,但是它又不是在对象实例中定义的,则可肯定是在原型中定义的

以上面的sitting属性为例,一开始sitting仅包含在原型中,故fido.hasOwnProperty("sitting");返回false;
而调用fido.sit();后,在对象实例中添加了属性sitting,重写了原型,则fido.hasOwnProperty("sitting");返回true;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值