摘要:返回布爾值,表示參數(shù)字符串是否在原字符串的頭部。模板字符串之中還能調(diào)用函數(shù)。其他對字符串還有許多擴展,例如對字符表示的擴充以及為字符串提供了遍歷方法詳情請點擊正則的擴展構(gòu)造函數(shù)在中,構(gòu)造函數(shù)的參數(shù)有兩種情況。
ES6對各種基本類型都做了擴展,內(nèi)容有些多,本章節(jié)挑選比較重要的擴展說明。
1 字符串的擴展 1.1 includes(), startsWith(), endsWith()傳統(tǒng)上,JavaScript只有indexOf方法,可以用來確定一個字符串是否包含在另一個字符串中。ES6又提供了三種新方法。
includes():返回布爾值,表示是否找到了參數(shù)字符串。
startsWith():返回布爾值,表示參數(shù)字符串是否在原字符串的頭部。
endsWith():返回布爾值,表示參數(shù)字符串是否在原字符串的尾部。
var s = "Hello world!"; s.startsWith("Hello") // true s.endsWith("!") // true s.includes("o") // true
這三個方法都支持第二個參數(shù),表示開始搜索的位置。
var s = "Hello world!"; s.startsWith("world", 6) // true s.endsWith("Hello", 5) // true s.includes("Hello", 6) // false
上面代碼表示,使用第二個參數(shù)n時,endsWith的行為與其他兩個方法有所不同。它針對前n個字符,而其他兩個方法針對從第n個位置直到字符串結(jié)束。
1.2 repeat()repeat方法返回一個新字符串,表示將原字符串重復n次。
"x".repeat(3) // "xxx" "hello".repeat(2) // "hellohello" "na".repeat(0) // ""
參數(shù)如果是小數(shù),會被取整。
"na".repeat(2.9) // "nana"
如果repeat的參數(shù)是負數(shù)或者Infinity,會報錯。
"na".repeat(Infinity) // RangeError "na".repeat(-1) // RangeError
但是,如果參數(shù)是0到-1之間的小數(shù),則等同于0,這是因為會先進行取整運算。0到-1之間的小數(shù),取整以后等于-0,repeat視同為0。
"na".repeat(-0.9) // ""
參數(shù)NaN等同于0。
"na".repeat(NaN) // ""
如果repeat的參數(shù)是字符串,則會先轉(zhuǎn)換成數(shù)字。
"na".repeat("na") // "" "na".repeat("3") // "nanana"1.3 模板字符串
這個功能應(yīng)該是最值得介紹的了,因為有了這個,我們可以拋棄之前用 + 號拼接字符串了。
傳統(tǒng)的JavaScript語言,輸出模板通常是這樣寫的。
$("#result").append( "There are " + basket.count + " " + "items in your basket, " + "" + basket.onSale + " are on sale!" );
上面這種寫法相當繁瑣不方便,而且改動麻煩,ES6引入了模板字符串解決這個問題。
$("#result").append(` There are ${basket.count} items in your basket, ${basket.onSale} are on sale! `);
模板字符串(template string)是增強版的字符串,用反引號(`)標識。它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。
// 普通字符串 `In JavaScript " " is a line-feed.` // 多行字符串 `In JavaScript this is not legal.` console.log(`string text line 1 string text line 2`); // 字符串中嵌入變量 var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?`
上面代碼中的模板字符串,都是用反引號表示。如果在模板字符串中需要使用反引號,則前面要用反斜杠轉(zhuǎn)義。
var greeting = ``Yo` World!`;
如果使用模板字符串表示多行字符串,所有的空格和縮進都會被保留在輸出之中。
$("#list").html(`
上面代碼中,所有模板字符串的空格和換行,都是被保留的,比如
$("#list").html(`
模板字符串中嵌入變量,需要將變量名寫在${}之中。
function authorize(user, action) { if (!user.hasPrivilege(action)) { //傳統(tǒng)寫法為 //return "str:" + a + "XXXX"; return `str: ${a} XXXX`); } }
大括號內(nèi)部可以放入任意的JavaScript表達式,可以進行運算,以及引用對象屬性。
var x = 1; var y = 2; `${x} + ${y} = ${x + y}` // "1 + 2 = 3" `${x} + ${y * 2} = ${x + y * 2}` // "1 + 4 = 5" var obj = {x: 1, y: 2}; `${obj.x + obj.y}` // "3"
模板字符串之中還能調(diào)用函數(shù)。
function fn() { return "Hello World"; } `foo ${fn()} bar` // foo Hello World bar
如果大括號中的值不是字符串,將按照一般的規(guī)則轉(zhuǎn)為字符串。比如,大括號中是一個對象,將默認調(diào)用對象的toString方法。
如果模板字符串中的變量沒有聲明,將報錯。
// 變量place沒有聲明 var msg = `Hello, ${place}`; // 報錯
由于模板字符串的大括號內(nèi)部,就是執(zhí)行JavaScript代碼,因此如果大括號內(nèi)部是一個字符串,將會原樣輸出。
`Hello ${"World"}` // "Hello World"
模板字符串甚至還能嵌套。
const tmpl = addrs => `
${addr.first} |
${addr.last} |
Bond |
Lars |
ES6對字符串還有許多擴展,例如 對 字符Unicode表示的擴充以及為字符串提供了遍歷方法(for ... of)
詳情請點擊 http://es6.ruanyifeng.com/#do...
在 ES5 中,RegExp構(gòu)造函數(shù)的參數(shù)有兩種情況。
第一種情況是,參數(shù)是字符串,這時第二個參數(shù)表示正則表達式的修飾符(flag)。
var regex = new RegExp("xyz", "i"); // 等價于 var regex = /xyz/i;
第二種情況是,參數(shù)是一個正則表示式,這時會返回一個原有正則表達式的拷貝。
var regex = new RegExp(/xyz/i); // 等價于 var regex = /xyz/i;
但是,ES5 不允許此時使用第二個參數(shù)添加修飾符,否則會報錯。
var regex = new RegExp(/xyz/, "i"); // Uncaught TypeError: Cannot supply flags when constructing one RegExp from another
ES6 改變了這種行為。如果RegExp構(gòu)造函數(shù)第一個參數(shù)是一個正則對象,那么可以使用第二個參數(shù)指定修飾符。而且,返回的正則表達式會忽略原有的正則表達式的修飾符,只使用新指定的修飾符。
new RegExp(/abc/ig, "i").flags // "i"
上面代碼中,原有正則對象的修飾符是ig,它會被第二個參數(shù)i覆蓋。
2.2 新的修飾符-uES6 對正則表達式添加了u修飾符,含義為“Unicode模式”,用來正確處理大于uFFFF的 Unicode 字符。也就是說,會正確處理四個字節(jié)的 UTF-16 編碼。
/^uD83D/u.test("uD83DuDC2A") // false /^uD83D/.test("uD83DuDC2A") // true
上面代碼中,uD83DuDC2A是一個四個字節(jié)的 UTF-16 編碼,代表一個字符。但是,ES5 不支持四個字節(jié)的 UTF-16 編碼,會將其識別為兩個字符,導致第二行代碼結(jié)果為true。加了u修飾符以后,ES6 就會識別其為一個字符,所以第一行代碼結(jié)果為false。
2.3 新的修飾符-y除了u修飾符,ES6 還為正則表達式添加了y修飾符,叫做“粘連”(sticky)修飾符。
y修飾符的作用與g修飾符類似,也是全局匹配,后一次匹配都從上一次匹配成功的下一個位置開始。不同之處在于,g修飾符只要剩余位置中存在匹配就可,而y修飾符確保匹配必須從剩余的第一個位置開始,這也就是“粘連”的涵義。
var s = "aaa_aa_a"; var r1 = /a+/g; var r2 = /a+/y; r1.exec(s) // ["aaa"] r2.exec(s) // ["aaa"] r1.exec(s) // ["aa"] r2.exec(s) // null
上面代碼有兩個正則表達式,一個使用g修飾符,另一個使用y修飾符。這兩個正則表達式各執(zhí)行了兩次,第一次執(zhí)行的時候,兩者行為相同,剩余字符串都是_aa_a。由于g修飾沒有位置要求,所以第二次執(zhí)行會返回結(jié)果,而y修飾符要求匹配必須從頭部開始,所以返回null。
如果改一下正則表達式,保證每次都能頭部匹配,y修飾符就會返回結(jié)果了。
var s = "aaa_aa_a"; var r = /a+_/y; r.exec(s) // ["aaa_"] r.exec(s) // ["aa_"]3 數(shù)值的擴展 3.1 Number.isFinite(), Number.isNaN()
ES6 在Number對象上,新提供了Number.isFinite()和Number.isNaN()兩個方法。
Number.isFinite()用來檢查一個數(shù)值是否為有限的(finite)。
Number.isFinite(15); // true Number.isFinite(0.8); // true Number.isFinite(NaN); // false Number.isFinite(Infinity); // false Number.isFinite(-Infinity); // false Number.isFinite("foo"); // false Number.isFinite("15"); // false Number.isFinite(true); // false
ES5 可以通過下面的代碼,部署Number.isFinite方法。
(function (global) { var global_isFinite = global.isFinite; Object.defineProperty(Number, "isFinite", { value: function isFinite(value) { return typeof value === "number" && global_isFinite(value); }, configurable: true, enumerable: false, writable: true }); })(this);
Number.isNaN()用來檢查一個值是否為NaN。
Number.isNaN(NaN) // true Number.isNaN(15) // false Number.isNaN("15") // false Number.isNaN(true) // false Number.isNaN(9/NaN) // true Number.isNaN("true"/0) // true Number.isNaN("true"/"true") // true
ES5 通過下面的代碼,部署Number.isNaN()。
(function (global) { var global_isNaN = global.isNaN; Object.defineProperty(Number, "isNaN", { value: function isNaN(value) { return typeof value === "number" && global_isNaN(value); }, configurable: true, enumerable: false, writable: true }); })(this);
它們與傳統(tǒng)的全局方法isFinite()和isNaN()的區(qū)別在于,傳統(tǒng)方法先調(diào)用Number()將非數(shù)值的值轉(zhuǎn)為數(shù)值,再進行判斷,而這兩個新方法只對數(shù)值有效,Number.isFinite()對于非數(shù)值一律返回false, Number.isNaN()只有對于NaN才返回true,非NaN一律返回false。
isFinite(25) // true isFinite("25") // true Number.isFinite(25) // true Number.isFinite("25") // false isNaN(NaN) // true isNaN("NaN") // true Number.isNaN(NaN) // true Number.isNaN("NaN") // false Number.isNaN(1) // false3.2 Number.parseInt(), Number.parseFloat()
ES6 將全局方法parseInt()和parseFloat(),移植到Number對象上面,行為完全保持不變。
// ES5的寫法 parseInt("12.34") // 12 parseFloat("123.45#") // 123.45 // ES6的寫法 Number.parseInt("12.34") // 12 Number.parseFloat("123.45#") // 123.45
這樣做的目的,是逐步減少全局性方法,使得語言逐步模塊化。
Number.parseInt === parseInt // true Number.parseFloat === parseFloat // true3.3 Number.isInteger()
Number.isInteger()用來判斷一個值是否為整數(shù)。需要注意的是,在 JavaScript 內(nèi)部,整數(shù)和浮點數(shù)是同樣的儲存方法,所以3和3.0被視為同一個值。
Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false Number.isInteger("15") // false Number.isInteger(true) // false
ES5 可以通過下面的代碼,部署Number.isInteger()。
(function (global) { var floor = Math.floor, isFinite = global.isFinite; Object.defineProperty(Number, "isInteger", { value: function isInteger(value) { return typeof value === "number" && isFinite(value) && floor(value) === value; }, configurable: true, enumerable: false, writable: true }); })(this);4 函數(shù)的擴展 4.1 函數(shù)參數(shù)的默認值
基本用法
ES6 之前,不能直接為函數(shù)的參數(shù)指定默認值,只能采用變通的方法。
function log(x, y) { y = y || "World"; console.log(x, y); } log("Hello") // Hello World log("Hello", "China") // Hello China log("Hello", "") // Hello World
上面代碼檢查函數(shù)log的參數(shù)y有沒有賦值,如果沒有,則指定默認值為World。這種寫法的缺點在于,如果參數(shù)y賦值了,但是對應(yīng)的布爾值為false,則該賦值不起作用。就像上面代碼的最后一行,參數(shù)y等于空字符,結(jié)果被改為默認值。
為了避免這個問題,通常需要先判斷一下參數(shù)y是否被賦值,如果沒有,再等于默認值。
if (typeof y === "undefined") { y = "World"; }
ES6 允許為函數(shù)的參數(shù)設(shè)置默認值,即直接寫在參數(shù)定義的后面。
function log(x, y = "World") { console.log(x, y); } log("Hello") // Hello World log("Hello", "China") // Hello China log("Hello", "") // Hello
可以看到,ES6 的寫法比 ES5 簡潔許多,而且非常自然。
除了簡潔,ES6 的寫法還有兩個好處:首先,閱讀代碼的人,可以立刻意識到哪些參數(shù)是可以省略的,不用查看函數(shù)體或文檔;其次,有利于將來的代碼優(yōu)化,即使未來的版本在對外接口中,徹底拿掉這個參數(shù),也不會導致以前的代碼無法運行。
參數(shù)變量是默認聲明的,所以不能用let或const再次聲明。
function foo(x = 5) { let x = 1; // error const x = 2; // error }
上面代碼中,參數(shù)變量x是默認聲明的,在函數(shù)體中,不能用let或const再次聲明,否則會報錯。
使用參數(shù)默認值時,函數(shù)不能有同名參數(shù)。
// 不報錯 function foo(x, x, y) { // ... } // 報錯 function foo(x, x, y = 1) { // ... } // SyntaxError: Duplicate parameter name not allowed in this context
另外,一個容易忽略的地方是,參數(shù)默認值不是傳值的,而是每次都重新計算默認值表達式的值。也就是說,參數(shù)默認值是惰性求值的。
let x = 99; function foo(p = x + 1) { console.log(p); } foo() // 100 x = 100; foo() // 101
上面代碼中,參數(shù)p的默認值是x + 1。這時,每次調(diào)用函數(shù)foo,都會重新計算x + 1,而不是默認p等于 100。
參數(shù)默認值的位置
通常情況下,定義了默認值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)。因為這樣比較容易看出來,到底省略了哪些參數(shù)。如果非尾部的參數(shù)設(shè)置默認值,實際上這個參數(shù)是沒法省略的。
// 例一 function f(x = 1, y) { return [x, y]; } f() // [1, undefined] f(2) // [2, undefined]) f(, 1) // 報錯 f(undefined, 1) // [1, 1] // 例二 function f(x, y = 5, z) { return [x, y, z]; } f() // [undefined, 5, undefined] f(1) // [1, 5, undefined] f(1, ,2) // 報錯 f(1, undefined, 2) // [1, 5, 2]
上面代碼中,有默認值的參數(shù)都不是尾參數(shù)。這時,無法只省略該參數(shù),而不省略它后面的參數(shù),除非顯式輸入undefined。
如果傳入undefined,將觸發(fā)該參數(shù)等于默認值,null則沒有這個效果。
function foo(x = 5, y = 6) { console.log(x, y); } foo(undefined, null) // 5 null
上面代碼中,x參數(shù)對應(yīng)undefined,結(jié)果觸發(fā)了默認值,y參數(shù)等于null,就沒有觸發(fā)默認值。
函數(shù)的 length 屬性
指定了默認值以后,函數(shù)的length屬性,將返回沒有指定默認值的參數(shù)個數(shù)。也就是說,指定了默認值后,length屬性將失真。
(function (a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2
上面代碼中,length屬性的返回值,等于函數(shù)的參數(shù)個數(shù)減去指定了默認值的參數(shù)個數(shù)。比如,上面最后一個函數(shù),定義了3個參數(shù),其中有一個參數(shù)c指定了默認值,因此length屬性等于3減去1,最后得到2。
這是因為length屬性的含義是,該函數(shù)預期傳入的參數(shù)個數(shù)。某個參數(shù)指定默認值以后,預期傳入的參數(shù)個數(shù)就不包括這個參數(shù)了。同理,后文的 rest 參數(shù)也不會計入length屬性。
(function(...args) {}).length // 0
如果設(shè)置了默認值的參數(shù)不是尾參數(shù),那么length屬性也不再計入后面的參數(shù)了。
(function (a = 0, b, c) {}).length // 0 (function (a, b = 1, c) {}).length // 1
作用域
一旦設(shè)置了參數(shù)的默認值,函數(shù)進行聲明初始化時,參數(shù)會形成一個多帶帶的作用域(context)。等到初始化結(jié)束,這個作用域就會消失。這種語法行為,在不設(shè)置參數(shù)默認值時,是不會出現(xiàn)的。
var x = 1; function f(x, y = x) { console.log(y); } f(2) // 2
上面代碼中,參數(shù)y的默認值等于變量x。調(diào)用函數(shù)f時,參數(shù)形成一個多帶帶的作用域。在這個作用域里面,默認值變量x指向第一個參數(shù)x,而不是全局變量x,所以輸出是2。
再看下面的例子。
let x = 1; function f(y = x) { let x = 2; console.log(y); } f() // 1
上面代碼中,函數(shù)f調(diào)用時,參數(shù)y = x形成一個多帶帶的作用域。這個作用域里面,變量x本身沒有定義,所以指向外層的全局變量x。函數(shù)調(diào)用時,函數(shù)體內(nèi)部的局部變量x影響不到默認值變量x。
如果此時,全局變量x不存在,就會報錯。
function f(y = x) { let x = 2; console.log(y); } f() // ReferenceError: x is not defined
下面這樣寫,也會報錯。
var x = 1; function foo(x = x) { // ... } foo() // ReferenceError: x is not defined
上面代碼中,參數(shù)x = x形成一個多帶帶作用域。實際執(zhí)行的是let x = x,由于暫時性死區(qū)的原因,這行代碼會報錯”x 未定義“。
4.2 rest 參數(shù)ES6 引入 rest 參數(shù)(形式為...變量名),用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments對象了。rest 參數(shù)搭配的變量是一個數(shù)組,該變量將多余的參數(shù)放入數(shù)組中。
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10
上面代碼的add函數(shù)是一個求和函數(shù),利用 rest 參數(shù),可以向該函數(shù)傳入任意數(shù)目的參數(shù)。
下面是一個 rest 參數(shù)代替arguments變量的例子。
// arguments變量的寫法 function sortNumbers() { return Array.prototype.slice.call(arguments).sort(); } // rest參數(shù)的寫法 const sortNumbers = (...numbers) => numbers.sort();
上面代碼的兩種寫法,比較后可以發(fā)現(xiàn),rest 參數(shù)的寫法更自然也更簡潔。
rest 參數(shù)中的變量代表一個數(shù)組,所以數(shù)組特有的方法都可以用于這個變量。下面是一個利用 rest 參數(shù)改寫數(shù)組push方法的例子。
function push(array, ...items) { items.forEach(function(item) { array.push(item); console.log(item); }); } var a = []; push(a, 1, 2, 3)
注意,rest 參數(shù)之后不能再有其他參數(shù)(即只能是最后一個參數(shù)),否則會報錯。
// 報錯 function f(a, ...b, c) { // ... }
函數(shù)的length屬性,不包括 rest 參數(shù)。
(function(a) {}).length // 1 (function(...a) {}).length // 0 (function(a, ...b) {}).length // 14.3 箭頭函數(shù)
基本用法
ES6 允許使用“箭頭”(=>)定義函數(shù)。
var f = v => v;
上面的箭頭函數(shù)等同于:
var f = function(v) { return v; };
如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù),就使用一個圓括號代表參數(shù)部分。
var f = () => 5; // 等同于 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) { return num1 + num2; };
如果箭頭函數(shù)的代碼塊部分多于一條語句,就要使用大括號將它們括起來,并且使用return語句返回。
var sum = (num1, num2) => { return num1 + num2; }
由于大括號被解釋為代碼塊,所以如果箭頭函數(shù)直接返回一個對象,必須在對象外面加上括號,否則會報錯。
// 報錯 let getTempItem = id => { id: id, name: "Temp" }; // 不報錯 let getTempItem = id => ({ id: id, name: "Temp" });
如果箭頭函數(shù)只有一行語句,且不需要返回值,可以采用下面的寫法,就不用寫大括號了。
let fn = () => void doesNotReturn();
箭頭函數(shù)可以與變量解構(gòu)結(jié)合使用。
const full = ({ first, last }) => first + " " + last; // 等同于 function full(person) { return person.first + " " + person.last; }
箭頭函數(shù)使得表達更加簡潔。
const isEven = n => n % 2 == 0; const square = n => n * n;
上面代碼只用了兩行,就定義了兩個簡單的工具函數(shù)。如果不用箭頭函數(shù),可能就要占用多行,而且還不如現(xiàn)在這樣寫醒目。
箭頭函數(shù)的一個用處是簡化回調(diào)函數(shù)。
// 正常函數(shù)寫法 [1,2,3].map(function (x) { return x * x; }); // 箭頭函數(shù)寫法 [1,2,3].map(x => x * x);
另一個例子是
// 正常函數(shù)寫法 var result = values.sort(function (a, b) { return a - b; }); // 箭頭函數(shù)寫法 var result = values.sort((a, b) => a - b); 下面是 rest 參數(shù)與箭頭函數(shù)結(jié)合的例子。 const numbers = (...nums) => nums; numbers(1, 2, 3, 4, 5) // [1,2,3,4,5] const headAndTail = (head, ...tail) => [head, tail]; headAndTail(1, 2, 3, 4, 5) // [1,[2,3,4,5]]
使用注意點
箭頭函數(shù)有幾個使用注意點。
(1)函數(shù)體內(nèi)的this對象,就是定義時所在的對象,而不是使用時所在的對象。
(2)不可以當作構(gòu)造函數(shù),也就是說,不可以使用new命令,否則會拋出一個錯誤。
(3)不可以使用arguments對象,該對象在函數(shù)體內(nèi)不存在。如果要用,可以用 rest 參數(shù)代替。
(4)不可以使用yield命令,因此箭頭函數(shù)不能用作 Generator 函數(shù)。
上面四點中,第一點尤其值得注意。this對象的指向是可變的,但是在箭頭函數(shù)中,它是固定的。
function foo() { setTimeout(() => { console.log("id:", this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // id: 42
上面代碼中,setTimeout的參數(shù)是一個箭頭函數(shù),這個箭頭函數(shù)的定義生效是在foo函數(shù)生成時,而它的真正執(zhí)行要等到100毫秒后。如果是普通函數(shù),執(zhí)行時this應(yīng)該指向全局對象window,這時應(yīng)該輸出21。但是,箭頭函數(shù)導致this總是指向函數(shù)定義生效時所在的對象(本例是{id: 42}),所以輸出的是42。
箭頭函數(shù)可以讓setTimeout里面的this,綁定定義時所在的作用域,而不是指向運行時所在的作用域。下面是另一個例子。
function Timer() { this.s1 = 0; this.s2 = 0; // 箭頭函數(shù) setInterval(() => this.s1++, 1000); // 普通函數(shù) setInterval(function () { this.s2++; }, 1000); } var timer = new Timer(); setTimeout(() => console.log("s1: ", timer.s1), 3100); setTimeout(() => console.log("s2: ", timer.s2), 3100); // s1: 3 // s2: 0
上面代碼中,Timer函數(shù)內(nèi)部設(shè)置了兩個定時器,分別使用了箭頭函數(shù)和普通函數(shù)。前者的this綁定定義時所在的作用域(即Timer函數(shù)),后者的this指向運行時所在的作用域(即全局對象)。所以,3100毫秒之后,timer.s1被更新了3次,而timer.s2一次都沒更新。
箭頭函數(shù)可以讓this指向固定化,這種特性很有利于封裝回調(diào)函數(shù)。下面是一個例子,DOM 事件的回調(diào)函數(shù)封裝在一個對象里面。
var handler = { id: "123456", init: function() { document.addEventListener("click", event => this.doSomething(event.type), false); }, doSomething: function(type) { console.log("Handling " + type + " for " + this.id); } };
上面代碼的init方法中,使用了箭頭函數(shù),這導致這個箭頭函數(shù)里面的this,總是指向handler對象。否則,回調(diào)函數(shù)運行時,this.doSomething這一行會報錯,因為此時this指向document對象。
this指向的固定化,并不是因為箭頭函數(shù)內(nèi)部有綁定this的機制,實際原因是箭頭函數(shù)根本沒有自己的this,導致內(nèi)部的this就是外層代碼塊的this。正是因為它沒有this,所以也就不能用作構(gòu)造函數(shù)。
所以,箭頭函數(shù)轉(zhuǎn)成 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); }
上面代碼中,轉(zhuǎn)換后的ES5版本清楚地說明了,箭頭函數(shù)里面根本沒有自己的this,而是引用外層的this。
除了this,arguments在箭頭函數(shù)之中也是不存在的,它是指向外層函數(shù)的對應(yīng)變量。
function foo() { setTimeout(() => { console.log("args:", arguments); }, 100); setTimeout(function() { console.log("args:", arguments); }, 100); } foo(2, 4, 6, 8) // args: [2, 4, 6, 8]
上面代碼中,箭頭函數(shù)內(nèi)部的變量arguments,其實是函數(shù)foo的arguments變量。
另外,由于箭頭函數(shù)沒有自己的this,所以當然也就不能用call()、apply()、bind()這些方法去改變this的指向。
(function() { return [ (() => this.x).bind({ x: "inner" })() ]; }).call({ x: "outer" }); // ["outer"]
上面代碼中,箭頭函數(shù)沒有自己的this,所以bind方法無效,內(nèi)部的this指向外部的this。
長期以來,JavaScript 語言的this對象一直是一個令人頭痛的問題,在對象方法中使用this,必須非常小心。箭頭函數(shù)”綁定”this,很大程度上解決了這個困擾。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/85065.html
摘要:字符串的擴展字符的表示法允許采用形式表示一個字符,其中表示字符的碼點。返回布爾值,表示參數(shù)字符串是否在源字符串的頭部。使用和這兩個常量,用來表示這個范圍的上下限。對于那些無法用個二進制位精確表示的小數(shù),方法返回最接近這個小數(shù)的單精度浮點數(shù)。 字符串的擴展 字符的 Unicode 表示法 JavaScript 允許采用uxxxx形式表示一個字符,其中xxxx表示字符的 Unicode 碼...
摘要:標準入門讀書筆記和命令新增命令,用于聲明變量,是塊級作用域。用于頭部補全,用于尾部補全。函數(shù)調(diào)用的時候會在內(nèi)存形成一個調(diào)用記錄,又稱為調(diào)用幀,保存調(diào)用位置和內(nèi)部變量等信息。等到執(zhí)行結(jié)束再返回給,的調(diào)用幀才消失。 《ES6標準入門》讀書筆記 @(StuRep) showImg(https://segmentfault.com/img/remote/1460000006766369?w=3...
摘要:字符串的擴展一字符串的遍歷器接口為字符串添加了遍歷器接口,使得字符串可以被循環(huán)遍歷。返回布爾值,表示參數(shù)字符串是否在源字符串的頭部。三方法返回一個新字符串,表示將原字符串重復次。如果模板字符串中的變量沒有聲明,將報錯。 字符串的擴展 一、 字符串的遍歷器接口 ES6 為字符串添加了遍歷器接口,使得字符串可以被for...of循環(huán)遍歷。 for (let codePoint of foo...
摘要:字符串的擴展一字符串的遍歷器接口為字符串添加了遍歷器接口,使得字符串可以被循環(huán)遍歷。返回布爾值,表示參數(shù)字符串是否在源字符串的頭部。三方法返回一個新字符串,表示將原字符串重復次。如果模板字符串中的變量沒有聲明,將報錯。 字符串的擴展 一、 字符串的遍歷器接口 ES6 為字符串添加了遍歷器接口,使得字符串可以被for...of循環(huán)遍歷。 for (let codePoint of foo...
閱讀 3447·2023-04-26 01:45
閱讀 2222·2021-11-23 09:51
閱讀 3638·2021-10-18 13:29
閱讀 3428·2021-09-07 10:12
閱讀 698·2021-08-27 16:24
閱讀 1765·2019-08-30 15:44
閱讀 2192·2019-08-30 15:43
閱讀 2944·2019-08-30 13:11