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

JavaScript 中的 new 关键字和函数调用方法详解,apply、call 和 bind 的区别

一、实现 new 关键字的思路

new 关键字的作用
当使用 new 调用构造函数时,JavaScript 会执行以下操作:

创建一个新对象

将这个新对象的原型指向构造函数的 prototype 属性

将构造函数的 this 绑定到这个新对象

执行构造函数中的代码

如果构造函数没有返回对象,则返回这个新对象

实现一个自定义的 new 方法

function myNew(constructor, ...args) {// 1. 创建一个新对象,并将其原型指向构造函数的 prototypeconst obj = Object.create(constructor.prototype);// 2. 调用构造函数,并将 this 绑定到新对象const result = constructor.apply(obj, args);// 3. 如果构造函数返回了一个对象,则返回该对象,否则返回新对象return typeof result === 'object' && result !== null ? result : obj;
}// 测试用例
function Person(name, age) {this.name = name;this.age = age;
}Person.prototype.sayHello = function() {console.log(`Hello, my name is ${this.name}`);
};const p1 = new Person('Alice', 25);
const p2 = myNew(Person, 'Bob', 30);p1.sayHello(); // Hello, my name is Alice
p2.sayHello(); // Hello, my name is Bob

二、apply、call 和 bind 的区别

这三个方法都用于改变函数执行时的 this 指向,但有一些关键区别:
1. call 方法

func.call(thisArg, arg1, arg2, ...)

立即执行函数

第一个参数是 this 的指向

后续参数是传递给函数的参数列表

示例:

function greet(greeting, punctuation) {console.log(`${greeting}, ${this.name}${punctuation}`);
}const person = { name: 'Alice' };greet.call(person, 'Hello', '!'); // Hello, Alice!

2. apply 方法

func.apply(thisArg, [argsArray])

立即执行函数

第一个参数是 this 的指向

第二个参数是一个数组或类数组对象,包含传递给函数的参数

function greet(greeting, punctuation) {console.log(`${greeting}, ${this.name}${punctuation}`);
}const person = { name: 'Bob' };greet.apply(person, ['Hi', '!!']); // Hi, Bob!!

3. bind 方法

func.bind(thisArg, arg1, arg2, ...)

不立即执行函数,而是返回一个新函数

第一个参数是 this 的指向

可以预先设置部分参数(柯里化)

返回的函数可以稍后调用

function greet(greeting, punctuation) {console.log(`${greeting}, ${this.name}${punctuation}`);
}const person = { name: 'Charlie' };
const greetCharlie = greet.bind(person, 'Hey');greetCharlie('...'); // Hey, Charlie...

三、实现 apply 或 call 方法

实现 call 方法

Function.prototype.myCall = function(context, ...args) {// 如果 context 为 null 或 undefined,则指向全局对象(浏览器中是 window)context = context || globalThis;// 创建一个唯一的属性名,避免覆盖原有属性const fnKey = Symbol('fn');// 将当前函数(this)作为 context 的一个方法context[fnKey] = this;// 调用该方法,此时 this 指向 contextconst result = context[fnKey](...args);// 删除临时添加的方法delete context[fnKey];return result;
};// 测试用例
function showInfo(age, city) {console.log(`${this.name}, ${age}, ${city}`);
}const person = { name: 'David' };showInfo.myCall(person, 28, 'New York'); // David, 28, New York

实现 apply 方法

Function.prototype.myApply = function(context, argsArray) {// 如果 context 为 null 或 undefined,则指向全局对象context = context || globalThis;// 检查 argsArray 是否是数组或类数组if (argsArray && typeof argsArray !== 'object') {throw new TypeError('CreateListFromArrayLike called on non-object');}// 创建一个唯一的属性名const fnKey = Symbol('fn');// 将当前函数作为 context 的一个方法context[fnKey] = this;// 调用该方法,传入数组参数const result = context[fnKey](...(argsArray || []));// 删除临时添加的方法delete context[fnKey];return result;
};// 测试用例
function showDetails(profession, hobby) {console.log(`${this.name} is a ${profession} who loves ${hobby}`);
}const person = { name: 'Eva' };showDetails.myApply(person, ['developer', 'hiking']); 
// Eva is a developer who loves hiking

实现 bind 方法

Function.prototype.myBind = function(context, ...bindArgs) {const originalFunc = this;return function(...callArgs) {// 如果是作为构造函数调用(使用 new),则忽略绑定的 thisif (new.target) {return new originalFunc(...bindArgs, ...callArgs);}return originalFunc.apply(context, [...bindArgs, ...callArgs]);};
};// 测试用例
function introduce(greeting, punctuation) {console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}const person = { name: 'Frank' };
const boundIntroduce = introduce.myBind(person, 'Hi');boundIntroduce('!'); // Hi, I'm Frank!// 作为构造函数测试
function Person(name, age) {this.name = name;this.age = age;
}const BoundPerson = Person.myBind(null, 'DefaultName');
const p = new BoundPerson(30);
console.log(p.name, p.age); // DefaultName 30

new.target 的作用

new.target 是 JavaScript 中的一个 元属性(meta property),它用于检测函数是否是通过 new 关键字调用的:

如果函数是通过 new 调用的(如 new Foo()),new.target 会返回该函数的引用(即 Foo)。

如果函数是普通调用(如 Foo()),new.target 是 undefined。

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

相关文章:

  • 解决MySQL不能编译存储过程的问题
  • session和cookie作用详解
  • 如何使用AI+工单实现高效率高质量的服务?
  • python 断点pdb
  • 选择美颜sdk时应该关注什么?美白滤镜效果与兼容性对比评测
  • 【人工智能-17】机器学习:KNN算法、模型选择和调优、朴素贝叶斯分类
  • JS核心语法与实战技巧
  • 如何实现长时间录音的自动分段与文本生成?
  • 自定义View学习记录之 折线图View
  • 栈与队列的泛型实现
  • gcc g++ makefile CMakeLists.txt cmake make 的关系
  • [lvgl_player] 用户界面(LVGL) | 播放器核心设计
  • 桌面端界面设计 |货物 TMS 系统 - SaaS UI UX 设计:审美积累之境
  • 图像处理拉普拉斯算子
  • 进阶08:Winform编写与SQL Server通信范例
  • 【OD机试题解法笔记】考古学家考古问题
  • SOLIDWORKS材料明细表设置,属于自己的BOM表模板
  • 【数据结构】-----排序的艺术画卷
  • 上海月赛kk
  • 1.2.6 装配式混凝土建筑设计构造要求
  • LOVON——面向足式Open-Vocabulary的物体导航:LLM做任务分解、YOLO11做目标检测,最后L2MM将指令和视觉映射为动作(且解决动态模糊)
  • RAGFLOW~knowledge graph
  • JavaScript 中的对象继承:从浅入深
  • 2025牛客多校第六场D题解
  • Object对象中的常用方法
  • 当10米精度遇上64维AI大脑——Google全球卫星嵌入数据集(Satellite Embedding V1)全解析
  • 【华为机试】34. 在排序数组中查找元素的第一个和最后一个位置
  • 移动端 WebView 内存泄漏与性能退化问题如何排查 实战调试方法汇总
  • 文章发布Typecho网站技巧
  • Squid服务配置代理