简要回顾一下,温故而知新
this指向和Call、Bind、Apply
调用方式 | 示例 | 函数中的this指向 |
---|---|---|
通过new调用 | new method() | 新对象 |
直接调用 | Method() | 全局对象globalThis |
通过对象调用 | obj.method | 前面的对象 |
Call,apply,bind | Method.call(ctx) | 第一个参数 |
手写new
使用new
关键字创建对象的过程涉及以下步骤:
- 创建一个新的空对象。
- 将这个空对象的原型(
__proto__
)指向构造函数(即被new
调用的函数)的原型对象。 - 将构造函数的作用域(
this
)绑定到新创建的对象上,以便在构造函数中可以使用this
关键字引用新对象。 - 执行构造函数内部的代码,对新对象进行初始化操作。
- 如果构造函数没有显式返回其他对象,则返回这个新对象;否则返回构造函数内显式返回的对象。
这些步骤使得通过new
关键字创建的对象能够继承构造函数的属性和方法,并且可以通过原型链访问到构造函数原型对象上定义的属性和方法。
使用
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.printName = function () {
console.log('this: ', this)
console.log('this.name: ', this.name)
}
const sxc = new Person('sxc', 23)
console.log('sxc: ', sxc)
sxc.printName()
console.log('-------------------------')
// 函数返回对象的情况
function Person1(name, age) {
this.name = name
this.age = age
return {
name: '固定名字',
length: '18cm',
printName: function () {
console.log('this: ', this)
console.log('this.name: ', this.name)
}
}
}
const sxc01 = new Person1('sxc', 23)
console.log('sxc01: ', sxc01)
sxc01.printName()
// 打印
sxc: Person { name: 'sxc', age: 23 }
this: Person { name: 'sxc', age: 23 }
this.name: sxc
-------------------------
sxc01: { name: '固定名字', length: '18cm', printName: [Function: printName] }
this: { name: '固定名字', length: '18cm', printName: [Function: printName] }
this.name: 固定名字
实现myNew
function myNew(constructor, ...allArgs) {
// 创建一个新的空对象
const obj = {}
// 将这个空对象的原型(__proto__)指向构造函数(即被new调用的函数)的原型对象
Object.setPrototypeOf(obj, constructor.prototype)
// 将构造函数的作用域(this)绑定到新创建的对象上,以便在构造函数中可以使用this关键字引用新对象
const res = constructor.apply(obj, allArgs)
// 如果构造函数没有显式返回其他对象,则返回这个新对象;否则返回构造函数内显式返回的对象
return Object.prototype.toString.call(res) === '[object Object]' ? res : obj
}
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.printName = function () {
console.log('this: ', this)
console.log('this.name: ', this.name)
}
const sxc = new Person('sxc', 23)
console.log('sxc: ', sxc)
sxc.printName()
console.log('-------------------------')
// 函数返回对象的情况
function Person1(name, age) {
this.name = name
this.age = age
return {
name: '固定名字',
length: '18cm',
printName: function () {
console.log('this: ', this)
console.log('this.name: ', this.name)
}
}
}
const sxc01 = new Person1('sxc', 23)
console.log('sxc01: ', sxc01)
sxc01.printName()
console.log('-------------------------')
function myNew(constructor, ...allArgs) {
// 创建一个新的空对象
const obj = {}
// 将这个空对象的原型(__proto__)指向构造函数(即被new调用的函数)的原型对象
Object.setPrototypeOf(obj, constructor.prototype)
// 将构造函数的作用域(this)绑定到新创建的对象上,以便在构造函数中可以使用this关键字引用新对象
const res = constructor.apply(obj, allArgs)
// 如果构造函数没有显式返回其他对象,则返回这个新对象;否则返回构造函数内显式返回的对象
return Object.prototype.toString.call(res) === '[object Object]' ? res : obj
}
const sxc03 = myNew(Person, 'sxc03', 23)
console.log('sxc03: ', sxc03)
sxc03.printName()
console.log('-------------------------')
const sxc04 = myNew(Person1, 'sxc04', 23)
console.log('sxc04: ', sxc04)
sxc04.printName()
console.log('-------------------------')
// 打印
sxc: Person { name: 'sxc', age: 23 }
this: Person { name: 'sxc', age: 23 }
this.name: sxc
-------------------------
sxc01: { name: '固定名字', length: '18cm', printName: [Function: printName] }
this: { name: '固定名字', length: '18cm', printName: [Function: printName] }
this.name: 固定名字
-------------------------
sxc03: Person { name: 'sxc03', age: 23 }
this: Person { name: 'sxc03', age: 23 }
this.name: sxc03
-------------------------
sxc04: { name: '固定名字', length: '18cm', printName: [Function: printName] }
this: { name: '固定名字', length: '18cm', printName: [Function: printName] }
this.name: 固定名字
-------------------------
手写Bind
Function.prototype.myBind = function (ctx, ...boundArgs) {
const fn = this
// 特殊处理
const resFunc = function () {
const restArgs = [...arguments]
const totalArgs = boundArgs.concat(restArgs)
if(Object.getPrototypeOf(this) === resFunc.prototype){
// 当前是使用new的方式调用的这个函数
return new fn(...totalArgs)
} else {
// 其他情况
return fn.apply(ctx, totalArgs)
}
}
return resFunc
}
常规使用
function func(a, b, c, d) {
console.log(this)
console.log(a, b, c, d)
}
const boundFunc1 = func.bind({ a: 1 }, 1, 2)(3, 4)
const boundFunc2 = new (func.bind({ b: 1 }, 11, 12))(13, 14)
const boundFunc3 = func.myBind({ c: 1 }, 21, 22)(23, 24)
const boundFunc4 = new (func.bind({ d: 1 }, 31, 32))(33, 34)
// 打印结果
{ a: 1 }
1 2 3 4
func {}
11 12 13 14
{ c: 1 }
21 22 23 24
func {}
31 32 33 34
手写Call
Function.prototype.myCall = function (ctx,...args) {
ctx = ctx === undefined || ctx === null ? globalThis : ctx
// 待执行的函数
const fn = this
const fnKey = Symbol('fn')
Object.defineProperty(ctx, fnKey, {
enumerable: false,
value: fn,
})
const fnRes = ctx[fnKey](...args)
delete ctx[fnKey]
return fnRes
}
效果
const a = () => {
return 1
}
console.log(Object.prototype.toString.call(a) === '[object Function]')
function sayHello(info) {
console.log('Hello, ' + this.name + '!' + (info || ''))
}
const person1 = { name: 'Alice' }
const person2 = { name: 'Bob' }
// 使用 call 方法调用 sayHello,并指定不同的 this 值
sayHello.call(person1) // 输出: Hello, Alice
sayHello.call(person2) // 输出: Hello, Bob
console.log('-----------------------')
const person3 = { name: 'JJ' }
sayHello.myCall(person3, 123123)
console.log('person3: ', person3);
console.log(Object.prototype.toString.myCall(a) === '[object Function]')
手写Apply
Function.prototype.myApply = function (ctx, args) {
ctx = ctx === undefined || ctx === null ? globalThis : ctx
// 待执行的函数
const fn = this
const fnKey = Symbol('fn')
Object.defineProperty(ctx, fnKey, {
enumerable: false,
value: fn
})
const fnRes = ctx[fnKey](...(args || []))
delete ctx[fnKey]
return fnRes
}
效果
const a = () => {
return 1
}
console.log(Object.prototype.toString.apply(a) === '[object Function]')
function sayHello(info) {
console.log('Hello, ' + this.name + '!' + (info || ''))
}
const person1 = { name: 'Alice' }
const person2 = { name: 'Bob' }
// 使用 call 方法调用 sayHello,并指定不同的 this 值
sayHello.apply(person1)
sayHello.apply(person2)
console.log('-----------------------')
const person3 = { name: 'JJ' }
sayHello.myApply(person3, [123123])
console.log('person3: ', person3);
console.log(Object.prototype.toString.myApply(a) === '[object Function]')
原文链接:https://juejin.cn/post/7352769502129946676 作者:豆子前端