>  기사  >  웹 프론트엔드  >  js에서 호출, 바인드, 인스턴스를 수동으로 구현합니다.

js에서 호출, 바인드, 인스턴스를 수동으로 구현합니다.

angryTom
angryTom앞으로
2020-02-15 17:55:282561검색

JS에서 호출은 이것의 지점을 변경할 수 있고, 바인드는 이것의 지점을 변경할 수 있으며, 함수를 반환할 수 있습니다. 이것은 어떻게 구현되나요? 이 글은 이러한 기능을 구현하는 방법을 단계별로 설명할 것이며, JavaScript를 배우는 친구들에게 도움이 되기를 바랍니다.

js에서 호출, 바인드, 인스턴스를 수동으로 구현합니다.

머리말

이제 프런트엔드의 문턱이 점점 높아지고 있으며 더 이상 페이지 작성만큼 간단하지 않습니다. 모듈화, 자동화, 크로스엔드 개발 등은 점차 요구 사항이 되고 있지만 이러한 것들은 우리의 견고한 기반 위에 구축되어야 합니다. 프레임워크와 모델이 어떻게 바뀌더라도 기본 원칙을 정해야만 시장 변화에 빠르게 적응할 수 있습니다. 일반적으로 사용되는 소스 코드 구현은 다음과 같습니다.

호출 구현

바인드 구현

새 구현

구현 인스턴스

Object.create 구현

deep copy 구현

게시 및 구독 모드

call

call 함수의 this 지점을 변경하고 함수를 실행하는 데 사용됩니다.

(추천 js 튜토리얼 , 환영합니다!)

일반적으로 함수를 호출하는 사람이 누구인지 함수의 this는 누구를 가리킵니다. 이 기능을 활용하여 함수를 객체의 속성으로 사용하고 객체에서 호출하면 함수의 this 지점을 변경할 수 있습니다. 이를 암시적 바인딩이라고 합니다. Apply는 동일한 방법을 구현하며, 입력 매개변수 형식만 변경하면 됩니다.

let obj = {
  name: 'JoJo'
}
function foo(){
  console.log(this.name)
}
obj.fn = foo
obj.fn() // log: JOJO

Function.prototype.mycall = function () {
  if(typeof this !== 'function'){
    throw 'caller must be a function'
  }
  let othis = arguments[0] || window
  othis._fn = this
  let arg = [...arguments].slice(1)
  let res = othis._fn(...arg)
  Reflect.deleteProperty(othis, '_fn') //删除_fn属性
  return res
}

bind

bind를 사용하여

let obj = {
  name: 'JoJo'
}
function foo(){
  console.log(this.name)
}
foo.mycall(obj) // JoJo

를 구현하고 함수의 this 지점을 변경하고 함수를 반환합니다

참고:

생성자 호출의 this 포인터로

Maintain the 프로토타입 체인

Function.prototype.mybind = function (oThis) {
  if(typeof this != 'function'){
    throw 'caller must be a function'
  }
  let fThis = this
  //Array.prototype.slice.call 将类数组转为数组
  let arg = Array.prototype.slice.call(arguments,1)
  let NOP = function(){}
  let fBound = function(){
    let arg_ = Array.prototype.slice.call(arguments)
    // new 绑定等级高于显式绑定
    // 作为构造函数调用时,保留指向不做修改
    // 使用 instanceof 判断是否为构造函数调用
    return fThis.apply(this instanceof fBound ? this : oThis, arg.concat(arg_))
  }
  // 维护原型
  if(this.prototype){
    NOP.prototype = this.prototype
  }
  fBound.prototype = new NOP()
  return fBound
}

생성자를 사용하여 인스턴스 객체를 생성하려면

let obj = {
  msg: 'JoJo'
}
function foo(msg){
  console.log(msg + '' + this.msg)
}
let f = foo.mybind(obj)
f('hello') // hello JoJo

new

new를 사용하고 이 속성과 메소드를 인스턴스 객체에 추가하세요.

new 프로세스:

새 객체 생성

새 객체 __proto__는 생성자 프로토타입을 가리킵니다.

새 객체 추가 속성 메서드(this를 가리킴)

this

function new_(){
  let fn = Array.prototype.shift.call(arguments)
  if(typeof fn != 'function'){
    throw fn + ' is not a constructor'
  }
  let obj = {}
  obj.__proto__ = fn.prototype
  let res = fn.apply(obj, arguments)
  return typeof res === 'object' ? res : obj
}

instanceof

instanceof가 가리키는 새 객체를 반환하여 왼쪽에 프로토타입이 존재하는지 확인합니다. 오른쪽 프로토타입 체인에 있습니다.

구현 아이디어: 프로토타입을 레이어별로 찾아보세요. 최종 프로토타입이 null이면 프로토타입 체인에 존재하지 않는다는 것을 증명하고, 그렇지 않으면 존재합니다.

function instanceof_(left, right){
  left = left.__proto__
  while(left !== right.prototype){
    left = left.__proto__ // 查找原型,再次while判断
    if(left === null){
      return false
    }
  }
  return true
}

Object.create

Object.create는 새로 생성된 객체의 __proto__를 제공하기 위해 기존 객체를 사용하여 새 객체를 생성합니다. 두 번째 선택적 매개변수는 속성 설명 object

function objectCreate_(proto, propertiesObject = {}){
  if(typeof proto !== 'object' || typeof proto !== 'function' || proto !== null){
    throw('Object prototype may only be an Object or null:'+proto)
  }
  let res = {}
  res.__proto__ = proto
  Object.defineProperties(res, propertiesObject)
  return res
}

deep copy

입니다. Deep Copy는 객체의 동일한 복사본을 생성하지만 둘의 참조 주소는 다릅니다. 깊은 복사는 개체를 사용하고 싶지만 원본 개체를 수정하고 싶지 않을 때 좋은 선택입니다. 여기서는 기본 버전이 구현되어 객체와 배열의 전체 복사본만 만듭니다.

구현 아이디어: 객체를 탐색하고, 재귀를 사용하여 참조 유형을 계속 복사하고, 기본 유형에 값을 직접 할당합니다.

function deepClone(origin) {
  let toStr = Object.prototype.toString
  let isInvalid = toStr.call(origin) !== '[object Object]' && toStr.call(origin) !== '[object Array]'
  if (isInvalid) {
    return origin
  }
  let target = toStr.call(origin) === '[object Object]' ? {} : []
  for (const key in origin) {
    if (origin.hasOwnProperty(key)) {
      const item = origin[key];
      if (typeof item === 'object' && item !== null) {
        target[key] = deepClone(item)
      } else {
        target[key] = item
      }
    }
  }
  return target
}

게시 및 구독 모드

게시 및 구독 모드는 실제 개발에서 모듈 간의 완전한 분리를 달성할 수 있습니다. 모듈은 이벤트 등록 및 트리거링에만 주의하면 됩니다.

EventBus를 구현하기 위한 모델 게시 및 구독:

class EventBus{
  constructor(){
    this.task = {}
  }

  on(name, cb){
    if(!this.task[name]){
      this.task[name] = []
    }
    typeof cb === 'function' && this.task[name].push(cb)
  }

  emit(name, ...arg){
    let taskQueen = this.task[name]
    if(taskQueen && taskQueen.length > 0){
      taskQueen.forEach(cb=>{
        cb(...arg)
      })
    }
  }

  off(name, cb){
    let taskQueen = this.task[name]
    if(taskQueen && taskQueen.length > 0){
      let index = taskQueen.indexOf(cb)
      index != -1 && taskQueen.splice(index, 1)
    }
  }

  once(name, cb){
    function callback(...arg){
      this.off(name, cb)
      cb(...arg)
    }
    typeof cb === 'function' && this.on(name, callback)
  }
}

사용

let bus = new EventBus()
bus.on('add', function(a,b){
  console.log(a+b)
})
bus.emit('add', 10, 20) //30

위 내용은 js에서 호출, 바인드, 인스턴스를 수동으로 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 cnblogs.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제