ES6所改良的javascript“缺陷”問題
塊級作用域
ES5沒有塊級作用域,只有全局作用域和函數(shù)作用域,由于這一點,變量的作用域甚廣,所以一進入函數(shù)就要馬上將它創(chuàng)建出來。這就造成了所謂的變量提升。
ES5的“變量提升”這一特性往往一不小心就會造成一下錯誤:
1.內(nèi)層變量覆蓋外層變量
var tmp = new Date(); function f() { console.log(tmp); if (false) { //執(zhí)行則undefined var tmp = "hello world"; } }
2.變量泄露,成為全局變量
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5
往常我們往往是使用閉包來解決這一問題的(比如自執(zhí)行函數(shù))?,F(xiàn)在,基于這一問題,ES6增加了塊級作用域,所以不再需要自執(zhí)行函數(shù)了。
let 和 const
ES6是是向后兼容的,而保持向后兼容性意味著永不改變JS代碼在Web平臺上的行為,所以var創(chuàng)建的變量其作用域依舊將會是全局作用域和函數(shù)作用域。這樣以來,即使擁有了塊級作用域,也無法解決ES5的“變量提升”問題。所以,這里ES6新增了倆個新關鍵詞:let和const。
1.let
“l(fā)et是更完美的var”,它有著更好的作用域規(guī)則。
2.const
const聲明一個只讀的常量。一旦聲明,常量的值就不能改變,但const聲明的對象可以有屬性變化(對象凍結(jié)Object.freeze)
const a = []; a.push('Hello'); // 可執(zhí)行 a = ['Dave']; // 報錯
也可以使用Object.freeze將對象凍結(jié)
const foo = Object.freeze({}); // 常規(guī)模式時,下面一行不起作用; // 嚴格模式時,該行會報錯 foo.prop = 123;//
使用let和const:
•變量只在聲明所在的塊級作用域內(nèi)有效
•變量聲明后方可使用(暫時性死區(qū))
•不能重復定義變量
•聲明的全局變量,不屬于全局對象的屬性
var a = 1; window.a // 1 let b = 1; window.b // undefined
this關鍵字
我們知道,ES5函數(shù)中的this指向的是運行時所在的作用域。比如
function foo() { setTimeout(function(){ console.log('id:', this.id); }, 100); } var id = 21; foo.call({id:42});//id: 21
在這里,我聲明了一個函數(shù)foo,其內(nèi)部為一個延遲函數(shù)setTimeout,每隔100ms打印一個this.id。我們通過foo.call({id:42})來調(diào)用它,并且為這個函數(shù)設定作用域。它真正執(zhí)行要等到100毫秒后,由于this指向的是運行時所在的作用域,所以這里的this就指向了全局對象window,而不是函數(shù)foo。這里:
•使用call來改變foo的執(zhí)行上下文,使函數(shù)的執(zhí)行上下文不再是window,從而來辨別setTimeout中的this指向
•setTimeout方法掛在window對象下,所以其this指向執(zhí)行時所在的作用域——window對象。
超時調(diào)用的代碼都是在全局作用域中執(zhí)行的,因此函數(shù)中this 的值在非嚴格模式下指向window 對象,在嚴格模式下是undefined --《javascript高級程序設計》
為了解決這一問題,我們往常的做法往往是將this賦值給其他變量:
function foo() {var that = this; setTimeout(function(){ console.log('id:', that.id); }, 100); } var id = 21; foo.call({id:42});//id: 42
而現(xiàn)在ES6推出了箭頭函數(shù)解決了這一問題。
箭頭函數(shù)
標識符=> 表達式
var sum = (num1, num2) => { return num1 + num2; } // 等同于 var sum = function(num1, num2) { return num1 + num2; };
•如果函數(shù)只有一個參數(shù),則可以省略圓括號
•如果函數(shù)只有一條返回語句,則可以省略大括號和return
•如果函數(shù)直接返回一個對象,必須在對象外面加上括號。(因為一個空對象{}和一個空的塊 {} 看起來完全一樣。所以需要用小括號包裹對象字面量。)
針對this關鍵字的問題,ES6規(guī)定箭頭函數(shù)中的this綁定定義時所在的作用域,而不是指向運行時所在的作用域。這一以來,this指向固定化了,從而有利于封裝回調(diào)函數(shù)。
function foo() {var that = this; setTimeout(()=>{ console.log('id:', that.id); }, 100); } var id = 21; foo.call({id:42});//id: 42
注意:箭頭函數(shù)this指向的固定化,并不是因為箭頭函數(shù)內(nèi)部有綁定this的機制,實際原因是箭頭函數(shù)根本沒有自己的this。而箭頭函數(shù)根本沒有自己的this,其內(nèi)部的this也就是外層代碼塊的this。這就導致了其:
•不能用作構造函數(shù)
•不能用call()、apply()、bind()這些方法去改變this的指向
類與繼承
傳統(tǒng)ECMAScript沒類的概念,它描述了原型鏈的概念,并將原型鏈作為實現(xiàn)繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。而實現(xiàn)這一行為的傳統(tǒng)方法便是通過構造函數(shù):
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')'; }; var p = new Point(1, 2);
在這里,構造函數(shù)Point會有一個原型對象(prototype),這個原型對象包含一個指向Point的指針(constructor),而實例p包含一個指向原型對象的內(nèi)部指針(prop)。所以整個的繼承是通過原型鏈來實現(xiàn)的。詳情可見我的這篇文章:javascript中的prototype和constructor
class
ES6提供了更接近傳統(tǒng)語言的寫法,引入了Class(類)這個概念,作為對象的模板。通過class關鍵字,可以定義類。但是類只是基于原型的面向?qū)ο竽J降恼Z法糖。對于class的引入,褒貶不一,很多人認為它反而是一大缺陷,但對我來說,這是一個好的語法糖,因為往常的原型鏈繼承的方式往往能把我繞那么一會兒。
//定義類 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var p = new Point(1, 2);
•類里面有一個constructor方法,它是類的默認方法,通過new命令生成對象實例時,自動調(diào)用該方法。一個類必須有constructor方法,如果沒有顯式定義,一個空的constructor方法會被默認添加。
•constructor方法中的this關鍵字代表實例對象,
•定義“類”的方法(如上例的toString)的時候,前面不需要加上function這個關鍵字,直接把函數(shù)定義放進去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。
•使用的時候,也是直接對類使用new命令,跟構造函數(shù)的用法完全一致
•類的所有方法都定義在類的prototype屬性上面
class的繼承——extend
Class之間可以通過extends關鍵字實現(xiàn)繼承,這比ES5的通過修改原型鏈實現(xiàn)繼承,要清晰和方便很多。
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 調(diào)用父類的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 調(diào)用父類的toString() } }
•super關鍵字,作為函數(shù)調(diào)用時(即super(...args)),它代表父類的構造函數(shù);作為對象調(diào)用時(即super.prop或super.method()),它代表父類。在這里,它表示父類的構造函數(shù),用來新建父類的this對象。
•子類必須在constructor方法中調(diào)用super方法,否則新建實例時會報錯。這是因為子類沒有自己的this對象,而是繼承父類的this對象,然后對其進行加工。如果不調(diào)用super方法,子類就得不到this對象。
模塊化
歷史上,JavaScript一直沒有模塊(module)體系,無法將一個大程序拆分成互相依賴的小文件,再用簡單的方法拼裝起來,這對開發(fā)大型的、復雜的項目形成了巨大障礙。為了適應大型模塊的開發(fā),社區(qū)制定了一些模塊加載方案,比如CMD和AMD。
ES6的模塊化寫法:
import { stat, exists, readFile } from 'fs';
上面代碼的實質(zhì)是從fs模塊加載3個方法,其他方法不加載。這種加載稱為“編譯時加載”,即ES6可以在編譯時就完成模塊加載,效率要比CommonJS模塊的加載方式高。當然,這也導致了沒法引用ES6模塊本身,因為它不是對象。
模塊功能主要由兩個命令構成:
•export
用于規(guī)定模塊的對外接口,對外的接口,必須與模塊內(nèi)部的變量建立一一對應關系。
// 寫法一 export var m = 1; //錯誤 export 1; // 寫法二 var m = 1; export {m}; //錯誤 export m; // 寫法三 重命名 var n = 1; export {n as m};
•import
用于輸入其他模塊提供的功能,它接受一個對象(用大括號表示),里面指定要從其他模塊導入的變量名(也可以使用*號整體加載)
字符串插值
在javascript的開發(fā)中,我們常常需要這樣來輸出模板:
function sayHello(name){ return "hello,my name is "+name+" I am "+getAge(18); } function getAge(age){ return age; } sayHello("brand") //"hello,my name is brand I am 18"
我們需要使用+來連接字符串和變量(或者表達式)。例子比較簡單,所以看上去無傷大雅,但是一旦在比較復雜的情況下,就會顯得相當繁瑣不方便,這一用法也讓我們不厭其煩。對此,ES6引入了模板字符串,可以方便優(yōu)雅地將 JS 的值插入到字符串中。
模板字符串
對于模板字符串,它:
•使用反引號``包裹;
•使用${}來輸出值;
•${}里的內(nèi)容可以是任何 JavaScript 表達式,所以函數(shù)調(diào)用和算數(shù)運算等都是合法的;
•如果一個值不是字符串,它將被轉(zhuǎn)換為字符串;
•保留所有的空格、換行和縮進,并輸出到結(jié)果字符串中(可以書寫多行字符串)
•內(nèi)部使用反引號和大括號需要轉(zhuǎn)義,轉(zhuǎn)義使用反斜杠\
對于上面的例子,模板字符串的寫法是:
function sayHello(name){ return `hello,my name is ${name} I am ${getAge(18)}`; } function getAge(age){ return age; } sayHello("brand") //"hello,my name is brandI am 18"
嚴格模式
嚴格模式的目標之一是允許更快地調(diào)試錯誤。幫助開發(fā)者調(diào)試的最佳途徑是當確定的問題發(fā)生時拋出相應的錯誤(throw errors when certain patterns occur),而不是悄無聲息地失敗或者表現(xiàn)出奇怪的行為(非嚴格模式下經(jīng)常發(fā)生)。嚴格模式下的代碼會拋出更多的錯誤信息,能幫助開發(fā)者很快注意到一些必須立即解決的問題。在 ES5 中, 嚴格模式是可選項,但是在 ES6 中,許多特性要求必須使用嚴格模式,這個習慣有助于我們書寫更好的 JavaScript。
以上所述是小編給大家介紹的ES6所改良的javascript“缺陷”問題,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- JavaScript學習筆記之ES6數(shù)組方法
- JavaScript中ES6 Babel正確安裝過程
- 深入理解React中es6創(chuàng)建組件this的方法
- 詳解JavaScript ES6中的模板字符串
- JavaScript ES6中CLASS的使用詳解
- 解析JavaScript的ES6版本中的解構賦值
- JavaScript ES6的新特性使用新方法定義Class
- 深入淺析react native es6語法
- ES6中如何使用Set和WeakSet
- Es6 寫的文件import 起來解決方案詳解
- 詳解Javascript ES6中的箭頭函數(shù)(Arrow Functions)
- 簡單談談ES6的六個小特性
- ES6生成器用法實例分析
相關文章
this,this,再次討論javascript中的this,超全面(經(jīng)典)
在JavaScript中,this 的概念比較復雜。除了在面向?qū)ο缶幊讨校瑃his 還是隨處可用的。這篇文章介紹了javascript中的this相關知識,對javascript this相關知識感興趣的朋友一起學習吧2016-01-01JavaScript實現(xiàn)事件總線(Event?Bus)的方法詳解
Event?Bus?事件總線,通常作為多個模塊間的通信機制,相當于一個事件管理中心。本文將介紹如何在JavaScript中實現(xiàn)事件總線,需要的可以參考一下2022-05-05