js で call、bind、instanceof を手動で実装する

angryTom
リリース: 2020-02-15 17:56:13
転載
2633 人が閲覧しました

js では、call で this のポインタを変更し、bind で this のポインタを変更して関数を返すことができますが、これはどのように実装されているのでしょうか?この記事では、これらの関数を実装する方法を段階的に説明します。JavaScript を学習している友人にとって役立つことを願っています。

js で call、bind、instanceof を手動で実装する

まえがき

現在、フロントエンドのしきい値はますます高くなっており、ページはとてもシンプルです。モジュール化、自動化、クロスエンド開発などが徐々に要件になってきていますが、これらは強固な基盤の上に構築される必要があります。枠組みやモデルがどんなに変わっても、基本原則を定めて初めて市場の変化に素早く対応できるのです。一般的に使用されるソース コードの実装をいくつか示します。

呼び出し実装

バインド実装

新しい実装

実装のインスタンス

Object .create 実装

ディープ コピー実装

パブリッシュおよびサブスクライブ モード

call

call は変更に使用されます。関数 this pointer を指定し、関数

(推奨 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
}
ログイン後にコピー

使用

let obj = {
  name: 'JoJo'
}
function foo(){
  console.log(this.name)
}
foo.mycall(obj) // JoJo
ログイン後にコピー

bind

bindは関数のこのポインタを変更し、関数を返すために使用されます

注:

これはコンストラクター呼び出しを指します

プロトタイプ チェーンを維持します

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
}
ログイン後にコピー

Use

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__ を提供します。2 番目のパラメーターはオプションです。属性説明オブジェクト

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
}
ログイン後にコピー

ディープ コピー

ディープ コピーはオブジェクトの同一のコピーを作成しますが、2 つの参照アドレスは異なります。ディープ コピーは、オブジェクトを使用したいが元のオブジェクトを変更したくない場合に適しています。ここでは基本バージョンが実装されており、オブジェクトと配列のディープ コピーのみを作成します。

実装アイデア: オブジェクトを走査し、再帰を使用して参照型のコピーを継続し、基本型を直接割り当てる

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 で call、bind、instanceof を手動で実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:cnblogs.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート