|
導讀網頁的本質就是超級文本標記語言,通過結合使用其他的Web技術(如:腳本語言、公共網關接口、組件等),可以創造出功能強大的網頁。因而,超級文本標記語言是萬維網(Web)編程的基礎,也就是說萬維網是建立... 網頁的本質就是超級文本標記語言,通過結合使用其他的Web技術(如:腳本語言、公共網關接口、組件等),可以創造出功能強大的網頁。因而,超級文本標記語言是萬維網(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。 (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) // 2let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1參數初始化會在函數被調用時進行,無論是給參數傳遞了一個值、還是使用了參數的默認值。 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 = 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 parameterrest 參數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被限定只能使用單個參數;而剩余參數按照定義是不限制參數數量的,因此它在此處不被許可。 (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的嚴格模式為代碼塊內部的函數聲明引入了一個錯誤。 "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。 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函數不同: 區別
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,所以也就不能用作構造函數。 // 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從“動態”變成“靜態”,下面兩個場合不應該使用箭頭函數。 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調用。 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中函數的擴展(代碼示例)的詳細內容,更多請關注php中文網其它相關文章! 網站建設是一個廣義的術語,涵蓋了許多不同的技能和學科中所使用的生產和維護的網站。 |
溫馨提示:喜歡本站的話,請收藏一下本站!