javaScript之原型

一、原型的作用

1、节省内存空间
2、实现数据共享(继承)

二、原型、构造函数、实例对象三者关系

结论 1、任何一个函数都有prototype属性,本身是一个对象

function myFunction() {  
    // 函数体  
}  
console.log(myFunction.prototype); // 输出:{constructor: ƒ}

在上面的代码中,我们定义了一个名为 myFunction 的函数,并打印了它的 prototype 属性。结果会是一个对象,这个对象至少包含一个 constructor 属性,它指向原函数。

function MyObject() {  
    // 构造函数体  
}  
MyObject.prototype.myMethod = function() {  
    // 方法体  
};  
var obj = new MyObject();  
obj.myMethod(); // 可以调用原型上的方法

在这个例子中,我们为 MyObject 函数的 prototype 对象添加了一个名为 myMethod 的方法。然后,我们创建了一个 MyObject 的新实例 obj,并调用了它的 myMethod 方法。这是因为 myMethod 方法实际上是定义在 MyObject.prototype 上的,所有的 MyObject 实例都继承了这个方法。
综上所述,我们可以确认在JavaScript中,任何函数都有 prototype 属性,并且这个属性本身是一个对象。
结论 2、构造函数也有prototype属性,本身是一个对象,我们称之为原型

function MyConstructor() {  
    // 构造函数的实现  
}
console.log(MyConstructor.prototype); // 输出:{constructor: ƒ}

首先,创建一个构造函数,然后,我们验证这个构造函数是否有prototype属性,输出结果应该是一个对象,该对象至少包含一个constructor属性,它指向了原始的构造函数。这个对象就是构造函数的原型。
接下来,我们验证这个prototype属性本身是否是一个对象:

console.log(typeof MyConstructor.prototype); // 输出:"object"

使用typeof操作符,我们可以确认MyConstructor.prototype确实是一个对象。
结论 3、原型上的属性和方法都可以被实例化对象所继承
我们用上面的这个原型对象添加属性或方法,然后通过构造函数的实例来访问这些属性或方法,从而进一步验证原型对象的存在和作用:

// 给原型对象添加属性  
MyConstructor.prototype.myProperty = 'Hello, World!';  
// 给原型对象添加方法  
MyConstructor.prototype.myMethod = function() {  
    console.log('This is a method on the prototype.');  
};  
// 创建构造函数的实例  
var instance = new MyConstructor();  
// 访问原型上的属性  
console.log(instance.myProperty); // 输出:"Hello, World!"  
// 调用原型上的方法  
instance.myMethod(); // 输出:"This is a method on the prototype."

在这个例子中,我们给MyConstructor的原型添加了一个属性myProperty和一个方法myMethod。然后我们创建了一个MyConstructor的实例instance,并能够通过这个实例来访问和调用原型上的属性和方法。
综上所述,我们可以得出结论:在JavaScript中,构造函数也有prototype属性,这个属性本身是一个对象,我们称之为原型。构造函数的原型对象可以被其所有实例共享,从而实现了代码的重用和继承。
结论 4、任何一个对象都有constructor属性,实例化对象的constructor属性指向构造函数
沿用以上的代码我们验证objInstance的constructor属性是否指向MyObject构造函数:

console.log(objInstance.constructor === MyObject); // 输出:true

输出结果应该是true,表明objInstance的constructor属性确实指向了MyObject构造函数。
再让我们看看通过字面量创建的对象的情况:

var literalObj = {  
    property: 'value'  
};

在这种情况下,对象的constructor属性通常指向Object构造函数,因为字面量创建的对象本质上是Object的实例。

console.log(literalObj.constructor === Object); // 输出:true

然而,需要注意的是,在某些情况下,constructor属性可以被重写或更改,这可能导致它不再指向原始的构造函数。因此,在依赖constructor属性来识别对象的类型时,需要谨慎行事。通常更可靠的方法是使用instanceof操作符或者Object.getPrototypeOf()方法配合constructor属性来确定对象的构造函数。
这里是一个使用instanceof的例子:

console.log(objInstance instanceof MyObject); // 输出:true  
console.log(literalObj instanceof Object); // 输出:true

使用instanceof可以避免constructor属性可能被篡改的问题,因为它检查的是原型链,而不是直接检查constructor属性。
综上所述,我们可以确认在JavaScript中,除了原始值(如null和undefined),每个对象都有一个constructor属性,该属性指向创建该对象实例的构造函数。对于通过构造函数创建的对象,它指向该构造函数;对于通过字面量创建的对象,它通常指向Object构造函数。
结论 5、原型也是对象,也有constructor属性,指向构造函数
在JavaScript中,每个函数的prototype属性都是一个对象,这个对象被称为原型对象。原型对象同样具有constructor属性,该属性指向与其关联的构造函数。这个constructor属性在原型链的继承中起着关键作用,用于标识实例是由哪个构造函数创建的。

function MyConstructor() {  
    // 构造函数体  
}  
// 验证原型是一个对象  
console.log(typeof MyConstructor.prototype); // 输出:"object"  
// 验证原型对象具有constructor属性,并且指向构造函数  
console.log(MyConstructor.prototype.constructor === MyConstructor); // 输出:true

在这个例子中,我们首先创建了一个名为MyConstructor的构造函数。然后,我们使用typeof操作符来验证MyConstructor.prototype确实是一个对象。接着,我们检查MyConstructor.prototype.constructor是否等于MyConstructor,以确认原型对象的constructor属性指向了正确的构造函数。
需要注意的是,尽管constructor属性通常指向关联的构造函数,但它并非不可变。如果在原型链中的某个环节修改了constructor属性的值,那么这个修改将会影响所有基于该原型的实例。因此,在修改constructor属性时需要特别小心,以避免破坏原型链的完整性。
在实践中,我们更倾向于使用Object.getPrototypeOf()方法来获取一个对象的原型,然后再访问原型上的constructor属性,这样可以避免直接操作可能已被修改的constructor属性。

// 创建实例  
var instance = new MyConstructor();  
  
// 获取实例的原型  
var prototype = Object.getPrototypeOf(instance);  
  
// 验证原型的constructor属性指向构造函数  
console.log(prototype.constructor === MyConstructor); // 输出:true

使用Object.getPrototypeOf()方法可以确保我们获取到的是实例的实际原型,而不是可能被篡改的constructor.prototype。这样可以更可靠地确定实例与构造函数之间的关系。
结论 6、任何一个对象都有__proto__属性,实例对象的__proto__属性指向构造函数的原型
在JavaScript中,每个对象实例都有一个内部链接到它的原型,这个链接通常通过__proto__属性来访问。需要注意的是,__proto__是一个非标准的属性,尽管在大多数现代浏览器中它都可以工作,但在严格遵循ECMAScript标准的代码中应避免使用它。相反,应使用Object.getPrototypeOf()方法来获取对象的原型。然而,为了验证你的陈述,我们可以使用__proto__属性。
下面我们来验证实例对象的__proto__属性确实指向构造函数的原型:

// 创建实例  
function MyConstructor() {  
    // 构造函数体  
}  
// 创建实例对象  
var instance = new MyConstructor();  
// 验证实例对象的__proto__属性指向构造函数的原型  
console.log(instance.__proto__ === MyConstructor.prototype); // 输出:true

在这个例子中,我们定义了一个名为MyConstructor的构造函数,并创建了一个它的实例instance。然后,我们比较instance.__proto__和MyConstructor.prototype是否相等,以验证实例对象的__proto__属性确实指向构造函数的原型。
然而,由于__proto__不是一个标准属性,它的行为可能在不同的JavaScript环境中有所不同,甚至可能在未来的JavaScript版本中被移除。因此,推荐的做法是使用Object.getPrototypeOf()来获取对象的原型:

// 使用Object.getPrototypeOf获取实例的原型  
var prototype = Object.getPrototypeOf(instance);  
// 验证通过Object.getPrototypeOf获取的原型与构造函数的原型是否相等  
console.log(prototype === MyConstructor.prototype); // 输出:true

这种方法更加可靠,因为它遵循了ECMAScript标准,并且在所有现代浏览器中都得到了支持。通过Object.getPrototypeOf(),我们可以确保无论环境如何变化,都能正确地获取到对象的原型。

三、结语

原型(Prototype)在JavaScript中是一个核心概念,它允许对象通过原型链来共享属性和方法,从而实现了基于原型的继承机制。每个构造函数都有一个prototype属性,该属性是一个对象,包含了可以由该构造函数的实例访问的属性和方法。当创建构造函数的实例时,实例内部会包含一个指向其构造函数的prototype对象的链接,这个链接通常通过非标准的__proto__属性来访问,但更推荐使用标准的Object.getPrototypeOf()方法来获取。
原型链是JavaScript实现继承和共享属性的方式,如果一个对象自身不包含某个属性或方法,那么JavaScript会在该对象的原型上查找这个属性或方法,如果还没有找到,就继续沿着原型链向上查找,直到找到为止或到达原型链的末尾(通常是Object.prototype)。
通过原型,JavaScript能够以一种灵活且高效的方式组织代码,实现代码复用和对象之间的继承关系。了解并熟练掌握原型及其相关概念,对于深入理解JavaScript的面向对象编程模型以及编写高质量、可维护的JavaScript代码至关重要。

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值