ホームページ ウェブフロントエンド jsチュートリアル JS 関数の 5 つの高度なテクニックを共有します

JS 関数の 5 つの高度なテクニックを共有します

Oct 26, 2020 pm 05:57 PM
javascript 関数

JS 関数の 5 つの高度なテクニックを共有します

関数は、呼び出されたときに実行されるイベント駆動型または再利用可能なコードのブロックです。関数は、あらゆる言語、特に JavaScript の中心となる概念です。この記事では、関数に関する 5 つの高度なテクニックを詳しく紹介します。

スコープセーフ コンストラクター

コンストラクターは実際には new 演算子を使用して呼び出される関数です

function Person(name,age,job){
    this.name=name;    
    this.age=age;    
    this.job=job;
}
var person=new Person('match',28,'Software Engineer');
console.log(person.name);//match
ログイン後にコピー

new 演算子が使用されない場合、次の 3 つはもともと Person オブジェクトを対象としていた属性が window オブジェクトに追加されました

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
}
var person = Person('match', 28, 'Software Engineer');
console.log(person); //undefinedconsole.log(window.name);//match
ログイン後にコピー

window の name 属性は、リンク ターゲットとフレームを識別するために使用されます。ここでこの属性を誤って上書きすると、ページ上で他のエラーが発生する可能性があります。この問題の解決策は、スコープセーフなコンストラクターを作成することです。

function Person(name, age, job) {
    if (this instanceof Person) {
        this.name = name;
        this.age = age;
        this.job = job;
    } else {
        return new Person(name, age, job);
    }
}
var person = Person('match', 28, 'Software Engineer');
console.log(window.name); // ""
console.log(person.name); //'match'
var person= new Person('match',28,'Software Engineer');
console.log(window.name); // ""
console.log(person.name); //'match'
ログイン後にコピー

ただし、コンストラクター盗用パターンの継承には副作用が伴います。これは、次のコードでは、 this オブジェクトが Polygon オブジェクトのインスタンスではないため、コンストラクター Polygon() が新しいインスタンスを作成して返します。

function Polygon(sides) {
    if (this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function() {
            return 0;
        }
    } else {
        return new Polygon(sides);
    }
}
function Rectangle(wifth, height) {
    Polygon.call(this, 2);
    this.width = this.width;
    this.height = height;
    this.getArea = function() {
        return this.width * this.height;
    };
}
var rect = new Rectangle(5, 10);
console.log(rect.sides); //undefined
ログイン後にコピー

スコープセーフなコンストラクタースチールパターンを使用したい場合は、プロトタイプチェーンの継承を結合し、Rectangle のプロトタイプ属性を書き換えて、そのインスタンスが Polygon のインスタンスにもなるようにする必要があります。

function Polygon(sides) {
    if (this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function() {
            return 0;
        }
    } else {
        return new Polygon(sides);
    }
}
function Rectangle(wifth, height) {
    Polygon.call(this, 2);
    this.width = this.width;
    this.height = height;
    this.getArea = function() {
        return this.width * this.height;
    };
}
Rectangle.prototype = new Polygo
ログイン後にコピー

遅延読み込み関数

ブラウザ間の動作の違いのため、ブラウザの機能をチェックするために関数に多数の if ステートメントを含めることがよくあります。さまざまなブラウザとの互換性の問題を解決します。たとえば、dom ノードにイベントを追加するための最も一般的な関数

function addEvent(type, element, fun) {
    if (element.addEventListener) {
        element.addEventListener(type, fun, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + type, fun);
    } else {
        element['on' + type] = fun;
    }
}
ログイン後にコピー

addEvent 関数が呼び出されるたびに、ブラウザでサポートされている機能を確認する必要があります。まず、addEventListener メソッドがサポートされているかどうかを確認してください。サポートされていない場合は、 , サポートされている場合は、attachEvent メソッドがサポートされているかどうかを確認し、まだサポートされていない場合は、dom0 レベルのメソッドを使用してイベントを追加します。

このプロセスは、addEvent 関数が呼び出されるたびに実行する必要があります。実際、ブラウザがいずれかのメソッドをサポートしている場合は、常にそれをサポートするため、他の分岐を実行する必要はありません。検出されました。つまり、if ステートメントを毎回実行する必要がなく、コードをより高速に実行できます。

解決策は遅延読み込みです。いわゆる遅延読み込みとは、関数実行の分岐が 1 回だけ発生することを意味します。遅延読み込みを実装するには 2 つの方法があります。

1。1 つ目は、関数が呼び出されたときに関数を処理する方法です。関数が初めて呼び出されるとき、その関数は適切な方法で実行される別の関数として上書きされるため、元の関数への呼び出しは実行のブランチを経由する必要がありません。

次のメソッドを使用できます。遅延読み込みを使用して addEvent() を書き換えます

function addEvent(type, element, fun) {
    if (element.addEventListener) {
        addEvent = function(type, element, fun) {
            element.addEventListener(type, fun, false);
        }
    } else if (element.attachEvent) {
        addEvent = function(type, element, fun) {
            element.attachEvent('on' + type, fun);
        }
    } else {
        addEvent = function(type, element, fun) {
            element['on' + type] = fun;
        }
    }
    return addEvent(type, element, fun);
}
ログイン後にコピー

この遅延読み込み addEvent() では、if ステートメントの各分岐が addEvent 変数に値を割り当て、元の関数を効果的にカバーします。最後のステップは、新しい割り当て関数を呼び出すことです。次回 addEvent() を呼び出すと、新しく割り当てられた関数が直接呼び出されるため、if ステートメントを実行する必要はありません。

ただし、この方法にはデメリットがあり、関数名が変わると修正が大変になります。

2. 2 つ目は、関数の宣言時に適切な関数を指定することです。この方法では、関数が初めて呼び出されるときにパフォーマンスが低下することはありませんが、コードがロードされるときはパフォーマンスがわずかに低下するだけです。以下は、この考え方に従って addEvent() を書き直したものです。次のコードは、どの関数実装を使用するかを決定するためにさまざまな関数を分岐する匿名の自己実行関数を作成します。

var addEvent = (function() {
    if (document.addEventListener) {
        return function(type, element, fun) {
            element.addEventListener(type, fun, false);
        }
    } else if (document.attachEvent) {
        return function(type, element, fun) {
            element.attachEvent('on' + type, fun);
        }
    } else {
        return function(type, element, fun) {
            element['on' + type] = fun;
        }
    }
})();
ログイン後にコピー

関数バインディング

JavaScript と DOM の間の対話では、多くの場合、関数バインディングを使用する必要があります。関数を定義し、それを特定の DOM 要素にバインドします。イベント トリガー プログラムでは、コードの実行環境を保持しながら関数を変数として渡すために、バインディング関数がコールバック関数やイベント ハンドラーとともによく使用されます。

<button id="btn">按钮</button>
<script>var handler = {
    message: "Event handled.",
    handlerFun: function() {
      alert(this.message);
    }
  };
  btn.onclick = handler.handlerFun;</script>
ログイン後にコピー

上記のコードは、handler というオブジェクトを作成します。 handler.handlerFun() メソッドは、DOM ボタンのイベント ハンドラーとして割り当てられます。ボタンが押されると、この関数が呼び出され、アラート ボックスが表示されます。

警告ボックスには「イベントが処理されました」と表示されるはずですが、実際には「undefend」と表示されます。問題は、handler.handleClick() の環境が保存されていないため、このオブジェクトがハンドラーではなく DOM ボタンを指してしまうことです。

クロージャを使用してこの問題を解決できます

<button id="btn">按钮</button>
<script>var handler = {
    message: "Event handled.",
    handlerFun: function() {
      alert(this.message);
    }
  };
  btn.onclick = function() {
    handler.handlerFun();
  }</script>
ログイン後にコピー

もちろん、これはこのシナリオに固有の解決策であり、複数のクロージャを作成すると、コードの理解とデバッグが困難になる可能性があります。より良いアプローチは、関数バインディングを使用することです。

単純なバインディング関数 binding() は、関数と環境を受け入れ、指定された環境で指定された関数を呼び出す関数を返し、すべてのパラメーターをそのまま渡します。

function bind(fn, context) {
    return function() {
        return fn.apply(context, arguments);
    }
}
ログイン後にコピー

この関数は単純そうに見えますが、その機能は非常に強力です。クロージャは、bind() で作成されます。クロージャは、apply() を使用して受信関数を呼び出し、コンテキスト オブジェクトとパラメータを apply() に渡します。返された関数が呼び出されると、指定された環境で渡された関数が実行され、すべての引数が与えられます。

<button id="btn">按钮</button>
<script>function bind(fn, context) {
    return function() {
      return fn.apply(context, arguments);
    }
  }
  var handler = {
    message: "Event handled.",
    handlerFun: function() {
      alert(this.message);
    }
  };
  btn.onclick = bind(handler.handlerFun, handler);</script>
ログイン後にコピー

ECMAScript5 は、すべての関数に対してネイティブの binding() メソッドを定義し、操作をさらに簡素化します。

只要是将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,被绑定函数的效用就突显出来了。它们主要用于事件处理程序以及setTimeout()和setInterval()。

然而,被绑定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一点,所以最好只在必要时使用。

函数柯里化

与函数绑定紧密相关的主题是函数柯里化(function currying),它用于创建已经设置好了一个或多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。

function add(num1, num2) {
    return num1 + num2;
}
function curriedAdd(num2) {
    return add(5, num2);
}
console.log(add(2, 3)); //5
console.log(curriedAdd(3));//8
ログイン後にコピー

这段代码定义了两个函数:add()和curriedAdd()。后者本质上是在任何情况下第一个参数为5的add()版本。尽管从技术来说curriedAdd()并非柯里化的函数,但它很好地展示了其概念。

柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。

下面是创建柯里化函数的通用方式:

function curry(fn) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function() {
        var innerArgs = Array.prototype.slice.call(arguments),
        finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    };
}
ログイン後にコピー

curry()函数的主要工作就是将被返回函数的参数进行排序。curry()的第一个参数是要进行柯里化的函数,其他参数是要传入的值。

为了获取第一个参数之后的所有参数,在arguments对象上调用了slice()方法,并传入参数1表示被返回的数组包含从第二个参数开始的所有参数。然后args数组包含了来自外部函数的参数。在内部函数中,创建了innerArgs数组用来存放所有传入的参数(又一次用到了slice())。

有了存放来自外部函数和内部函数的参数数组后,就可以使用concat()方法将它们组合为finalArgs,然后使用apply()将结果传递给函数。注意这个函数并没有考虑到执行环境,所以调用apply()时第一个参数是null。curry()函数可以按以下方式应用。

function add(num1, num2) {
    return num1 + num2;
}
var curriedAdd = curry(add, 5);
alert(curriedAdd(3)); //8
ログイン後にコピー

在这个例子中,创建了第一个参数绑定为5的add()的柯里化版本。当调用cuurriedAdd()并传入3时,3会成为add()的第二个参数,同时第一个参数依然是5,最后结果便是和8。也可以像下例这样给出所有的函数参数:

function add(num1, num2) {
    return num1 + num2;
}
var curriedAdd2 = curry(add, 5, 12);
alert(curriedAdd2()); //17
ログイン後にコピー

在这里,柯里化的add()函数两个参数都提供了,所以以后就无需再传递给它们了,函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的bind()函数。

function bind(fn, context) {
    var args = Array.prototype.slice.call(arguments, 2);
    return function() {
        var innerArgs = Array.prototype.slice.call(arguments),
        finalArgs = args.concat(innerArgs);
        return fn.apply(context, finalArgs);
    };
}
ログイン後にコピー

对curry()函数的主要更改在于传入的参数个数,以及它如何影响代码的结果。curry()仅仅接受一个要包裹的函数作为参数,而bind()同时接受函数和一个object对象。

这表示给被绑定的函数的参数是从第三个开始而不是第二个,这就要更改slice()的第一处调用。另一处更改是在倒数第3行将object对象传给apply()。当使用bind()时,它会返回绑定到给定环境的函数,并且可能它其中某些函数参数已经被设好。

要想除了event对象再额外给事件处理程序传递参数时,这非常有用。

var handler = {
    message: "Event handled",
    handleClick: function(name, event){
        alert(this.message + ":" + name + ":" + event.type);
    }
};var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));
ログイン後にコピー

handler.handleClick()方法接受了两个参数:要处理的元素的名字和event对象。作为第三个参数传递给bind()函数的名字,又被传递给了handler.handleClick(),而handler.handleClick()也会同时接收到event对象。

ECMAScript5的bind()方法也实现函数柯里化,只要在this的值之后再传入另一个参数即可。

var handler = {
    message: "Event handled",
    handleClick: function(name, event) {
        alert(this.message + ":" + name + ":" + event.type);
    }
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler, "my-btn"));
ログイン後にコピー

javaScript中的柯里化函数和绑定函数提供了强大的动态函数创建功能。使用bind()还是curry()要根据是否需要object对象响应来决定。它们都能用于创建复杂的算法和功能,当然两者都不应滥用,因为每个函数都会带来额外的开销。

函数重写

由于一个函数可以返回另一个函数,因此可以用新的函数来覆盖旧的函数。

function a(){
    console.log(&#39;a&#39;);
    a = function(){
        console.log(&#39;b&#39;);
    }
}
ログイン後にコピー

这样一来,当我们第一次调用该函数时会console.log('a')会被执行;全局变量a被重定义,并被赋予新的函数

当该函数再次被调用时, console.log('b')会被执行。

再复杂一点的情况如下所示:

var a = (function() {
    function someSetup() {
        var setup = &#39;done&#39;;
    }
    function actualWork() {
        console.log(&#39;work&#39;);
    }
    someSetup();
    return actualWork;
})()
ログイン後にコピー

我们使用了私有函数someSetup()和actualWork(),当函数a()第一次被调用时,它会调用someSetup(),并返回函数actualWork()的引用。

相关免费学习推荐:js视频教程

更多编程相关知识,请访问:编程入门!!

以上がJS 関数の 5 つの高度なテクニックを共有しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

golang 関数で新しい関数を動的に作成するためのヒント golang 関数で新しい関数を動的に作成するためのヒント Apr 25, 2024 pm 02:39 PM

Go 言語は、クロージャとリフレクションという 2 つの動的関数作成テクノロジを提供します。クロージャを使用すると、クロージャ スコープ内の変数にアクセスでき、リフレクションでは FuncOf 関数を使用して新しい関数を作成できます。これらのテクノロジーは、HTTP ルーターのカスタマイズ、高度にカスタマイズ可能なシステムの実装、プラグイン可能なコンポーネントの構築に役立ちます。

C++ 関数の命名におけるパラメーターの順序に関する考慮事項 C++ 関数の命名におけるパラメーターの順序に関する考慮事項 Apr 24, 2024 pm 04:21 PM

C++ 関数の名前付けでは、読みやすさを向上させ、エラーを減らし、リファクタリングを容易にするために、パラメーターの順序を考慮することが重要です。一般的なパラメータの順序規則には、アクション-オブジェクト、オブジェクト-アクション、意味論的な意味、および標準ライブラリへの準拠が含まれます。最適な順序は、関数の目的、パラメーターの種類、潜在的な混乱、および言語規約によって異なります。

Java で効率的で保守しやすい関数を記述するにはどうすればよいでしょうか? Java で効率的で保守しやすい関数を記述するにはどうすればよいでしょうか? Apr 24, 2024 am 11:33 AM

効率的で保守しやすい Java 関数を作成するための鍵は、シンプルに保つことです。意味のある名前を付けてください。特殊な状況に対処します。適切な可視性を使用してください。

Excel関数の公式の完全なコレクション Excel関数の公式の完全なコレクション May 07, 2024 pm 12:04 PM

1. SUM 関数は、列またはセルのグループ内の数値を合計するために使用されます (例: =SUM(A1:J10))。 2. AVERAGE 関数は、列またはセルのグループ内の数値の平均を計算するために使用されます (例: =AVERAGE(A1:A10))。 3. COUNT 関数。列またはセルのグループ内の数値またはテキストの数をカウントするために使用されます。例: =COUNT(A1:A10)。 4. IF 関数。指定された条件に基づいて論理的な判断を行い、結果を返すために使用されます。対応する結果。

C++関数のデフォルトパラメータと可変パラメータの長所と短所の比較 C++関数のデフォルトパラメータと可変パラメータの長所と短所の比較 Apr 21, 2024 am 10:21 AM

C++ 関数のデフォルト パラメーターの利点には、呼び出しの簡素化、可読性の向上、エラーの回避などがあります。欠点は、柔軟性が限られていることと、名前の制限があることです。可変引数パラメーターの利点には、無制限の柔軟性と動的バインディングが含まれます。欠点としては、複雑さの増大、暗黙的な型変換、デバッグの難しさなどが挙げられます。

参照型を返す C++ 関数の利点は何ですか? 参照型を返す C++ 関数の利点は何ですか? Apr 20, 2024 pm 09:12 PM

C++ で参照型を返す関数の利点は次のとおりです。 パフォーマンスの向上: 参照による受け渡しによりオブジェクトのコピーが回避され、メモリと時間が節約されます。直接変更: 呼び出し元は、返された参照オブジェクトを再割り当てせずに直接変更できます。コードの簡素化: 参照渡しによりコードが簡素化され、追加の代入操作は必要ありません。

カスタム PHP 関数と定義済み関数の違いは何ですか? カスタム PHP 関数と定義済み関数の違いは何ですか? Apr 22, 2024 pm 02:21 PM

カスタム PHP 関数と定義済み関数の違いは次のとおりです。 スコープ: カスタム関数はその定義のスコープに限定されますが、事前定義関数はスクリプト全体からアクセスできます。定義方法: カスタム関数は function キーワードを使用して定義されますが、事前定義関数は PHP カーネルによって定義されます。パラメータの受け渡し: カスタム関数はパラメータを受け取りますが、事前定義された関数はパラメータを必要としない場合があります。拡張性: カスタム関数は必要に応じて作成できますが、事前定義された関数は組み込みで変更できません。

C++ 関数例外の詳細: カスタマイズされたエラー処理 C++ 関数例外の詳細: カスタマイズされたエラー処理 May 01, 2024 pm 06:39 PM

C++ の例外処理は、特定のエラー メッセージ、コンテキスト情報を提供し、エラーの種類に基づいてカスタム アクションを実行するカスタム例外クラスを通じて強化できます。 std::Exception から継承した例外クラスを定義して、特定のエラー情報を提供します。カスタム例外をスローするには、throw キーワードを使用します。 try-catch ブロックでdynamic_castを使用して、キャッチされた例外をカスタム例外タイプに変換します。実際の場合、open_file 関数は FileNotFoundException 例外をスローします。例外をキャッチして処理すると、より具体的なエラー メッセージが表示されます。

See all articles