JavaScript教程:深入理解原生原型(Native Prototypes)

JavaScript教程:深入理解原生原型(Native Prototypes)

en.javascript.info Modern JavaScript Tutorial en.javascript.info 项目地址: https://gitcode.com/gh_mirrors/en/en.javascript.info

前言

在JavaScript中,原型(prototype)是一个核心概念,而原生原型(Native Prototypes)则是JavaScript内置对象的基础。理解原生原型对于掌握JavaScript面向对象编程至关重要。本文将带你深入探索JavaScript原生原型的工作原理和应用场景。

什么是原生原型?

JavaScript中的所有内置构造函数(如Object、Array、Date等)都使用prototype属性来存储它们的方法。这些内置构造函数的原型就是我们所说的"原生原型"。

Object.prototype:一切的原型起点

让我们从一个简单的空对象开始:

let obj = {};
console.log(obj); // 输出: "[object Object]"

这个[object Object]字符串是从哪里来的?它来自内置的toString方法,但空对象obj本身并没有定义这个方法。

实际上,obj = {}这种字面量写法等同于obj = new Object(),而Object是一个内置的构造函数,它的prototype属性引用了一个包含toString等方法的对象。

原型链关系如下:

obj -> Object.prototype -> null

我们可以验证这一点:

let obj = {};

console.log(obj.__proto__ === Object.prototype); // true
console.log(obj.toString === Object.prototype.toString); // true

值得注意的是,Object.prototype是原型链的顶端,它的__proto__null

其他内置对象的原型

JavaScript中的其他内置对象如ArrayDateFunction等也遵循相同的模式,将方法存储在它们的原型中。

数组的原型链

当我们创建一个数组时:

let arr = [1, 2, 3];

实际上调用了new Array()构造函数,因此Array.prototype成为它的原型。完整的原型链是:

arr -> Array.prototype -> Object.prototype -> null

验证代码:

let arr = [1, 2, 3];

console.log(arr.__proto__ === Array.prototype); // true
console.log(arr.__proto__.__proto__ === Object.prototype); // true
console.log(arr.__proto__.__proto__.__proto__); // null

方法的覆盖

当原型链中的多个原型都有同名方法时,JavaScript会选择最接近的那个。例如:

let arr = [1, 2, 3];
console.log(arr.toString()); // "1,2,3" (来自Array.prototype.toString)
console.log(Object.prototype.toString.call(arr)); // "[object Array]"

这里数组调用了Array.prototype.toString而不是Object.prototype.toString,因为前者在原型链中更近。

原始值的特殊处理

字符串、数字和布尔值这些原始类型不是对象,但当我们尝试访问它们的属性时,JavaScript会临时创建对应的包装对象:

let str = "Hello";
console.log(str.toUpperCase()); // "HELLO"

背后发生的事情是:

  1. 创建一个临时的String对象
  2. 调用它的toUpperCase()方法
  3. 返回结果后销毁临时对象

这些包装对象的方法存储在String.prototypeNumber.prototypeBoolean.prototype中。

例外情况nullundefined没有对应的包装对象,也没有可用的方法。

修改原生原型

虽然可以修改原生原型,但通常不建议这样做:

String.prototype.show = function() {
  console.log(this);
};

"Test".show(); // 输出: "Test"

风险

  1. 全局影响,可能导致命名冲突
  2. 破坏代码的预期行为
  3. 可能与其他库产生兼容性问题

唯一例外:polyfill(为尚未实现的规范方法提供实现)

if (!String.prototype.repeat) {
  String.prototype.repeat = function(n) {
    return new Array(n + 1).join(this);
  };
}

console.log("La".repeat(3)); // "LaLaLa"

方法借用

我们可以从原生原型中"借用"方法:

let arrayLike = {
  0: "Hello",
  1: "World",
  length: 2
};

arrayLike.join = Array.prototype.join;
console.log(arrayLike.join(',')); // "Hello,World"

这种方法之所以有效,是因为许多数组方法只关心正确的索引和length属性,而不严格检查对象是否为真正的数组。

总结要点

  1. 所有内置对象都遵循相同模式:

    • 方法存储在原型中(Array.prototype, Object.prototype等)
    • 对象本身只存储数据
  2. 原始值通过临时包装对象访问方法:

    • String.prototype, Number.prototype, Boolean.prototype
    • nullundefined没有包装对象
  3. 修改原生原型通常是不好的实践,唯一的例外是实现polyfill

  4. 方法借用是一种灵活的技术,可以从不同的对象中组合功能

理解原生原型是掌握JavaScript面向对象编程的关键,它能帮助你更好地理解JavaScript的内部工作机制,并编写出更优雅、高效的代码。

en.javascript.info Modern JavaScript Tutorial en.javascript.info 项目地址: https://gitcode.com/gh_mirrors/en/en.javascript.info

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

史霁蔷Primrose

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值