ECMAScript入门(一)
一、ECMAscript简介:
1.ECMAScript和JavaScript的关系:
前者是后者的规格,后者是前者的实现。
2.ES6和ECMAScript2015的关系:
版本命名方式改变了。ES6实际上就是ES2015.也可指新一代script标准,囊括ES2015、16、17
3.语法提案的批准流程
Stage 0 - Strawman(展示阶段)
Stage 1 - Proposal(征求意见阶段)
Stage 2 - Draft(草案阶段)
Stage 3 - Candidate(候选人阶段)
Stage 4 - Finished(定案阶段)
4.ECMAScript历史:
1997年发布第一版,1999年第三版,第四版失败原因是太激进,但其中很多激进部分被6继承,因此可以看做6是从此开始。第五版变动不大是由3.1改名而来,但是正是发布是09年。15年6月6正是成为标准,从四算起15年。
5.部署进度:
可通过一个GitHub链接查询浏览器的ES6支持情况。
node环境支持度更高,可通过以下代码查看。
// Linux & Mac
$ node --v8-options | grep harmony
// Windows
$ node --v8-options | findstr harmony
6.Babel转码器:
Babel是一个ES6转码器,其功能是将ES6代码转为ES5,这意味着可以用ES6写代码而不用考虑浏览器的ES6支持情况。
// npm安装Babel CLI 和 env preset(根据你支持的环境自动决定适合你的 Babel 插件的 Babel preset):
npm install --save-dev babel-cli babel-preset-env
// 创建 .babelrc 文件(或使用 package.json)
{
"presets": ["env"] //转码规则
"plugins":[] //插件
}
也可以限定所要支持的浏览器版本,这样变异的代码包更小。browserslist 支持的有效的查询格式
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
// Babel只转换语法,可以使用babel-polyfill支持新的全局变量,例如Promise、新的原生方法如String.padStart(left-pad)
npm install --save-dev babel-polyfill
//方便起见,你可以使用"node": "current" 来包含用于运行Babel的Node.js最新版所必需的polyfills和transforms
{
"presets": [
["env", {
"targets": {
"node": "current"
}
}]
]
}
二、let和const命令:
1.let命令
基本用法:
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
let声明的是所在块作用域内有效,用let不存在变量提升。for中每次let是新生成但是能记住上次值是因为JavaScript引擎内部记住了,此外设置循环部分是父作用域,循环体内部是子作用域。eg.
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
暂时性死区(TDZ):
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。暂时性死区说明typeof不再是百分百安全操作。
不允许重复声明:
let不允许在相同作用域内,重复声明同一个变量。
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
// 报错
因此,不能在函数内部重新声明参数。
function func(arg) {
let arg;
}
func() // 报错
function func(arg) {
{
let arg;
}
}
func() // 不报错
2.块级作用域:
块级作用域的作用:
避免内层覆盖外层,避免循环变量泄露为全局变量。
let为ES6带来了块级作用域
ES6允许块级作用域任意嵌套,外层无法读取内层。有了let就不用使用IIFE了。
IIFE( 立即调用函数表达式)是一个在定义时就会立即执行的 JavaScript 函数。
这是一个被称为 自执行匿名函数 的设计模式,主要包含两部分。第一部分是包围在 圆括号运算符() 里的一个匿名函数,这个匿名函数拥有独立的词法作用域。这不仅避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域。
第二部分再一次使用 () 创建了一个立即执行函数表达式,JavaScript 引擎到此将直接执行函数。
ES6中块级作用域内声明的函数类似于let。所以应该用函数表达式。
另外,还有一个需要注意的地方。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。
// 不报错
'use strict';
if (true) {
function f() {}
}
// 报错
'use strict';
if (true)
function f() {}
3.const命令
声明一个只读常量,一旦声明不能改变。,声明同时必须赋值,也有块级作用域。声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。同一块内不能重复声明。
本质上const是浅控制不可变。冻结对象应该用Object.freeze方法(添加新属性不起作用,严格报错)。
除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象彻底冻结的函数。
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
ES6 声明变量的六种方法
var function let const import class
4.顶层对象的属性
顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。
window.a = 1;
a // 1
a = 2;
window.a // 2
ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
5.global对象
ES5 的顶层对象,本身也是一个问题,因为它在各种实现里面是不统一的:
浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。
浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self。
Node 里面,顶层对象是global,但其他环境都不支持。
同一段代码为了能够在各种环境,都能取到顶层对象,现在一般是使用this变量,但是有局限性:
全局环境中,this会返回顶层对象。但是,Node 模块和 ES6 模块中,this返回的是当前模块。
函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。但是,严格模式下,这时this会返回undefined。
不管是严格模式,还是普通模式,new Function(‘return this’)(),总是会返回全局对象。但是,如果浏览器用了 CSP(Content Security Policy,内容安全策略),那么eval、new Function这些方法都可能无法使用。
综上所述,很难找到一种方法,可以在所有情况下,都取到顶层对象。下面是两种勉强可以使用的方法。
// 方法一
(typeof window !== 'undefined'
? window
: (typeof process === 'object' &&
typeof require === 'function' &&
typeof global === 'object')
? global
: this);
// 方法二
var getGlobal = function () {
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('unable to locate global object');
};
现在有一个提案,在语言标准的层面,引入global作为顶层对象。也就是说,在所有环境下,global都是存在的,都可以从它拿到顶层对象。
垫片库system.global模拟了这个提案,可以在所有环境拿到global。
// CommonJS 的写法
require('system.global/shim')();
// ES6 模块的写法
import shim from 'system.global/shim'; shim();
上面代码可以保证各种环境里面,global对象都是存在的。
// CommonJS 的写法
var global = require('system.global')();
// ES6 模块的写法
import getGlobal from 'system.global';
const global = getGlobal();
上面代码将顶层对象放入变量global。
文档是读阮一峰ECMAScript入门教程后的提炼总结。感恩阮大佬开源共享。