当前位置: 首页 > news >正文

【JavaScript】原生函数

什么是原生函数

原生函数 (Native Functions), 也称为内置函数或内建构造函数, 是 JavaScript 语言内置的全局对象, 用于创建和处理特定类型的数据.


常用原生函数列表

基本类型相关:

  • String - 字符串
  • Number - 数字
  • Boolean - 布尔值
  • Symbol - 符号 (ES6+)
  • BigInt - 任意精度整数 (ES2020+)

引用类型相关:

  • Object - 对象
  • Array - 数组
  • Function - 函数
  • RegExp - 正则表达式
  • Date - 日期
  • Error - 错误对象

注意: nullundefined 虽然是基本类型, 但没有对应的构造函数.



原生函数的两种用法

原生函数可以作为普通函数调用, 也可以作为构造函数使用 (配合 new 关键字).


作为普通函数调用

直接调用原生函数通常用于类型转换:

String(123); // "123"
Number("456"); // 456
Boolean(0); // false

作为构造函数使用

使用 new 关键字会创建对应类型的封装对象:

const str = new String("abc");typeof str; // "object" (不是 "string")
str instanceof String; // true
Object.prototype.toString.call(str); // "[object String]"

通过构造函数创建的是封装对象 (wrapper object), 而非基本类型值本身.


两者的区别

// 普通调用: 返回基本类型值
const a = String("hello");
typeof a; // "string"// 构造函数调用: 返回封装对象
const b = new String("hello");
typeof b; // "object"// 两者的值相等, 但类型不同
a == b; // true (值相等)
a === b; // false (类型不同)

特殊情况

Symbol 和 BigInt 不能使用 new:

// Symbol 不能作为构造函数
const sym = Symbol("description"); // 正确
// const sym = new Symbol("desc");     // TypeError: Symbol is not a constructor// BigInt 也不能作为构造函数
const big = BigInt(123); // 正确
// const big = new BigInt(123);        // TypeError: BigInt is not a constructor

Array 和 Function 可省略 new:

// 以下两种写法效果相同
const arr1 = new Array(1, 2, 3);
const arr2 = Array(1, 2, 3);const fn1 = new Function("a", "return a * 2");
const fn2 = Function("a", "return a * 2");



基本类型与封装对象

基本类型值 vs 封装对象

JavaScript 有 7 种基本类型:

  • string
  • number
  • boolean
  • symbol
  • bigint
  • null
  • undefined

基本类型值本身没有属性和方法, 但我们却能调用它们的方法:

const str = "hello";
str.length; // 5
str.toUpperCase(); // "HELLO"

这是因为 JavaScript 引擎会自动进行装箱操作.


自动装箱 (Auto-boxing)

当访问基本类型值的属性或方法时, JavaScript 会自动完成以下步骤:

  1. 创建对应类型的封装对象
  2. 调用封装对象上的方法
  3. 立即销毁该封装对象
// 等价于以下过程:
const str = "hello";
str.toUpperCase();// 1. 创建临时封装对象
// const temp = new String("hello");
// 2. 调用方法
// temp.toUpperCase();
// 3. 销毁临时对象

重要特性: 封装对象是临时的, 无法为基本类型值添加属性:

const str = "test";
str.customProp = "value";
console.log(str.customProp); // undefined (属性丢失)

拆箱 (Unboxing)

获取封装对象中的基本类型值, 使用 valueOf() 方法:

const strObj = new String("abc");
const numObj = new Number(42);
const boolObj = new Boolean(true);strObj.valueOf(); // "abc"
numObj.valueOf(); // 42
boolObj.valueOf(); // true

隐式拆箱会在需要基本类型值的上下文中自动发生:

const a = new String("hello");
const b = a + " world"; // 隐式调用 valueOf()typeof a; // "object"
typeof b; // "string"
b; // "hello world"

使用建议与最佳实践

避免直接使用封装对象:

// 不推荐: 使用封装对象
const str = new String("abc");
const num = new Number(42);// 推荐: 使用基本类型值
const str = "abc";
const num = 42;

封装对象的陷阱:

// Boolean 封装对象始终为真值
const falseObj = new Boolean(false);if (falseObj) {console.log("执行了!"); // 会执行 (对象是真值)
}// 基本类型值的行为符合预期
const falsePrimitive = false;if (falsePrimitive) {console.log("不会执行"); // 不会执行
}

最佳实践:

  • 优先使用基本类型值 ("abc", 42, true)
  • 避免使用 new String(), new Number(), new Boolean()
  • 让 JavaScript 引擎自动处理装箱和拆箱
  • 仅在类型转换时直接调用构造函数 (不使用 new)

Object 作为通用包装类构造函数

除了使用 String, Number, Boolean 构造函数创建对应的封装对象, 还可以使用 Object 构造函数, 它会根据参数类型自动返回对应包装类的实例:

// 传入字符串, 返回 String 实例
const strObj = new Object("hello");
strObj instanceof String; // true
typeof strObj; // "object"// 传入数值, 返回 Number 实例
const numObj = new Object(42);
numObj instanceof Number; // true// 传入布尔值, 返回 Boolean 实例
const boolObj = new Object(true);
boolObj instanceof Boolean; // true

这种方式虽然可行, 但不推荐使用, 原因:

  1. 代码意图不明确 (无法直接看出要创建哪种类型)
  2. 性能略低于直接使用对应构造函数
  3. 不符合现代 JavaScript 最佳实践

注意: nullundefined 没有对应的包装类, 传入 Object 构造函数会返回空对象:

const nullObj = new Object(null);
const undefinedObj = new Object(undefined);nullObj; // {}
undefinedObj; // {}



对象类型检测

typeof 的局限性

typeof 对于对象类型的检测能力有限:

typeof []; // "object" (无法区分数组)
typeof null; // "object" (历史遗留 bug)
typeof new Date(); // "object"
typeof /regex/; // "object"
typeof new String("abc"); // "object"

Object.prototype.toString() 方法

更准确的类型检测方式是使用 Object.prototype.toString():

Object.prototype.toString.call([1, 2, 3]); // "[object Array]"
Object.prototype.toString.call(/regex/i); // "[object RegExp]"
Object.prototype.toString.call(new Date()); // "[object Date]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"

基本类型值的自动装箱:

// 基本类型值会被自动包装
Object.prototype.toString.call("abc"); // "[object String]"
Object.prototype.toString.call(42); // "[object Number]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(Symbol()); // "[object Symbol]"
Object.prototype.toString.call(123n); // "[object BigInt]"

Symbol.toStringTag

ES6 引入了 Symbol.toStringTag, 允许自定义对象的类型标签:

// 默认行为
class MyClass {}
Object.prototype.toString.call(new MyClass()); // "[object Object]"// 使用 Symbol.toStringTag 自定义
class MyCustomClass {get [Symbol.toStringTag]() {return "MyCustomClass";}
}Object.prototype.toString.call(new MyCustomClass()); // "[object MyCustomClass]"

内置对象的 Symbol.toStringTag:

JavaScript 内置对象 (如 Map, Set, Promise) 内部也定义了 Symbol.toStringTag 属性, 这就是 Object.prototype.toString() 能够识别它们类型的原因:

// 内置对象的 Symbol.toStringTag 属性
const map = new Map();
map[Symbol.toStringTag]; // "Map"const set = new Set();
set[Symbol.toStringTag]; // "Set"const promise = Promise.resolve();
promise[Symbol.toStringTag]; // "Promise"// toString() 方法读取这个属性来生成类型字符串
Object.prototype.toString.call(map); // "[object Map]"
Object.prototype.toString.call(set); // "[object Set]"
Object.prototype.toString.call(promise); // "[object Promise]"

自定义对象使用 Symbol.toStringTag:

我们可以在自定义类中使用相同的机制, 让对象有更友好的类型标识:

class Collection {constructor(items) {this.items = items;}get [Symbol.toStringTag]() {return "Collection";}
}const col = new Collection([1, 2, 3]);// 自定义的类型标签
col[Symbol.toStringTag]; // "Collection"// toString() 会使用这个标签
Object.prototype.toString.call(col); // "[object Collection]"// 对比: 没有定义 Symbol.toStringTag 的普通类
class NormalClass {}
const normal = new NormalClass();
Object.prototype.toString.call(normal); // "[object Object]"

注意事项:

由于 Symbol.toStringTag 可以被随意修改, 不应完全依赖 toString() 进行类型检测:

const fakeArray = {get [Symbol.toStringTag]() {return "Array";},
};Object.prototype.toString.call(fakeArray); // "[object Array]"
Array.isArray(fakeArray); // false (更可靠)



原生原型

原型对象的特性

每个原生构造函数都有对应的 prototype 对象, 包含该类型的方法和属性.

// 字符串方法来自 String.prototype
String.prototype.toUpperCase; // [Function: toUpperCase]
"hello".toUpperCase === String.prototype.toUpperCase; // true// 数组方法来自 Array.prototype
Array.prototype.map; // [Function: map]
[1, 2, 3].map === Array.prototype.map; // true

特殊的原生原型: 某些原生原型本身就是对应类型的实例.

// Function.prototype 是一个函数
typeof Function.prototype; // "function"
Function.prototype(); // undefined (空函数)// Array.prototype 是一个数组
Array.isArray(Array.prototype); // true
Array.prototype.length; // 0// RegExp.prototype 是一个正则表达式
RegExp.prototype.toString(); // "/(?:)/"
"test".match(RegExp.prototype); // [""]

修改原型的风险

虽然可以修改原生原型, 但这是强烈不推荐的做法:

// 不推荐: 修改原生原型
Array.prototype.customMethod = function () {return "custom";
};[1, 2, 3].customMethod(); // "custom"// 风险 1: 影响所有数组
const arr1 = [1, 2];
const arr2 = [3, 4];
arr1.customMethod === arr2.customMethod; // true (共享)// 风险 2: 可能与未来标准冲突
// 如果 ES 未来添加同名方法, 会导致不兼容// 风险 3: 影响 for...in 遍历
for (let key in [1, 2, 3]) {console.log(key); // "0", "1", "2", "customMethod"
}

可修改原型的例外情况:

// Polyfill: 为旧浏览器添加标准方法
if (!Array.prototype.includes) {Array.prototype.includes = function (searchElement) {return this.indexOf(searchElement) !== -1;};
}

现代最佳实践

不要使用原生原型作为默认值:

// 不推荐 (旧做法)
function oldWay(vals, fn, rx) {vals = vals || Array.prototype;fn = fn || Function.prototype;rx = rx || RegExp.prototype;return rx.test(vals.map(fn).join(""));
}// 推荐 (使用默认参数)
function modernWay(vals = [], fn = (v) => v, rx = /.*/) {return rx.test(vals.map(fn).join(""));
}// 或者使用解构赋值
function betterWay({ vals = [], fn = (v) => v, rx = /.*/ } = {}) {return rx.test(vals.map(fn).join(""));
}

原因:

  1. ES6+ 默认参数更清晰, 性能更好
  2. 避免意外修改原生原型
  3. 代码可读性更强

不要修改内置对象的原型:

// 不推荐: 扩展内置对象
String.prototype.reverse = function () {return this.split("").reverse().join("");
};// 推荐: 使用独立函数
function reverseString(str) {return str.split("").reverse().join("");
}// 或者使用工具类
class StringUtils {static reverse(str) {return str.split("").reverse().join("");}
}

http://www.dtcms.com/a/503298.html

相关文章:

  • 渗透测试中爆破与撞库的区别
  • 门户网站如何做谷歌seo儿童网站建设
  • AI智能体的“瑞士军刀”:工具调用功能详解与实践
  • GYCTF2020
  • 2025-10-19 hetao1733837刷题记录
  • 批量字符替换工具,支持多种格式
  • 50.情感分析:AI读懂你的心情
  • 嵌入式Linux开发环境学习(二)
  • 分析静态代码分析工具
  • unix做网站常用的数据库用php做网站后台
  • Fiddler抓包+Postman实战之--客户关系管理软件自动化测试
  • 『 数据库 』MySQL复习 - 从更新删除到分组聚合查询实践
  • 力扣2025.10.19每日一题
  • 广州站扩建百度系app
  • 品牌微信网站开发企业网站排版规则
  • Java Socket 多线程实例
  • 机器学习01——概述
  • es的docker部署和docker相关的可可视化面板工具介绍
  • Java 反射机制深度剖析:性能与安全性的那些坑
  • SQLDeveloper 调试存储过程ORA-24247
  • 网站虚拟主机过期云霄县建设局网站
  • 如何通过共享内存和寄存器溢出优化CUDA内核性能
  • ArcMap转化图片为TIF
  • Kubernetes(K8s) —— 部署(保姆级教程)
  • 用 Python 写一个自动化办公小助手
  • 《二叉树“防塌”指南:AVL 树如何用旋转 “稳住” 平衡?》
  • 网站制作wap页面wordpress微信公众平台开发
  • 分解如何利用c++修复小程序的BUG
  • 若依微服务 nacos的配置文件
  • 63.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--预算告警