[js基础]this指向和Call、Bind、Apply原理Review

简要回顾一下,温故而知新

this指向和Call、Bind、Apply

调用方式 示例 函数中的this指向
通过new调用 new method() 新对象
直接调用 Method() 全局对象globalThis
通过对象调用 obj.method 前面的对象
Call,apply,bind Method.call(ctx) 第一个参数

手写new

使用new关键字创建对象的过程涉及以下步骤:

  1. 创建一个新的空对象。
  2. 将这个空对象的原型(__proto__)指向构造函数(即被new调用的函数)的原型对象。
  3. 将构造函数的作用域(this)绑定到新创建的对象上,以便在构造函数中可以使用this关键字引用新对象。
  4. 执行构造函数内部的代码,对新对象进行初始化操作。
  5. 如果构造函数没有显式返回其他对象,则返回这个新对象;否则返回构造函数内显式返回的对象。

这些步骤使得通过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 作者:豆子前端

(0)
上一篇 2024年4月1日 下午4:00
下一篇 2024年4月1日 下午4:10

相关推荐

发表评论

登录后才能评论