ホームページ php教程 PHP开发 ES6 の引数とパラメータの使用状況分析

ES6 の引数とパラメータの使用状況分析

Dec 14, 2016 am 09:53 AM

ECMAScript 6 (ECMAScript 2015 とも呼ばれます) は ECMAScript 標準の最新バージョンであり、JS でのパラメーターの処理方法が大幅に改善されています。他の新機能に加えて、残りのパラメーター、デフォルト値、代入の構造化なども使用できます。

このチュートリアルでは、引数とパラメーターを詳細に調査し、ES6 がそれらをどのように改善するかを見ていきます。

引数とパラメータの比較リンク

通常、引数とパラメータについて言及する場合、それらは同じ意味で使用されるものとみなされます。ただし、このチュートリアルでは明確に区別します。ほとんどの標準では、パラメーター (仮パラメーター) は関数名と関数本体を宣言するときに使用されるパラメーターを指しますが、引数 (実パラメーター) は関数が実際に呼び出されるときに渡される特定の値を指します。次の関数について考えてみましょう:

function foo(param1, param2) {
    // do something
}
foo(10, 20);
ログイン後にコピー

この関数では、param1 と param2 は関数の仮パラメータ (仮パラメータ) であり、関数 foo が呼び出されたときに渡される ( 10 と 20 ) は実際のパラメータ (実際のパラメータ) です。パラメーター)。

拡張演算子 Link

ES5 では、apply() メソッドを使用して関数に配列を簡単に渡すことができます。たとえば、配列内の最大値を取得するために、Math.max() と組み合わせて使用​​することがよくあります。以下のコードを見てください:

var myArray = [5, 10, 50];
Math.max(myArray);    // Error: NaN
Math.max.apply(Math, myArray);    // 50
ログイン後にコピー

Math.max() メソッドは配列の受け渡しをサポートしておらず、数値のみを受け入れます。したがって、配列をパラメータとして渡すと、エラーがスローされます。ただし、apply() メソッドを使用すると、配列は個別の数値に変換され、Math.max() で処理できます。

ありがたいことに、ES6 ではスプレッド演算子が導入されたため、apply() メソッドを使用する必要がなくなりました。展開演算子を使用すると、式の複数のパラメーターを簡単に渡すことができます:

var myArray = [5, 10, 50];
Math.max(...myArray);    // 50
ログイン後にコピー

ここで、展開演算子は myArray を独立した値に展開し、関数に渡します。 ES5 で apply() を使用して演算子を模倣すると目的は達成できますが、構文が複雑で、拡張演算子の柔軟性に欠けています。 スプレッド演算子は使いやすいだけでなく、他の多くの機能もカバーします。たとえば、関数呼び出しで複数回使用したり、他のパラメータと組み合わせたりすることができます:

function myFunction() {  for(var i in arguments){    console.log(arguments[i]);
  }
}var params = [10, 15];
myFunction(5, ...params, 20, ...[25]);    // 5 10 15 20 25
ログイン後にコピー

スプレッド演算子のもう 1 つの利点は、コンストラクターで簡単に使用できることです:

new Date(...[2016, 5, 6]);    // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)`
ログイン後にコピー

もちろん、上記のコードを次のように書き直すこともできます。 ES5 ですが、型エラーを避けるために複雑なパターンを使用する必要があります:

new Date.apply(null, [2016, 4, 24]);    // TypeError: Date.apply is not a constructor
new (Function.prototype.bind.apply(Date, [null].concat([2016, 5, 6])));   // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)
ログイン後にコピー

関数で呼び出される演算子を拡散するためのブラウザのサポート Link

デスクトップ ブラウザ;

浏览器对Rest参数的支持情况 Link

モバイル ブラウザ:

浏览器对Rest参数的支持情况 Link

REST パラメータ Link

残りのパラメータと展開演算子の構文は同じです。違いは、残りのパラメータはすべてのパラメータを収集して配列に変換するのに対し、展開演算子は配列を個別のパラメータに展開することです。

function myFunction(...options) {     return options;
}
myFunction('a', 'b', 'c');      // ["a", "b", "c"]
ログイン後にコピー

関数の呼び出し時に実際のパラメータが渡されない場合、rest パラメータは次のように空の配列を出力します:

function myFunction(...options) {
     return options;
}myFunction();      // []
ログイン後にコピー

rest パラメータは、変数関数 (つまり、パラメータの可変数) 。残りのパラメーターには配列本来の利点があり、引数オブジェクトをすぐに置き換えることができます (この用語については後で説明します)。次の関数は ES5 で書かれています。見てみましょう:

function checkSubstrings(string) {
  for (var i = 1; i < arguments.length; i++) {
    if (string.indexOf(arguments[i]) === -1) {
      return false;
    }
  }
  return true;
}
checkSubstrings(&#39;this is a string&#39;, &#39;is&#39;, &#39;this&#39;);   // true
ログイン後にコピー

この関数は、文字列 ( this is a string ) にこれらの部分文字列 ( is 、 this ) が含まれているかどうかをチェックします。この関数の最初の問題は、関数本体に複数のパラメータがあるかどうかを確認する必要があることです。 2 番目の問題は、arguments[0] が最初のパラメータ (これは文字列) を指しているため、ループは 0 からではなく 1 から開始する必要があることです。 後でこの文字列の前後に別のパラメータを追加する場合、ループ本体の更新を忘れる可能性があります。 rest パラメーターを使用すると、これらの問題を簡単に回避できます:

function checkSubstrings(string, ...keys) {
  for (var key of keys) {
    if (string.indexOf(key) === -1) {
      return false;
    }
  }
  return true;
}
checkSubstrings(&#39;this is a string&#39;, &#39;is&#39;, &#39;this&#39;);   // true
ログイン後にコピー

この関数の出力は、前の関数の出力と同じです。ここでもう一度述べておきますが、この関数の引数にはパラメータ文字列が含まれており、最初のパラメータが渡され、残りのパラメータは配列に入れられ、keys という名前の変数に割り当てられます。

コードの可読性を向上させ、JS 最適化の問題を回避するには、引数オブジェクトの代わりに残りのパラメーターを使用します。 ただし、残りのパラメータには欠点がないわけではありません。たとえば、これは最後のパラメータである必要があります。そうでない場合は、次の関数に示すようにエラーが報告されます:

function logArguments(a, ...params, b) {
        console.log(a, params, b);
}
logArguments(5, 10, 15);    // SyntaxError: parameter after rest parameter
ログイン後にコピー

もう 1 つの欠点は、関数宣言で 1 つの残りパラメータしか許可できないことです:

function logArguments(...param1, ...param2) {
}
logArguments(5, 10, 15);    // SyntaxError: parameter after rest parameter
ログイン後にコピー

残りパラメータのブラウザ サポート リンク

デスクトップブラウザ:

浏览器对Rest参数的支持情况 Link

移动端浏览器:

浏览器对Rest参数的支持情况 Link

默认参数 Link

ES5 默认参数 Link

在ES5中,JS 并不支持默认参数, 但是,我们也有一种变通的方案,那就是在函数中使用 OR 操作符( || )。我们简单地模仿ES5中的默认参数,请看下面函数:

function foo(param1, param2) {
   param1 = param1 || 10;
   param2 = param2 || 10;
   console.log(param1, param2);
}
foo(5, 5);  // 5 5
foo(5);    // 5 10
foo();    // 10 10
ログイン後にコピー

该函数预期传入两个参数,但如果在调用该函数时,没有传入实参,则它会用默认值。在函数体内,如果没有传入实际参数,则会被自动设为 undefined, 所以,我们可以检测这些参数,并且声明他们的默认值。我们可以使用 OR 操作符( || )来检测是否有传入实际参数,并且设定他们的默认值。 OR 操作符会检测它的第一个参数,如果有 实际值 2 ,则用第一个,如果没有,则用它的第二个参数。

这种方法在函数中普遍使用,但它有一个瑕疵,那就是传入 0 或者 null 也会触发默认值,因为 0 和 null 都被认为是false. 所以,如果我们需要给函数传入 0 和 null 时,我们需要另一种方式去检测这个参数是否缺失:

function foo(param1, param2) { 
 if(param1 === undefined){
    param1 = 10;
  }  if(param2 === undefined){
    param2 = 10;
  }  console.log(param1, param2);
}
foo(0, null);    // 0, nullfoo();    // 10, 10
ログイン後にコピー

在上面这个函数中,只有当所传的参数全等于 undefined 时,才会使用默认值。这种方式需要用到的代码稍微多点,但是安全度更高,我们可以给函数传入 0 和 null。

ES6 默认参数 Link

有了ES6,我们不需要再去检测哪些值为undefined并且给它们设定默认值了。现在我们可以直接在函数声明中放置默认值:

function foo(a = 10, b = 10) {
  console.log(a, b);
}
foo(5);    // 5 10
foo(0, null);    // 0 null
ログイン後にコピー

如你所见,省略一个参数,就会触发一个默认值,但是传入 0 或者 null 时,则不会。 我们甚至可以使用函数去找回默认参数的值:

function getParam() {
    alert("getParam was called");
    return 3;
}
function multiply(param1, param2 = getParam()) {
    return param1 * param2;
}
multiply(2, 5);     // 10
multiply(2);     // 6 (also displays an alert dialog)
ログイン後にコピー

注意 getParam 这个函数只有在第二个参数省略时才会被调用。所以当我们给 multiply 传入两个参数并调用它时,alert是不会出现的。

默认参数还有一个有趣的特性,那就是我们可以在函数声明中指定其它参数和变量的值:

function myFunction(a=10, b=a) {
     console.log(&#39;a = &#39; + a + &#39;; b = &#39;  + b);
}
myFunction();     // a=10; b=10
myFunction(22);    // a=22; b=22
myFunction(2, 4);    // a=2; b=4
ログイン後にコピー

你甚至可以在函数声明中做运算:

function myFunction(a, b = ++a, c = a*b) {
     console.log(c);
}
myFunction(5);    // 36
ログイン後にコピー

请注意,JavaScript 和其它语言不同, 它是在函数被调用时,才去求参数的默认值。

function add(value, array = []) {
  array.push(value);
  return array;
}
add(5);    // [5]
add(6);    // [6], not [5, 6]
ログイン後にコピー

浏览器对默认参数的支持情况 Link

桌面浏览器:

浏览器对Rest参数的支持情况 Link

移动端浏览器:

浏览器对Rest参数的支持情况 Link

解构赋值 Link

解构赋值是ES6的新特性。我们可以从数组和对象中提取值,对变量进行赋值。这种语法清晰且易于理解,尤其是在给函数传参时特别有用。

在ES5里面,我们经常用一个配置对象来处理大量的可选参数, 特别是当对象属性的顺序可变时:

function initiateTransfer(options) {
    var  protocol = options.protocol,
        port = options.port,
        delay = options.delay,
        retries = options.retries,
        timeout = options.timeout,
        log = options.log;    // code to initiate transfer}options = 
        {
  protocol: &#39;http&#39;,
  port: 800,
  delay: 150,
  retries: 10,
  timeout: 500,
  log: true};
initiateTransfer(options);
ログイン後にコピー

这种方式实现起来很好,已经被许多JS开发者所采用。 只是我们必须看函数内部,才知道函数预期需要哪些参数。结合解构赋值,我们就可以在函数声明中清晰地表示这些参数:

function initiateTransfer({protocol, port, delay, retries, timeout, log}) {
     // code to initiate transfer
};
var options = {
  protocol: &#39;http&#39;,
  port: 800,
  delay: 150,
  retries: 10,
  timeout: 500,
  log: true
}
initiateTransfer(options);
ログイン後にコピー

在该函数中,我们没有传入一个配置对象,而是以对象解构赋值的方式,给它传参数。这样做不仅使这个函数更加简明,可读性也更高。

我们也可以把解构赋值传参和其它规则的参数一起使用:

function initiateTransfer(param1, {protocol, port, delay, retries, timeout, log}) {
     // code to initiate transfer
}
initiateTransfer(&#39;some value&#39;, options);
ログイン後にコピー

注意如果函数调用时,参数被省略掉,则会抛出错误,如下:

function initiateTransfer({protocol, port, delay, retries, timeout, log}) {
     // code to initiate transfer
}
initiateTransfer();  // TypeError: Cannot match against &#39;undefined&#39; or &#39;null&#39;
ログイン後にコピー

当我们需要让参数都是必填时,这种方法能够得到我们想要的结果,但如果我们希望参数是可选的时候呢?想要让参数缺失时不会报错,我们就需要给默认参数设定一个默认值:

function initiateTransfer({protocol, port, delay, retries, timeout, log} = {}) {
     // code to initiate transfer
}
initiateTransfer();    // no error
ログイン後にコピー

上面这个函数中,需要解构赋值的参数有了一个默认值,这个默认值就是一个空对象。这时候,函数被调用时,即使没有传入参数,也不会报错了。

我们也可以给每个被解构的参数设定默认值,如下:

function initiateTransfer({
    protocol = &#39;http&#39;,
    port = 800,
    delay = 150,
    retries = 10,
    timeout = 500,
    log = true
}) {     // code to initiate transfer
}
ログイン後にコピー

在这个例子中,每个属性都有一个默认值,我们不需要手动去检查哪个参数值是 undefined ,然后在函数中给它设定默认值了。

浏览器对解构赋值的支持情况 Link

桌面浏览器:

浏览器对Rest参数的支持情况 Link

移动端浏览器:

浏览器对Rest参数的支持情况 Link

参数传递 Link

参数能通过 引用 或 值 传递给函数。修改按引用传递的参数,一般反映在全局中,而修改按值传递的参数,则只是反映在函数内部。

在像 Visual Basic 和 PowerShell 这样的语言中,我们可以选择是按引用还是按值来传递参数,但是在 JavaScript 中则不行。

按值传递参数 Link

从技术上来讲,JavaScript 只允许按值传参。当我们给函数按值传递一个参数时,该函数的作用域内就已经复制了这个值。因此,这个值的改变,只会在函数内部反映出来。请思考下面这个列子:

var a = 5;function increment(a) {
    a = ++a;
    console.log(a);
}
increment(a);   // 6
console.log(a);    // 5
ログイン後にコピー

这里,修改函数里面的参数 a = ++a ,是不会影响到原来 a 的值。 所以在函数外面打印 a 的值,输出仍然是 5 。

按引用传递参数 Link

在JavaScript中,一切都是按值传递的。但当我们给函数传一个变量,而这个变量所指向的是一个对象(包括数组)时,这个 变量 就是对象的一个引用。通过这个变量来改变对象的属性值,是会从根本上改变这个对象的。

来看下面这个例子:

function foo(param){
    param.bar = &#39;new value&#39;;
}
obj = {
    bar : &#39;value&#39;
}
console.log(obj.bar);   // value
foo(obj);
console.log(obj.bar);   // new value
ログイン後にコピー

如你所见,对象的属性值在函数内部被修改了,被修改的值在函数外面也是可见的。

当我们传递一个没有初始值的参数时,如数组或对象,会隐形地创建了一个变量,这个变量指向记忆中原对象所在的位置。这个变量随后被传递给了函数,在函数内部对这个变量进行修改将会影响到原对象。

参数类型检测、参数缺失或参数多余 Link

在强类型语言中,我们必须在函数声明中明确参数的类型,但是 JavaScript 没有这种特性。在JavaScript中,我们传递给函数的参数个数不限,也可以是任何类型的数据。

假设现在有一个函数,这个函数只接受一个参数。但是当函数被调用时,它本身没有限制传入的参数只能是一个,我们可以随意地传入一个、两个、甚至是更多。我们也可以什么都不传,这样都不会报错。

形参( arguments )和 实参( parameters )的个数不同有两种情况:

实参少于形参

缺失的参数都会等同于 undefined 。

实参多于形参

多余的参数都将被忽略,但它们会以数组的形式保存于变量 arguments 中(下文会讨论到)。

必填参数 Link

如果一个参数在函数调用时缺失了,它将被设为 undefined 。基于这一点,我们可以在参数缺失时抛出一个错误:

function foo(mandatory, optional) {
    if (mandatory === undefined) {
        throw new Error(&#39;Missing parameter: mandatory&#39;);
    }
}
ログイン後にコピー

在 ES6 中,我们可以更好地利用这个特性,使用默认参数来设定必填参数:

function throwError() {
    throw new Error(&#39;Missing parameter&#39;);
}
function foo(param1 = throwError(), param2 = throwError()) {
    // do something
}
foo(10, 20);    // ok
foo(10);   // Error: missing parameter
ログイン後にコピー

参数对象 Link

为了取代参数对象,rest参数在 ECMAScript 4 中就已经得到支持,但是 ECMAScript 4 没有落实。随着 ECMAScript 6 版本的发布,JS 正式支持rest参数。它也拟定计划,准备 抛弃 对参数对象 arguments object 的支持。

参数对象是一个类数组对象,可在一切函数内使用。它允许通过数字而不是名称,来找回被传递给函数的参数值。这个对象使得我们可以给函数传递任何参数。思考以下代码段:

function checkParams(param1) {
    console.log(param1);    // 2
    console.log(arguments[0], arguments[1]);    // 2 3
    console.log(param1 + arguments[0]);    // 2 + 2
}
checkParams(2, 3);
ログイン後にコピー

该函数预期接收一个参数。但是当我们给它传入两个参数并且调用它时,第一个参数通过名为 param1 的形参或者参数对象 arguments[0] 被函数所接受,而第二个参数只能被放在 argument[1] 里面。此外,请注意,参数对象可以与命名参数一起使用。

参数对象给每个被传递给函数的参数提供了一个入口,并且第一个入口的下标从 0开始。如果我们要给上面这个函数传递更多的参数,我们可以写 arguments[2] , arguments[3] 等等。

我们甚至可以跳过设定命名参数这一步,直接使用参数对象:

function checkParams() {
    console.log(arguments[1], arguments[0], arguments[2]);
}
checkParams(2, 4, 6);  // 4 2 6
ログイン後にコピー

事实上,命名参数只是为了方便使用,并不是必须的。类似地,rest参数也可用于反映被传递的参数:

function checkParams(...params) {
    console.log(params[1], params[0], params[2]);    // 4 2 6
    console.log(arguments[1], arguments[0], arguments[2]);    // 4 2 6
}
checkParams(2, 4, 6);
ログイン後にコピー

参数对象是一个类数组的对象,只是它没有数组本身具备的方法,如 slice() 和 foreach() 。 如果要在参数对象中使用数组的方法,首先要把它转换成一个真正的数组。

function sort() {
    var a = Array.prototype.slice.call(arguments);
    return a.sort();
}
sort(40, 20, 50, 30);    // [20, 30, 40, 50]
ログイン後にコピー

在该函数中,采用了 Array.prototype.slice.call() 来快速地把参数对象转换成一个数组。接着,在 sort() 方法中,为这个数组排序并且把它返回。

ES6 新增了更直接的方法,用 Array.from() 把任何类数组对象转换成数组:

function sort() {
    var a = Array.from(arguments);
    return a.sort();
}
sort(40, 20, 50, 30);    // [20, 30, 40, 50]
ログイン後にコピー

长度属性 Link

尽管参数对象从技术上来讲,不算是一个数组,但仍有一个长度属性,来检测传递给函数的参数个数:

function countArguments() {
    console.log(arguments.length);
}
countArguments();    // 0
countArguments(10, null, "string");    // 3
ログイン後にコピー

通过 length 属性,我们可以更好地控制传递给函数的参数个数。举个例子,如果一个函数只要求两个参数,那么我们就可以使用 length 属性来检测所传入的参数个数,如果少于要求的个数,则抛出错误:

function foo(param1, param2) {
    if (arguments.length < 2) {
        throw new Error("This function expects at least two arguments");
    } else if (arguments.length === 2) {
        // do something
    }
}
ログイン後にコピー

rest参数是数组,所以他们都有 length 属性。 所以上面的代码,在ES6里面可以用rest参数写成下面这样:

function foo(...params) {
  if (params.length < 2) {
        throw new Error("This function expects at least two arguments");
    } else if (params.length === 2) {
        // do something
    }
}
ログイン後にコピー

被调用属性与调用属性 Link

被调用 属性指向当前正在执行的函数,而 调用 属性则指向那个调用了 当前正在执行的函数 的函数。 在ES5的严格模式下,这些属性是不被支持的,如果尝试使用它们,则会报错。

arguments.callee 这个属性在递归函数中很有用,尤其在匿名函数中。因为匿名函数没有名称,只能通过 arguments.callee 来指向它。

var result = (function(n) {
  if (n
ログイン後にコピー

严格模式和非严格模式下的参数对象 Link

在ES5非严格模式下, 参数对象 有个不一般的特性:它能使 自身的值 跟 与之相对应的命名参数的值 保持同步。

请看下面这个例子:

function foo(param) {   console.log(param === arguments[0]);    // true
   arguments[0] = 500;   console.log(param === arguments[0]);    // true
   return param
}
foo(200);    // 500
ログイン後にコピー

在这个函数里面, arguments[0] 被重新赋值为 500 。由于 arguments 的值总是和对应的命名参数保持同步,所以改变了 arguments[0] 的值,也就相应的改变了param 的值。实际上,他们就像是同一个变量,拥有两个不同的名字而已。而在 ES5严格模式 下,参数对象的这种特性则被移除了。

"use strict";
function foo(param) {
   console.log(param === arguments[0]);    // true
   arguments[0] = 500;
   console.log(param === arguments[0]);    // false
   return param
}
foo(200);   // 200
ログイン後にコピー

加上 严格模式 , 现在改变 arguments[0] 的值是不会影响到 param 的值了,打印出来的值也跟预期的一致。 在 ES6 中 该函数的输出跟在 ES5 严格模式下是一样的。需要记住的是,当函数声明中使用了默认值时,参数对象是不会受到影响的:

function foo(param1, param2 = 10, param3 = 20) {
   console.log(param1 === arguments[0]);    // true
   console.log(param2 === arguments[1]);    // true
   console.log(param3 === arguments[2]);    // false
   console.log(arguments[2]);    // undefined
   console.log(param3);    // 20
}
foo(&#39;string1&#39;, &#39;string2&#39;);
ログイン後にコピー

在这个函数中,尽管 param3 有默认值 20 ,但是 arguments[2] 仍然是undefined , 因为函数调用时只传了两个值。换言之,设定默认值对参数对象是没有任何影响的。

总结 Link

ES6 给 JS 带来了上百个大大小小的改进。 越来越多的开发者正使用ES6的新特性, 所以我们都需要去了解它们。在本教程中,我们学习了ES6是如何改善JS的参数处理的,但我们仍只是知晓了ES6的皮毛。更多新的、有趣的特性值得我们去探讨。


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、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衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

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

SublimeText3 中国語版

SublimeText3 中国語版

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

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

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