亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ES6中函數擴展的示例

發布時間:2020-12-08 09:57:58 來源:億速云 閱讀:106 作者:小新 欄目:web開發

這篇文章給大家分享的是有關ES6中函數擴展的示例的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。

函數參數的默認值

ES6 允許為函數的參數設置默認值,即直接寫在參數定義的后面。  

ES6之前:

function makeRequest(url,timeout,callback) {
    timeout=(typeof timeout!=="undefined")?timeout:2000;
    callback=(typeof callback!=="undefined")?callback:function(){};
                //    函數的剩余部分
}
//ES6 
function log(x, y = 'World') {
  console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

參數變量是默認聲明的,所以不能用let或const再次聲明。

function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

使用參數默認值時,函數不能有同名參數。

// 不報錯
function foo(x, x, y) {
  // ...
}
// 報錯
function foo(x, x, y = 1) {
  // ...
}
// SyntaxError: Duplicate parameter name not allowed in this context

參數默認值不是傳值的,而是每次都重新計算默認值表達式的值。也就是說,參數默認值是惰性求值的。

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}
foo() // 100
x = 100;
foo() // 101

在函數聲明中能指定任意一個參數的默認值,即使該參數排在未指定默認值的參數之前也是可以的。

function makeRequest(ur1,timeout=2000,callback){
//函數的剩余部分
}
//    使用默認的    timeout
makeRequest("/foo",    undefined,    function(body)    {
    doSomething(body);
});
//    使用默認的    timeout
makeRequest("/foo");
//    不使用默認值
makeRequest("/foo",    null,    function(body)    {
    doSomething(body);
});

在本例中,只有在未傳遞第二個參數、或明確將第二個參數值指定為undefined時,timeout的默認值才會被使用。

與解構賦值默認值結合使用

function foo({x, y = 5}) {
  console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined

上面代碼只使用了對象的解構賦值默認值,沒有使用函數參數的默認值。只有當函數foo的參數是一個對象時,變量x和y才會通過解構賦值生成。如果函數foo調用時沒提供參數,變量x和y就不會生成,從而報錯。通過提供函數參數的默認值,就可以避免這種情況。

function foo({x, y = 5} = {}) {
  console.log(x, y);
}
foo() // undefined 5
function fetch(url, { body = '', method = 'GET', headers = {} }) {
  console.log(method);
}
fetch('http://example.com', {})
// "GET"
fetch('http://example.com')
// 報錯
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
  console.log(method);
}
fetch('http://example.com')
// "GET"

會影響函數的length屬性

指定了默認值以后,函數的length屬性,將返回沒有指定默認值的參數個數。也就是說,指定了默認值后,length屬性將失真。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

上面代碼中,length屬性的返回值,等于函數的參數個數減去指定了默認值的參數個數。比如,上面最后一個函數,定義了3個參數,其中有一個參數c指定了默認值,因此length屬性等于3減去1,最后得到2。  
這是因為length屬性的含義是,該函數預期傳入的參數個數。某個參數指定默認值以后,預期傳入的參數個數就不包括這個參數了。同理,后文的 rest 參數也不會計入length屬性。

(function(...args) {}).length // 0

如果設置了默認值的參數不是尾參數,那么length屬性也不再計入后面的參數了

(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1

參數默認值的作用域

一旦設置了參數的默認值,函數進行聲明初始化時,參數會形成一個單獨的作用域(context)。等到初始化結束,這個作用域就會消失。這種語法行為,在不設置參數默認值時,是不會出現的。

var x = 1;
function f(x, y = x) {
  console.log(y);
}
f(2) // 2
let x = 1;

function f(y = x) {
  let x = 2;
  console.log(y);
}

f() // 1

參數初始化會在函數被調用時進行,無論是給參數傳遞了一個值、還是使用了參數的默認值。  
上面代碼中,函數f調用時,參數y = x形成一個單獨的作用域。這個作用域里面,變量x本身沒有定義,所以指向外層的全局變量x。函數調用時,函數體內部的局部變量x影響不到默認值變量x。如果此時,全局變量x不存在,就會報錯。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  var x = 3;
  y();
  console.log(x);
}
foo() // 3
x // 1

上面代碼中,函數foo的參數形成一個單獨作用域。這個作用域里面,首先聲明了變量x,然后聲明了變量y,y的默認值是一個匿名函數。這個匿名函數內部的變量x,指向同一個作用域的第一個參數x。函數foo內部又聲明了一個內部變量x,該變量與第一個參數x由于不是同一個作用域,所以不是同一個變量,因此執行y后,內部變量x和外部全局變量x的值都沒變。  
如果將var x =3的var去除,函數foo的內部變量x就指向第一個參數x,與匿名函數內部的x是一致的,所以最后輸出的就是2,而外層的全局變量x依然不受影響。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}
foo() // 2
x // 1

參數默認值的暫時性死區

與1et聲明相似,函數每個參數都會創建一個新的標識符綁定,它在初始化之前不允許被訪問,否則會拋出錯誤。參數初始化會在函數被調用時進行,無論是給參數傳遞了一個值、還是使用了參數的默認值。

function    getValue(value)    {
    return    value    +    5;
}
function    add(first,    second    =    getValue(first))    {
    return    first    +    second;
}
console.log(add(1,    1));//    2
console.log(add(1));//    7

調用add(1,    1)和add(1)事實上執行了以下代碼來創建first與second的參數值:

//JS調用add(1,1)可表示為
let    first    =    1;
let    second    =    1;
//JS調用add(1)可表示為
let    first    =    1;
let    second    =    getValue(first);

改寫一下

function    add(first    =    second,    second)    {
    return    first    +    second;
}

console.log(add(1,    1));//    2
console.log(add(undefined,    1));//    拋出錯誤

本例中調用    add(1,    1)    與    add(undefined,    1)    對應著以下的后臺代碼:

//    JS    調用    add(1,    1)    可表示為
let    first    =    1;
let    second    =    1;

//    JS    調用    add(1)    可表示為
let    first    =    second;
let    second    =    1;

本例中調用add(undefined,1)拋出了錯誤,是因為在first被初始化時second尚未被初始化。此處的second存在于暫時性死區內,對于second的引用就拋出了錯誤。  
函數參數擁有各自的作用域和暫時性死區,與函數體的作用域相分離,這意味著參數的默認值不允許訪問在函數體內部聲明的任意變量。

應用

利用參數默認值,可以指定某一個參數不得省略,如果省略就拋出一個錯誤。

function throwIfMissing() {
  throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
  return mustBeProvided;
}
foo()
// Error: Missing parameter

rest 參數

ES6 引入 rest參數(形式為...變量名),用于獲取函數的多余參數,這樣就不需要使用arguments對象了。rest 參數搭配的變量是一個數組,該變量將多余的參數放入數組中。rest參數就是一個真正的數組,數組特有的方法都可以使用。

function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}
add(2, 5, 3) // 10
// arguments變量的寫法
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}
// rest參數的寫法
const sortNumbers = (...numbers) => numbers.sort();

rest 參數的限制條件

一是函數只能有一個剩余參數,并且它必須被放在最后。

// 報錯
function f(a, ...b, c) {
  // ...
}

第二個限制是剩余參數不能在對象字面量的    setter    屬性中使用,這意味著如下代碼同樣會導致語法錯誤:

1et object={
//語法錯誤:不能在setter中使用剩余參數
set name(...value){
//一些操作
};

存在此限制的原因是:對象字面量的setter被限定只能使用單個參數;而剩余參數按照定義是不限制參數數量的,因此它在此處不被許可。    
前面有講函數的length屬性,不包括 rest 參數。

(function(a) {}).length  // 1
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1

嚴格模式

從 ES5 開始,函數內部可以設定為嚴格模式。

function doSomething(a, b) {
  'use strict';
  // code
}

ES2016 做了一點修改,規定只要函數參數使用了默認值、解構賦值、或者擴展運算符,那么函數內部就不能顯式設定為嚴格模式,否則會報錯。

// 報錯
function doSomething(a, b = a) {
  'use strict';
  // code
}
// 報錯
const doSomething = function ({a, b}) {
  'use strict';
  // code
};
// 報錯
const doSomething = (...a) => {
  'use strict';
  // code
};
const obj = {
  // 報錯
  doSomething({a, b}) {
    'use strict';
    // code
  }
};

這樣規定的原因是,函數內部的嚴格模式,同時適用于函數體和函數參數。但是,函數執行的時候,先執行函數參數,然后再執行函數體。這樣就有一個不合理的地方,只有從函數體之中,才能知道參數是否應該以嚴格模式執行,但是參數卻應該先于函數體執行。  
把函數包在一個無參數的立即執行函數里面可以規避這種限制。

const doSomething = (function () {
  'use strict';
  return function(value = 42) {
    return value;
  };
}());

在代碼塊中聲明函數

在ES3或更早版本中,在代碼塊中聲明函數(即塊級函數)嚴格來說應當是一個語法錯誤,但所有的瀏覽器卻都支持該語法。可惜的是,每個支持該語法的瀏覽器都有輕微的行為差異,所以最佳實踐就是不要在代碼塊中聲明函數(更好的選擇是使用函數表達式)。

嚴格模式下

為了控制這種不兼容行為,ES5的嚴格模式為代碼塊內部的函數聲明引入了一個錯誤。  
然而ES6會將    doSomething()函數視為塊級聲明,并允許它在定義所在的代碼塊內部被訪問。塊級函數與    let    函數表達式相似,在執行流跳出定義所在的代碼塊之后,函數定義就會被移除。關鍵區別在于:塊級函數會被提升到所在代碼塊的頂部;而使用let的函數表達式則不會 。

"use    strict";
if    (true)    {

    console.log(typeof doSomething);//"function"
    function    doSomething()    {
        //    ...
    }
    doSomething();
}
console.log(typeof    doSomething);//    "undefined

非嚴格模式下

ES6    在非嚴格模式下同樣允許使用塊級函數,但行為有細微不同。塊級函數的作用域會被提升到所在函數或全局環境的頂部,而不是代碼塊的頂部。

//    ES6    behavior
if    (true)    {
    console.log(typeof    doSomething);//"function"
    function    doSomething()    {
        //    ...
    }
    doSomething();
}
console.log(typeof    doSomething);//    "function"

name 屬性

函數的name屬性,返回該函數的函數名。

function foo() {}
foo.name // "foo"

getter函數,因此它的名稱是    "get    firstName"    ,以標明它的特征;同樣,setter函數也會帶有"set"的前綴(getter與setter函數都必須用Object.getOwnPropertyDescriptor()來檢索)。另外,使用bind()創建的函數會在名稱屬性值之前帶有"bound”前綴;而使用Function構造器創建的函數,其名稱屬性則會有“anonymous”前綴,如果將一個匿名函數賦值給一個變量,ES5的name屬性,會返回空字符串,而 ES6 的name屬性會返回實際的函數名。

var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"

new.target元屬性

JS為函數提供了兩個不同的內部方法:[[Call]]與[[Construct]]。當函數未使用new進行調用時,[[Call]]方法會被執行,運行的是代碼中顯示的函數體。而當函數使用new進行調用時,[[Construct]]方法則會被執行,負責創建一個被稱為新目標的新的對象,并且使用該新目標作為this去執行函數體。擁有[[Construct]]    方法的函數被稱為構造器。記住并不是所有函數都擁有[[Construct]]方法,因此不是所有函數都可以用new來調用。

在ES5中判斷函數如何被調用

在ES5中判斷函數是不是使用了new來調用(即作為構造器),最流行的方式是使用instanceof,例如:

function    Person(name)    {
    if    (this    instanceof    Person)    {
            this.name    =    name;//    使用new
    }    else    {
            throw    new    Error("You    must    use    new    with    Person
    }
}

var    person    =    new    Person("Nicholas");
var    notAPerson    =    Person("Nicholas");//拋出錯誤

可惜的是,該方法并不絕對可靠,因為在不使用    new    的情況下this仍然可能是    Person    的實例。

var    notAPerson    =    Person.call(person,    "Michael");    //    奏效了!

在ES6中判斷函數如何被調用

為了解決這個問題,ES6引入了new.target 元屬性。元屬性指的是“非對象”(例如new)上的一個屬性,并提供關聯到它的目標的附加信息。當函數的[[Construct]]方法被調用時,new.target 會被填入new運算符的作用目標,該目標通常是新創建的對象實例的構造器,并且會成為函數體內部的this值。而若[[Call]]被執行,new.target的值則會是undefined。    
通過檢查new.target是否被定義,這個新的元屬性就讓你能安全地判斷函數是否被使用new 進行了調用。

function    Person(name)    {
    if    (typeof new.target    !==    "undefined")    {
            this.name    =    name;//    使用new
    }    else    {
            throw    new    Error("You    must    use    new    with    Person
    }
}
var    person    =    new    Person("Nicholas");
var    notAPerson    =    Person("Nicholas");//拋出錯誤

警告:在函數之外使用new.target會有語法錯誤。

箭頭函數

ES6 允許使用“箭頭”(=>)定義函數。但它的行為在很多重要方面與傳統的JS函數不同:

區別

  • 沒有this、super、arguments,也沒有new.target 綁定:this、super、arguments、以及函數內部的new.target的值由所在的、最靠近的非箭頭函數來決定。

  • 不能被使用new調用:箭頭函數沒有[[Construct]]方法,因此不能被用為構造函數,使用new調用箭頭函數會拋出錯誤。

  • 沒有原型:既然不能對箭頭函數使用new,那么它也不需要原型,也就是沒有prototype 屬性。

  • 不能更改this:this的值在函數內部不能被修改,在函數的整個生命周期內其值會保持不變。

  • 沒有arguments對象:既然箭頭函數沒有arguments綁定,你必須依賴于具名參數或剩余參數來訪問函數的參數。

  • 不允許重復的具名參數:箭頭函數不允許擁有重復的具名參數,無論是否在嚴格模式下;而相對來說,傳統函數只有在嚴格模式下才禁止這種重復。

  • 不可以使用yield命令,因此箭頭函數不能用作 Generator 函數。

var f = () => 5;
// 等同于
var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};

如果箭頭函數的代碼塊部分多于一條語句,就要使用大括號將它們括起來,并且使用return語句返回。

var sum = (num1, num2) => { return num1 + num2; }

由于大括號被解釋為代碼塊,所以如果箭頭函數直接返回一個對象,必須在對象外面加上括號,否則會報錯。

// 報錯
let getTempItem = id => { id: id, name: "Temp" };

// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });

下面是一種特殊情況,雖然可以運行,但會得到錯誤的結果。

let foo = () => { a: 1 };
foo() // undefined

上面代碼中,原始意圖是返回一個對象{a:1},但是由于引擎認為大括號是代碼塊,所以執行了一行語句a: 1。這時,a可以被解釋為語句的標簽,因此實際執行的語句是1;,然后函數就結束了,沒有返回值。  
如果箭頭函數只有一行語句,且不需要返回值,可以采用下面的寫法,就不用寫大括號了。

let fn = () => void doesNotReturn();

this指向的固定化,并不是因為箭頭函數內部有綁定this的機制,實際原因是箭頭函數根本沒有自己的this,導致內部的this就是外層代碼塊的this。正是因為它沒有this,所以也就不能用作構造函數。  
所以,箭頭函數轉成 ES5 的代碼如下。

// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}
// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}

箭頭函數的this看外層的是否有函數

如果有,外層函數的this就是內部箭頭函數的this。

 function Person() {
     this.obj = {
         showThis : () => {
             console.log(this);//person
         }
     }
 }
    let fun5 = new Person();
    fun5.obj.showThis();

如果沒有,this值就會是全局對象(在瀏覽器中是window,在nodejs中是global)。

    let obj = {
        name : 'kobe',
        age : 39,
        getName : () => {
            btn2.onclick = () => {
                console.log(this);//window
            };
        }
    };
    obj.getName();

不適用的場合

由于箭頭函數使得this從“動態”變成“靜態”,下面兩個場合不應該使用箭頭函數。  
第一個場合是定義函數的方法,且該方法內部包括this。

const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

第二個場合是需要動態this的時候,也不應使用箭頭函數。

var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

上面代碼運行時,點擊按鈕會報錯,因為button的監聽函數是一個箭頭函數,導致里面的this就是全局對象。如果改成普通函數,this就會動態指向被點擊的按鈕對象。  
另外,如果函數體很復雜,有許多行,或者函數內部有大量的讀寫操作,不單純是為了計算值,這時也不應該使用箭頭函數,而是要使用普通函數,這樣可以提高代碼可讀性。

雙冒號運算符

箭頭函數可以綁定this對象,大大減少了顯式綁定this對象的寫法(call、apply、bind)。但是,箭頭函數并不適用于所有場合,所以現在有一個提案,提出了“函數綁定”(function bind)運算符,用來取代call、apply、bind調用。  
函數綁定運算符是并排的兩個冒號(::),雙冒號左邊是一個對象,右邊是一個函數。該運算符會自動將左邊的對象,作為上下文環境(即this對象),綁定到右邊的函數上面。

foo::bar;
// 等同于
bar.bind(foo);

foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
  return obj::hasOwnProperty(key);
}

如果雙冒號左邊為空,右邊是一個對象的方法,則等于將該方法綁定在該對象上面。

var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;

let log = ::console.log;
// 等同于
var log = console.log.bind(console);

感謝各位的閱讀!關于ES6中函數擴展的示例就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

金山区| 永清县| 云和县| 普定县| 瓦房店市| 西和县| 涟水县| 重庆市| 开原市| 定州市| 凯里市| 苏尼特左旗| 玛多县| 太湖县| 泸西县| 新巴尔虎左旗| 林周县| 车致| 崇文区| 土默特左旗| 长武县| 彩票| 怀柔区| 阳泉市| 山阴县| 宣武区| 安康市| 白朗县| 武平县| 泰顺县| 荃湾区| 永善县| 绍兴市| 民丰县| 成都市| 巴林左旗| 泽库县| 桓台县| 平安县| 兴城市| 喜德县|