第 1 卷
手写JavaScript核心内置方法与Promise实现
你需要手写 new、 call、 apply、 bind 操作符/函数以及简易版Promise,下面我将逐一提供完整可运行的实现代码,并附带核心原理说明。
一、手写new操作符
核心原理
- 创建一个新的空对象,该对象的原型会指向构造函数的 prototype 属性
- 将构造函数的 this 绑定到这个新创建的对象上
- 执行构造函数的代码(为新对象添加属性和方法)
- 判断构造函数的返回值:若返回值是引用类型(对象/函数等),则返回该引用类型;否则返回新创
建的对象
实现代码
Code block
1function myNew(constructor, ...args) {
| 2 | // 1.创建空对象,且让该对象的__proto__指向构造函数的prototype |
|---|---|
| 3 | const newObj = Object.create(constructor.prototype); |
| 4 | // 2.绑定this,执行构造函数 |
| 5 | const result = constructor.apply(newObj, args); |
| 6 | // 3.判断返回值,返回对应结果 |
| 7 | return (typeof result === 'object' && result !== null) || typeof result |
=== 'function' 8? result
9: newObj;
10} 11
12// 测试示例 13function Person(name, age) {
14this.name = name;
15this.age = age;
16this.sayHi = function() { 17console.log(`我是${this.name},今年${this.age}岁`);
二、手写call函数
核心原理
1.call 的作用是改变函数的this指向,并立即执行该函数
- 为目标上下文(要绑定的this)添加一个临时方法,该方法指向当前函数
- 执行这个临时方法,并传入后续参数
- 删除临时方法,避免污染目标上下文
- 返回函数执行的结果
实现代码
Code block
1Function.prototype.myCall = function(context, ...args) { 2// 1. 处理默认this:若context为null/undefined,默认绑定全局对象(浏览器:window,
Node:globalThis)
| 3 | const ctx = context || globalThis; |
|---|---|
| 4 | // 2.创建唯一临时属性,避免覆盖ctx原有属性 |
| 5 | const tempFnKey = Symbol('tempFn'); |
| 6 | // 3.将当前函数(this,因为myCall是函数原型方法,this指向调用者函数)挂载到ctx上 |
| 7 | ctx[tempFnKey] = this; |
| 8 | // 4.执行临时方法,传入参数 |
| 9 | const result = ctx[tempFnKey](...args); |
| 10 | // 5.删除临时方法,清理环境 |
| 11 | delete ctx[tempFnKey]; |
| 12 | // 6.返回函数执行结果 |
| 13 | return result; |
| 14 }; |
15 16// 测试示例
17const obj = { name: '李四' };
18function fn(a, b) {
| 19 | console.log(`this.name:${this.name},参数:${a} + ${b} = ${a + b}`); |
|---|---|
| 20 | return a + b; |
| 22 | //使用手写myCall |
| 23 | const res1 = fn.myCall(obj, 10, 20); |
三、手写apply函数
核心原理
1.apply 与 call 功能一致,唯一区别是参数传递形式: apply 的后续参数是一个数组(或类数组)
- 其余逻辑与 call 一致:绑定this、临时挂载函数、执行并清理、返回结果
实现代码
Code block
1Function.prototype.myApply = function(context, argsArr) { 2// 1. 处理默认this
| 3 | const ctx = context || globalThis; |
|---|---|
| 4 | // 2.处理参数:若未传入argsArr,默认是空数组 |
| 5 | const args = Array.isArray(argsArr) ? argsArr : []; |
| 6 | // 3.创建唯一临时属性 |
| 7 | const tempFnKey = Symbol('tempFn'); |
| 8 | // 4.挂载当前函数到ctx |
| 9 | ctx[tempFnKey] = this; |
| 10 | // 5.执行临时方法,展开数组参数 |
| 11 | const result = ctx[tempFnKey](...args); |
| 12 | // 6.删除临时方法 |
| 13 | delete ctx[tempFnKey]; |
| 14 | // 7.返回执行结果 |
| 15 | return result; |
| 16 }; |
17 18// 测试示例
19const obj2 = { name: '王五' };
20function fn2(a, b) {
| 21 | console.log(`this.name:${this.name},参数:${a} * ${b} = ${a * b}`); |
|---|---|
| 22 | return a * b; |
| 23 | } |
| 24 | //使用手写myApply |
| 25 | const res2 = fn2.myApply(obj2, [10, 20]); |
| 26 | //输出:this.name:王五,参数:10 * 20 = 200 |
| 27 | console.log(res2);// 200 |