JavaScript原型探秘:构建对象与继承的艺术

大家好,我是睡个好jo,今天要给大家分享的是js中的原型。

在JavaScript中,原型(Prototype)机制是实现对象继承和属性共享的核心概念。这篇文章将会带着大家去了解并掌握原型的相关知识

原型(Prototype)

每个函数在JavaScript中都自带一个特殊的属性------prototype。这个属性是一个对象,用于定义通过该构造函数创建的所有实例所共享的属性和方法。简而言之,构造函数的prototype对象就是其所有实例对象的公共祖先。

当然,让我们通过一个具体的例子来说明prototype的工作原理。假设我们要创建一个表示动物的构造函数,特别是创建一个猫的类,我们可以这样做:

javascript 复制代码
function Cat(name, color) {
    this.name = name;
    this.color = color;
}

// 给Cat构造函数的prototype添加一个方法
Cat.prototype.meow = function() {
    console.log(this.name + ", the " + this.color + " cat, says 'Meow!'");
}

在这个例子中,Cat是一个构造函数,用于生成猫的实例。我们通过Cat.prototype给所有的猫实例添加了一个共享的方法meow()。这意味着,所有通过new Cat()创建的对象都将能够访问到meow方法,而不需要在每个实例中单独定义。

接下来,我们创建两个猫的实例并调用它们的meow方法:

javascript 复制代码
let kitty1 = new Cat("Whiskers", "gray");
let kitty2 = new Cat("Snowball", "white");

kitty1.meow(); // 输出: Whiskers, the gray cat, says 'Meow!'
kitty2.meow(); // 输出: Snowball, the white cat, says 'Meow!'

尽管meow方法是在Cat.prototype上定义的,但kitty1kitty2都能够访问到它,因为它们的原型链(__proto__)指向了Cat.prototype。这样,meow方法就成为了所有由Cat构造函数创建的对象所共享的功能,节省了内存并体现了面向对象编程中"继承"的概念。

以下内容需要先了解new,请先看你news什么new,stanley(是单例)

实例与原型的关系

当通过构造函数(使用new关键字)创建一个对象时,该对象会自动获得一个内部属性[[Prototype]](可通过__proto__访问,尽管非标准)。这个内部属性指向构造函数的prototype对象,使得实例能访问原型上的属性和方法。这些属性虽然对实例可见,但修改它们实际上会影响所有通过该构造函数创建的实例,因为它们是共享的。

但是实例不能改变原型对象上的属性和方法:

假设我们有一个构造函数Person,并在其原型上定义了一个属性gender。

Javascript 复制代码
function Person(name) {
    this.name = name;
}

Person.prototype.gender = "unknown"; // 在原型上定义gender属性

放在浏览器上访问: 现在,我们创建两个Person的实例person1和person2。

Javascript 复制代码
let person1 = new Person("Alice");
let person2 = new Person("Bob");

访问原型属性: 当我们访问person1.gender或person2.gender时,由于实例自身没有定义gender属性,JavaScript会沿着原型链找到Person.prototype.gender,因此两个实例都会显示"unknown"。

修改实例属性: 如果我们直接修改person1的gender属性,这实际上是在person1实例上新增了一个名为gender的属性,覆盖了从原型链上继承来的同名属性,但不影响person2或Person.prototype。

Javascript 复制代码
person1.gender = "female";
console.log(person1.gender); // 输出:"female"
console.log(person2.gender); // 输出:"unknown",未受影响
console.log(Person.prototype.gender); // 输出:"unknown",原型属性未变

总结

  • 直接在实例上修改一个从原型链上继承来的属性或方法,实际上是为该实例添加了一个新的局部属性或方法,而不是修改了原型对象上的那个。
  • 这种做法不会影响到其他通过同一原型链继承的对象,也不会改变原型对象本身的属性或方法。
  • 若要修改原型上的属性或方法,需直接在原型对象上操作,如Person.prototype.gender = "modified";这样会改变所有通过Person构造函数创建的对象的行为。

原型链

原型链是JavaScript实现对象属性查找的机制。当访问一个对象的属性时,如果该对象自身没有这个属性,JavaScript引擎会继续在其[[Prototype]](即隐式原型)所指向的对象中查找。这一过程会持续进行,沿着原型链逐级向上查找,直到找到该属性或到达原型链的末端(null)。

分析原型链

我们来根据以下图片来分析:

分析结果

  1. f1 和 f2 的关系f1f2都是Foo函数的实例,它们的__proto__属性指向Foo.prototype。这意味着这两个函数实例可以从Foo.prototype继承属性和方法。
  2. o1 和 o2 的关系o1o2都是Object函数的实例,它们的__proto__属性指向Object.prototype。同样,这两个对象实例也可以从Object.prototype继承属性和方法。
  3. FooObject 的关系FooObject都是Function函数的实例,它们的__proto__属性分别指向Function.prototype。这意味着这两个函数实例可以从Function.prototype继承属性和方法。
  4. Function 的关系Function函数的__proto__属性指向Function.prototype,这是由于Function是由内置的Function构造函数创建的,所以遵循同样的规则。
  5. Function.prototype 的关系Function.prototype__proto__属性指向null,表明这是原型链的终点。
  6. Object.prototype 的关系Object.prototype__proto__属性也指向null,这也是原型链的终点。
  7. Foo.prototype 的关系Foo.prototype__proto__属性指向Object.prototype,这是因为Foo.prototype是一个普通的对象,也是由Object函数创建的,所以遵循Object的原型链。
  8. ObjectFunction 的关系ObjectFunction__proto__属性都指向Function.prototype,因为它们都是函数,由Function构造函数创建。
  9. Foo 的关系Foo__proto__属性指向Function.prototype,因为它也是一个函数,由Function构造函数创建。

隐式原型与显式原型

  • 隐式原型(__proto__ :每个对象(除null外)都有一个内部属性,即隐式原型,它指向创建该对象的构造函数的prototype对象。
  • 显式原型(prototype:主要存在于函数对象上,用于定义实例共享的属性和方法。

特殊案例:无原型对象

并非所有JavaScript对象都有原型。使用Object.create(null)可以创建一个没有原型的对象,即该对象的__proto__null,这样的对象不继承任何属性或方法,是真正的"纯净"对象。

示例代码解析

javascript 复制代码
// 定义Car构造函数,为其原型添加属性
Car.prototype.name = 'su7';  // 品牌
Car.prototype.lang = 5000;  // 长度
Car.prototype.height = 1400;    // 高度 

function Car(color, owner) {
    this.color = color;
    this.owner = owner; 
}

// 创建Car实例
let car1 = new Car('pink', 'ii');
let car2 = new Car('orange', 'jj');
let car3 = new Car();

// 输出验证
console.log(car1.name);  // 输出: su7
console.log(car1.lang);  // 输出: 5000
console.log(Car);       // 输出: [Function: Car]
console.log(Car.prototype === Car.__proto__);  // 输出: false

在上述示例中,我们定义了一个Car构造函数,并通过其prototype属性为所有Car实例添加了共享属性。通过new Car()创建的每个实例都能访问这些共享属性。最后一行输出false是因为Car.prototypeCar函数的原型对象,而Car.__proto__实际上是Function.prototype,它们指向不同的对象。

通过这些概念和示例,我们可以更深刻地理解JavaScript的原型机制,它是实现面向对象编程、实现继承和属性复用的重要基石。

相关推荐
王哈哈^_^1 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie1 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic2 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿2 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具3 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161773 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test4 小时前
js下载excel示例demo
前端·javascript·excel
Yaml44 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事4 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶4 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json