深入理解 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 中原型的精髓。

相关推荐
gnip4 小时前
企业级配置式表单组件封装
前端·javascript·vue.js
一只叫煤球的猫5 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
excel6 小时前
Three.js 材质(Material)详解 —— 区别、原理、场景与示例
前端
掘金安东尼6 小时前
抛弃自定义模态框:原生Dialog的实力
前端·javascript·github
hj5914_前端新手10 小时前
javascript基础- 函数中 this 指向、call、apply、bind
前端·javascript
薛定谔的算法10 小时前
低代码编辑器项目设计与实现:以JSON为核心的数据驱动架构
前端·react.js·前端框架
Hilaku10 小时前
都2025年了,我们还有必要为了兼容性,去写那么多polyfill吗?
前端·javascript·css
yangcode10 小时前
iOS 苹果内购 Storekit 2
前端
LuckySusu10 小时前
【js篇】JavaScript 原型修改 vs 重写:深入理解 constructor的指向问题
前端·javascript
LuckySusu10 小时前
【js篇】如何准确获取对象自身的属性?hasOwnProperty深度解析
前端·javascript