魔幻代码之舞:探秘JavaScript原型的奇妙世界

1.前言

我们要探索 JavaScript 中的一个超酷的技术------原型。原型是编程中的一种强大工具,它能让你的代码更加高效和精巧。

想象一下,原型就像是编程的蓝图,可以帮助我们构建出更强大的对象。通过了解原型,我们能够更深入地理解 JavaScript 中的对象结构,让我们的代码变得更为优雅和可维护。

在这个学习之旅中,我们将深入了解原型的概念,探索如何利用原型链创建更灵活的代码结构。这并不是什么神秘的技巧,而是实实在在的编程智慧,能够让你的代码更加强大和高效。

2.原型的概念

2.1 函数的原型(显式原型、原型对象)

在 JavaScript 中,每个函数都有一个特殊的属性,称为 prototype。这个 prototype 是一个对象,它包含了一个指向原型对象的引用。

原型是函数天生就具有的属性。它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以隐式继承到原型上的属性和方法。

js 复制代码
function myFunction() {
  // 函数体
}

console.log(myFunction.prototype); // 输出: {}

这里,myFunction.prototype 就是 myFunction 函数的显示原型。

显示原型的作用

函数的原型对象是一个普通的对象,但它对于实现继承和共享属性和方法至关重要。让我们看看它的作用:

1.继承

当你创建一个对象实例时,它会继承其构造函数的原型。这样,通过原型链,实例可以访问原型对象中定义的属性和方法。

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

Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`)
}

let person1 = new Person('dante')
person1.sayHello(); // 输出:Hello, I'm dante

2 共享属性和方法

所有通过相同构造函数创建的实例都共享同一个原型对象。这意味着,如果你修改了原型对象,所有实例都会受到影响。

js 复制代码
Person.prototype.age = 18;

console.log(person1.age); // 输出: 18

let person2 = new Person('dante');
console.log(person2.age); // 输出: 18

prototype 与 proto

要注意 prototype 是函数特有的属性,而 __proto__ 则是实例对象特有的属性。__proto__ 指向其构造函数的原型对象。

js 复制代码
console.log(person1.__proto__ === Person.prototype); // 输出: true

虽然 __proto__ 是一种访问原型链的方式,但并不推荐使用,因为它不是标准的 JavaScript API。推荐使用 Object.getPrototypeOf() 方法来获取对象的原型。

js 复制代码
console.log(Object.getPrototypeOf(person1) === Person.prototype); // 输出: true

构造函数的 prototype

构造函数本身也有一个 prototype 属性,它与实例的 __proto__ 指向同一个对象。这个构造函数的 prototype 用于定义实例对象的原型。

js 复制代码
console.log(Person.prototype === myFunction.prototype); // 输出: true

函数原型在 JavaScript 中扮演着重要的角色,它通过原型链实现了继承和共享属性和方法的机制。深入理解函数原型有助于更好地利用 JavaScript 中的面向对象编程特性。

2.2对象的原型(隐式原型)

在 JavaScript 中,每个对象都有一个原型。原型可以看作是对象的父对象,它包含了对象共享的属性和方法。当我们访问一个对象的属性或方法时,JavaScript 引擎会首先在对象本身查找,如果找不到,就会去原型中查找。

现在我们通过具体的代码来了解什么是对象原型,使用构造函数是一种创建对象的常见方式。构造函数可以看作是一种特殊的函数,通过 new 关键字调用时,它会创建一个新的对象,并将该对象的原型指向构造函数的原型。

js 复制代码
Person.prototype.say = function (){
  return this.name + '今年' + this.age + '岁了'
}
function Person(){
  this.name = 'dante'
  this.age = 18
}
const p = new Person()
console.log(p) // Person {name: 'dante', age: 18}
console.log(p.say()) //dante今年18岁了

从上面的代码,我们可以看到,Person构造函数中并没有say()这样一个方法,但是为什么实例p可以访问say这个方法呢?这就是在创建实例p的时候,p继承了构造函数Person的显式原型中的方法,在js引擎查找这个say()方法时,首先在实例p中查找say,没有就会在p的原型上查找,刚好,实例p的原型继承了构造函数Person中的属性和方法。我总结了以下两点:

  1. 当访问对象属性时,先找对象显式具有的属性,没找到再去找对象的隐式原型。

  2. 实例对象的隐式原型 === 构造函数的显式原型

2.3原型链

从上面我们了解到了函数的显式原型和对象的隐式原型吗,明白了这两个概念,原型链就很好解决了,那什么是原型链呢?

当一个对象中的方法被调用执行时,引擎查找的时候首先会查找对象的显式具有的属性,没有,就会在对象原型中去查找,若还没有找到,就会继续向构造该对象的构造函数的显式原型中查找,还未找到,继续向上一层查找,一直到null。

顺着对象的隐式原型不断地向上查找上一级的隐式原型,直到找到目标或者一直到null,这种查找关系叫做原型链

3.所有的对象都有隐式原型吗?

了解了对象原型,那我们来思考这个问题,所有的对象都有隐式原型吗?文章读到这里,好像这句话说的非常的正确。 但是有一个特例,通过Object.create(null)创建的对象是没有隐式原型的。

js 复制代码
 //  Object.create()
  let obj = {
    a:1
  }
  let obj2 = Object.create(null)

  console.log(obj)
  console.log(obj2)

上面是在浏览器上打印obj的结果,是有Prototype的,而下方是打印obj2的结果,没有Prototype

Object.create(null) 创建的对象之所以为空(即没有原型链上的属性和方法),是因为它的原型被显式地设置为 null

在 JavaScript 中,Object.create() 方法接受一个参数,用于指定新创建对象的原型。当你传入 null 作为参数时,创建的对象将没有原型链,也就是说它不继承任何属性或方法。

示例:

js 复制代码
const emptyObject = Object.create(null);

console.log(emptyObject.toString); // 输出: undefined
console.log(emptyObject instanceof Object); // 输出: false

在这个例子中,emptyObject 被创建为一个空对象,它不具备任何继承的属性或方法。toString 属性为 undefined,而且它不是 Object 的实例,因为它没有原型链。

通常,这种操作被用于创建一个"纯净"的、不继承任何属性或方法的对象,以防止不必要的属性干扰。这样的对象可以用于一些特定的用途,比如作为映射(Map)的初始值,因为它不会受到原型链上其他属性的影响。

4. 总结

函数原型和对象原型共同构成了 JavaScript 中强大的原型链系统。它们通过原型链的连接关系,实现了继承、属性共享,为 JavaScript 提供了灵活且强大的面向对象编程能力。深入理解这两个概念,可以帮助你更好地设计和组织代码,使其更具可维护性和可扩展性。

相关推荐
WeiXiao_Hyy2 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡2 小时前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone2 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09012 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农2 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king3 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳3 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵4 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星4 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_4 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js