深入理解JavaScript原型与原型链
深入理解JavaScript原型与原型链
在JavaScript中,原型(prototype)是一个核心概念,它不仅关乎对象的创建方式,还深刻影响着对象间的继承机制。本文将深入探讨原型的内涵、原型链的工作原理,以及为什么并非所有对象都拥有原型。
原型基础
每个JavaScript函数都自动配备了一个名为prototype
的属性,这个属性是一个对象,用来定义当通过该函数作为构造函数创建新对象时,这些新对象(即实例)所共享的属性和方法。简而言之,原型就是构造函数创造实例的蓝图或原型模板。例如:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log("Hello, my name is " + this.name);
};
在这个例子中,Person.prototype.sayHello
定义了一个所有Person
实例都可以访问的方法。
实例对象与原型的关系
当我们使用构造函数创建一个新对象时,这个新对象会自动链接到构造函数的原型对象上。这种链接是通过每个实例对象内部的特殊属性[[Prototype]]
(通常可访问的形式为__proto__
,尽管不推荐直接使用)实现的。这意味着,实例对象可以访问其构造函数原型上的所有属性和方法,这一过程是隐式的,并且对于开发者来说是只读的。
Person.prototype.like = '听歌'
function Person() {
this.name = '田总'
}
let p = new Person()
p.like = '撸铁'
console.log(p.like); // 输出 撸铁
创建了Person
的一个实例p
,并为其显式地设置了一个同名属性like
,值为'撸铁'
。由于实例属性优先级高于原型属性,当访问p.like
时,它会返回实例自身的like
属性值,即'撸铁'
。
Constructor属性
每个实例对象都有一个constructor
属性,它指向创建该实例的构造函数,这对于理解对象的来源非常有用。
function Car() {
this.name = 'su7'
}
let car = new Car()
console.log(car.Constructor);// 输出: [Function: Car]
// 实例对象通过constructor来记录我是由谁创建的
在JavaScript中,每个由构造函数创建的对象都会自动获得一个constructor
属性,该属性指向创建该对象的构造函数。因此,当你打印car.constructor
时,它会显示出Car
构造函数本身,这表明car
对象是由Car
构造函数创建的。
原型链的奥秘
原型链是JavaScript实现继承的主要机制。当访问一个对象的属性或方法时,如果该对象本身没有定义该属性或方法,JavaScript引擎会继续在其[[Prototype]]
指向的对象中寻找,这个过程会一直向上追溯,直到找到该属性或方法,或者到达原型链的末端(null
)。这个链式查找的过程确保了对象可以从其祖先那里继承属性和方法。
并非所有对象都有原型?
通常情况下,JavaScript中的每个对象都会有一个原型链,因为就连Object.prototype
自身(所有对象原型的最终源头)也有一个null
作为其原型,形成了原型链的终点。然而,有一种特殊情况:通过Object.create(null)
创建的对象没有原型。这意味着该对象既没有继承自Object.prototype
的默认方法(如toString()
),也无法通过原型链查找机制获取任何属性或方法。这种对象常用于需要一个“纯净”对象,避免污染全局原型链的场景。
let a = {
num: 1
}
let b = Object.create(null);
这段代码在v8引擎中运行,对象a有原型,对象b由于通过Object.create(null)
创建,所以该对象没有原型。
结论
原型和原型链是JavaScript实现面向对象编程和继承的核心机制。它们提供了一种灵活且高效的方式来管理对象的属性与方法,同时也允许我们通过构造函数和原型链来模拟经典的类继承模式。理解原型链的运作原理,特别是认识到并非所有对象都默认拥有原型这一特例,对于深入掌握JavaScript语言的精髓至关重要。在实际开发中,合理运用原型链,可以编写出结构清晰、易于维护的代码。
转载自:https://juejin.cn/post/7376484708547821579