深入理解 JavaScript 中的原型与对象

深入理解 JavaScript 中的原型与对象

在 JavaScript 中,原型是一项强大的概念,贯穿于对象创建、继承、和属性访问的方方面面。本文将深入讨论原型及相关概念,从显式原型、隐式原型到原型链,为你揭示 JavaScript 中这个核心特性的奥秘。

javascript 复制代码
function Person() {
    this.name = '小明'
    this.age = 18
}
Person.eat = function() {
    console.log('想吃面条')
}

let p = new Person()
let p2 = new Person()
console.log(p === p2)//false

以上代码中,构造函数 Preson,当我们new了两个Person,得到两个对象。在之前的学习中,我们知道了函数是引用类型,存在于堆中,当我们拿到它,其实是拿到了它在堆中的地址。按理说第11行语句的结果是true才对,说明,每new一次得到的对象都是新的对象。

第5行代码我们可以看到,我们往Person挂了一个eat属性(函数也是对象),执行Person.eat()是可以调用的,但当我们去查看new出来的Person对象时发现它并不在里面,也就是说,p.eat()是会报错的。(我的浏览器没有识别出 '小明' -.-')我们如何去解决这个问题呢?往下看

1. 显式原型

1.1 定义

在 JavaScript 中,每个函数都有一个特殊的属性,称为 prototype。这个属性定义了由该函数创建的对象的公共祖先。通过该构造函数创建的对象,可以隐式地继承这个原型上的属性和方法。

1.2 意义

显式原型允许我们将共享的属性和方法定义在一个地方,从而简化代码。所有由同一构造函数创建的对象,都共享一个原型,节省内存并使代码更加可维护。

1.3 原型上的属性修改

需要注意的是,原型上的属性修改只能由原型自己操作,实例对象无权修改。这一点对于理解 JavaScript 的继承机制至关重要。

2. 隐式原型

2.1 定义

对象原型,通常称为隐式原型,是实例对象的 __proto__ 属性,指向构造函数的显式原型。

2.2 关系

实例对象的隐式原型与构造函数的显式原型相互关联。通过这种关系,实例对象可以访问构造函数原型上的属性和方法。

ini 复制代码
<script>
    //Car.prototype.name = 'BMW'
    //Car.prototype.lang = 4900
    //Car.prototype.height = 1400
    Car.prototype = {
        name: 'BMW',
        lang: 4900,
        height: 1400
    }

    function Car(owner, color) {
        // this.name = 'BMW'
        // this.lang = 4900
        // this.height = 1400
        this.owner = owner
        this.color = color
    }

    let car = new Car('小帅', 'red')
    let car2 = new Car('小美', 'pink')

    console.log(car);
</script>

以上代码我们可以看到,Car.prototype这就是往这个构造函数的显示原型里添加属性。所以当我们new 一个Car时,它会隐式继承该原型的属性和方法。所以当我们要修改原型属性,也要通过该种方法

也就是说car.__proto__===Car.prototype

3. new 操作符

3.1 创建过程

使用 new 操作符创建对象的过程包含以下步骤:

  1. 创建一个空对象。
  2. 执行构造函数中的逻辑,将 this 绑定到新创建的对象上。
  3. 将新对象的隐式原型指向构造函数的显式原型。
  4. 返回新创建的对象。

new 操作符的机制保证了新对象与构造函数原型之间的正确关联。

4. 原型链

4.1 定义

原型链是由对象的隐式原型一级一级连接起来的链条。在属性访问时,JavaScript 引擎会沿着原型链向上查找,直到找到目标属性或者到达原型链的末端(null)。

4.2 每个对象都有一个隐式原型:

在 JavaScript 中,每个对象都有一个 __proto__ 属性,它指向该对象的原型。这个原型对象也是一个对象,并且它也有自己的 __proto__,形成了一条由对象连接而成的链,即原型链。

4.3. 原型链的顺序查找:

当你访问一个对象的属性时,如果对象本身没有这个属性,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或者到达原型链的末端(null)。

4.4. 原型链的形成:

原型链是通过对象的隐式原型一级一级连接起来的。一个对象的隐式原型指向构造函数的显式原型,而构造函数的显式原型也是一个对象,它的隐式原型又指向另一个构造函数的显式原型,依此类推,形成了原型链。

4.5. 构造函数和原型链:

每个构造函数都有一个显式原型对象,通过 prototype 属性访问。新创建的对象通过 new 操作符与构造函数关联,其隐式原型指向构造函数的显式原型。

下面是一个简单的例子来演示原型链:

javascript 复制代码
// 构造函数
function Animal(name) {
    this.name = name;
}

// 在构造函数的原型上添加方法
Animal.prototype.sayName = function() {
    console.log("My name is " + this.name);
};

// 创建实例对象
const myAnimal = new Animal("Leo");

// 访问实例对象的方法
myAnimal.sayName(); // 输出: My name is Leo

// 实例对象的隐式原型指向构造函数的显式原型
console.log(myAnimal.__proto__ === Animal.prototype); // 输出: true

// 构造函数的显式原型的隐式原型指向 Object 构造函数的显式原型
console.log(Animal.prototype.__proto__ === Object.prototype); // 输出: true

// Object 构造函数的显式原型的隐式原型是 null
console.log(Object.prototype.__proto__ === null); // 输出: true

在这个例子中,myAnimal 实例对象通过原型链可以访问到 sayName 方法,而原型链的末端是 null,表示查找到此处为止。

5. 网易面试题解析

5.1 所有对象都有隐式原型吗?

在 JavaScript 中,几乎所有的对象都有隐式原型,但有一种特殊情况例外。通过 Object.create(null) 创建的对象是没有隐式原型的。

javascript 复制代码
const obj = Object.create(null);

console.log(obj.__proto__); // 输出: undefined

在这个例子中,通过 Object.create(null) 创建的对象 obj 不继承任何属性和方法,它的隐式原型是 undefined,而不是指向标准的对象原型(Object.prototype)。

这种情况通常用于创建一个纯净的、不继承任何属性和方法的对象,可以完全按照你的需求定义其属性和方法。然而,这也意味着这个对象失去了一些 JavaScript 常规对象的特性,比如无法使用一些内置的方法和属性。因此,使用 Object.create(null) 需要慎重考虑,确保你真的需要一个没有原型链的对象。

结语

深刻理解原型、隐式原型、new 操作符和原型链是成为 JavaScript 高效开发者的关键。这些概念贯穿了 JavaScript 的对象模型,对于正确理解和利用这门语言至关重要。希望通过本文的讲解,读者能够更清晰地理解 JavaScript 中原型的精髓。

相关推荐
一颗花生米。21 分钟前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐0125 分钟前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio199525 分钟前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&1 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈1 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水2 小时前
简洁之道 - React Hook Form
前端
正小安4 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch6 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光6 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   6 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发