本文作者:IMWeb coolriver 原文出处:IMWeb社区 未经同意,禁止转载
本文为初步阅读ECMAScript6入门后的一些记录与感想。
ES6的设计目标,是使得JavaScript语言可以用来编写大型复杂的应用程序,成为企业级开发语言。ES6将于2015年6月正式发布。
现在最新版本的Nodejs上可以通过`--harmony`参数开启ES6的部分支持。如果旧的Nodejs版本不支持或者想体验更多的ES6 特性
可以 使用Google的[traceur](https://github.com/google/traceur-compiler)工具。traceur可以在前端编译ES6代码,也可以
在node上 编译或执行ES6代码。
npm install -g traceur
traceur test.es6.js
traceur --script test.es6.js --out test.es5.js
ES6在很方面都对ES5有增强,下面将从不同方面对ES6在ES5上的加强进行介绍。 在ES5中,除了全局作用域外,限定所声明的变量的作用域是函数作用域。这使得ES5在很多情况下为了模拟块级作用域(避免变量名污染)需要使用立即执行的匿名函数。在ES6中新增了声明块级使用域变量的关键字let
。与var
相比,使用let
声明的变量有如下特点:
let
声明的变量所在的作用域为块级。let
声明的变量不存在变量提升。let
声明的变量不允许重复声明,否则会报错。 使用let
可以替代ES5中为了模拟块级作用域而使用的立即执行的匿名函数: //ES5实现方法:
(function(){
for (var i = 0;i < 10;i++){
//doSomething
}
})();
//ES6实现方法:
for (let i = 0;i < 10;i++){
//doSomething
}
ES6中可以使用const
关键字来声明常量,被声明的常量不能被修改。与使用let
声明的变量类似,const
声明的常量为块级作用域,不存在变量提升,且不可重复声明。
const只限定就是所以的地址不能改变,意味着如果声明的目标为对象,那么对象的属性是可以修改的。书中建议如果要使对象为常量的话可以配合Object.freeze()
函数来实现:
const foo = Object.freeze({a:1});
//foo = {b:2} 无法修改foo
//foo.a = 2 无法修改foo.a
以上方法中的Object.freeze()
函数本身有局限性,它只能冻结对象的属性不被修改,并不能冻结它的属性的属性被修改。如果要实现将对象内部所有属性冻结,需要使用自定义的强化的冻结函数。关于深度冻结对象的方法在codewars上的一个题目有描述,具体方案如下:
Object.deepFreeze = function (object) {
Object.freeze(object);
Object.keys(object).forEach(function(key) {
if(typeof(object[key]) == 'object')
return Object.deepFreeze(object[key]);
});
}
通过以上deepFreeze
即可实现完全将对象常量化。效果如下:
const foo = Object.freeze({a:[1]}); //使用原始的冻结函数
foo.a.push(2); //本操作可以使foo.a变为[1,2]
const foo2 = Object.deepFreeze({a:[1]}); //使用深度冻结函数
foo2.a.push(2); //本操作无法改变foo2.a
全局对象是最顶层的对象,在浏览器环境指的是window
对象,在Node.js指的是global
对象。ES5规定,所有全局变量都是全局对象的属性。
ES6规定,var
命令和function
命令声明的全局变量,属于全局对象的属性;let
命令、const
命令、class
命令声明的全局变量,不属于全局对象的属性。
ES6中新增了一种赋值方法,可以批量地从对象(或数组)中提取出相应的值赋值到一组对应的变量上。下面为数组形式的解构赋值:
//数组的解构赋值
var [a,b,c] = [1,2,3];
//相当于
var a = 1,b = 2,c = 3;
下面为对象形式的解构赋值:
//对象的解构赋值方式一
var {bar, foo} = {foo: "aaa", bar: "bbb"};
foo //"aaa"
bar //"bbb"
//对象的解构赋值方式二
var {first: f, last: l} = {first: "hello", last: "world"};
f //"hello"
l //"world"
解构赋值可以带来很多便利:
[x, y] = [y, x]
for (let [key, value] of map){}
const {SourceMapConstumer, SourceNode} = require("source-map")
"\uXXXX"
来表示字符的方法不能表示大于0XFFFF的字符,在ES6中可以使用大括号将码点括起来就可以表示大于0XFFFF的字符:"\u{XXXX}"
。var s = "?";
/^.$/.test(s) // false
/^.$/u.test(s) // true
*传统上,JavaScript只有indexOf
方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。
includes():返回布尔值,表示是否找到了参数字符串. startsWith():返回布尔值,表示参数字符串是否在源字符串的头部. endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部.undefined
"x".repeat(3) // "xxx"
"hello".repeat(2) // "hellohello"
$("#result").append(`
There are ${basket.count} items
in your basket,${basket.onSale}
are on sale!
`);
0b111110111 === 503 // true
0o767 === 503 // true
isFinite()
,isNaN()
,parseInt()
, parseFloat()
移至Number
上,分别变为Number.isFinite()
,Number.isNaN()
,Number.parseInt()
, Number.parseFloat()
。这样做是为了逐步减少全局方法,使语言逐步模块化。Math.trunc(n) 去除一个数的小数部分 Math.sign(b) 判断一个数是正数、负数还是0. Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine) Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine) Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent) Math.cbrt(x) 返回x的立方根 Math.clz32(x) 返回x的32位二进制整数表示形式的前导0的个数 Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine) Math.expm1(x) 返回eˆx - 1 Math.fround(x) 返回x的单精度浮点数形式 Math.hypot(...values) 返回所有参数的平方和的平方根 Math.imul(x, y) 返回两个参数以32位整数形式相乘的结果 Math.log1p(x) 返回1 + x的自然对数 Math.log10(x) 返回以10为底的x的对数 Math.log2(x) 返回以2为底的x的对数 Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)undefined
Array.from()
,可以将类数组对象(例如函数中的arguments)和遍历对象(如ES6中的Map和Set对象)。该函数可以方便地替代ES5中使用Array.prototype.slice
来进行数组转换。Array.of()
,用来将一组值转换为数组。该函数主要用于解决ES5中使用Array()
构造数组的不足(因为参数个数的不同导致构造行为的不同): //ES5:
Array() // []
Array(3) // [undefined, undefined, undefined]
Array(3,11,8) // [3, 11, 8]
//ES6:
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
find()
和findIndex()
。两者的参数都是一个回调函数,返回第一个回调函数返回值为true的元素的值(或下标)。这两个函数解决了ES5中indexOf()
函数不能找到NaN
元素的问题。fill()
,使用指定值对数组进行填充。参数为一个时将数组所有元素替换为参数的值,参数为三个时,将指定起始位置(第二个参数)和终止位置(第三个参数)替换为目标值(第一个参数)ectries()
,keys()
,values()
,三个都返回遍历器: for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
map()
和filter()
实现): //ES5:
var a1 = [1, 2, 3, 4];
var a2 = a1.map(function(i){return i * 2});
var a3 = a1.filter(function(i){return i > 2});
var a4 = a1.filter(function(i){return i > 2})
.map(function(i){return i * 2});
a2 // [2, 4, 6, 8]
a3 // [3, 4]
a4 // [6,8]
//ES6:
var a1 = [1, 2, 3, 4];
var a2 = [for (i of a1) i * 2];
var a3 = [for (i of a1) if (i > 2) i];
var a4 = [for (i of a1) if (i > 2) i * 2];//同时实现ES5中的map和filter
a2 // [2, 4, 6, 8]
a3 // [3, 4]
a4 // [6,8]
Array.observe()
和Array.unobserve()
方法用于监听(取消监听)数组的变化(包括add,update,delete,splice四种操作引起的数组变化)function f( x, y ) {
return { x, y, method(){return 1;}};
}
// 等同于
function f( x, y ) {
return { x: x, y: y, method: function(){return 1}};
}
let propKey = 'foo';
let obj = {
[propKey]: true,
['a'+'bc']: 123,
['h'+'ello'](){return 'hi';}
};
Object.is()
方法。用于比较两个值是否严格相等,相对于运算符===有两个不同:一是+0不等于-0,二是NaN等于自身。Object.assign()
方法。将源对象(第一个参数后的所有参数)的所有可枚举属性复制到目标对象(第一个参数),后面的属性值会覆盖前面的同名属性。可用于: 为对象添加属性和方法 克隆对象(不能复制原型链上的属性,可以和Object.create()配合来弥补) 合并多个对象 为属性指定默认值undefined
__proto__
属性直接获取和操作对象的方式,虽然不在ES6标准中,但是所有最新版本的浏览器都部署了这个属性。ES6推荐使用Object.setPrototypeOf()
来设置对象的原型,使用Object.getPrototypeOf()
来获取对象的原型。// 没有参数的情况
var s1 = Symbol();
var s2 = Symbol();
s1 === s2 // false
// 有参数的情况
var s1 = Symbol("foo");
var s2 = Symbol("foo");
s1 === s2 // false
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
proxy.times // 35
proxy.name // 35
proxy.title // 35
Object.observe()
和Object.unobserve()
方法用于监听(和取消监听)对象(以及数组的变化)。可以方便地实现数据绑定。以上两方法不属于ES6,而属于ES7,但是在Chrome 33起就已经支持。//ES5:
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
//ES6:
function log(x, y = 'World') {
console.log(x, y);
}
//参数默认值和解构赋值配合使用:
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined, 5
foo({x: 1}) // 1, 5
foo({x: 1, y: 2}) // 1, 2
rest
参数来获取函数的多余参数,这样就不需要用arguments
对象。rest
参数搭配一个数组就是,多余的参数会放入数组中:function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
rest
参数注意点:
rest参数后不能再有其他参数,否则会报错 函数的
length
属性不包括rest参数undefined
...
,相当于rest参数的逆运算,将一个数组转换为用逗号分隔的参数序列,主要用于函数调用(ES5中需要使用apply
函数来实现)://ES:
function f(x, y, z) { }
var args = [0, 1, 2];
f.apply(null, args);
//ES6:
function f(x, y, z) { }
var args = [0, 1, 2];
f(...args);
扩展运算符可以将一个数值扩展成数组,还可以将类似数组的对象转换为真正的数组:
[...5]
// [0, 1, 2, 3, 4, 5]
[..."hello"]
// [ "h", "e", "l", "l", "o" ]
var nodeList = document.querySelectorAll('div');
var array = [...nodeList];
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
var go = function*(){
yield 1;
yield 2;
yield 3;
};
[...go()] // [1, 2, 3]
=>
。箭头函数能够简化函数定义的写法,且能解决常规定义函数时this
变量在挂靠 时的变更。var f = v => v;
//等同于下面写法
var f = function(v) {
return v;
};
//箭头函数支持的写法如下:
var f = () => 5;
var sum = (num1, num2) => num1 + num2;
var sum = (num1, num2) => { return num1 + num2; }
使用箭头函数时需要注意以下几点:
函数体内的
this
对象,绑定定义时所在的对象,而不是使用时所在的对象 不可当作构造函数,也就是说不能使用new
命令,否则会抛出错误 不可以使用arguments
对象,因为该对象在函数体内不存在undefined
//下面属于尾调用:
function f(x){
return g(x);
}
function f(x) {
if (x > 0) {
return m(x)
}
return n(x);
}
//下面不属于尾调用:
function f(x){
let y = g(x);
return y;
}
function f(x){
return g(x) + 1;
}
尾调用优化只保留内层函数的调用帧,而不需要将上层函数的调用帧存放于调用栈中。尾调用优化可以节省内存。在递归函数中,如果调用自身的函数为尾调用,那么就可以进行尾递归优化,很大地节省了递归函数执行过程中耗费的内存。如将一些递归函数改写为尾调用的模式即可极大地优化程序执行效率和耗费内存:
//原始写法:
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
factorial(5) // 120
//改写为尾调用(尾递归):
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5, 1) // 120
Set
数据结构,类似于数组,但是成员的值是唯一的,不存在重复的值。添加重复的值不会改变原结构中的内容。在Set
结构中加入值时不进行类型转换,且判断新增值与原有值是否相同的比较方法类似于===
运算符,唯一的例外是NaN
等于自身。Set
的基本使用方法如下: var s = new Set();
[2,3,5,4,5,2,2].map(x => s.add(x))
for (i of s) {console.log(i)}
// 2 3 4 5
var items = new Set([1,2,3,4,5,5,5,5]);
items.size
// 5
Set
结构有以下属性:
Set.prototype.constructor:构造函数,默认就是Set函数。 Set.prototype.size:返回Set的成员总数。
Set
结构有以下方法:
add(value):添加某个值,返回Set结构本身。 delete(value):删除某个值,返回一个布尔值,表示删除是否成功。 has(value):返回一个布尔值,表示该值是否为Set的成员。 clear():清除所有成员,没有返回值 values():返回一个值的遍历器 keys():返回一个键的遍历器 entries():返回一个键值对的遍历器 forEach(fn):对每个成员执行某种操作,返回修改后的Set结构undefined
WeakSet
结构。与Set
类似,也是不重复的值的集合。与Set
有两个不同点: WeakSet
结构没有size
属性,有以下三个方法:
add(value):向WeakSet实例添加一个新成员 delete(value):清除WeakSet实例的指定成员 has(value):返回一个布尔值,表示某个值是否在WeakSet实例中undefined
WeakSet的一个用处是存储DOM节点,这样就不用担心节点从文档被移除时引起内存泄漏
Map
数据结构。类似于对象,是一个键值对的集合,但是键的范围不像对象一样仅限于字符串,各类型的值(包括对象)都可以当作Map
实例的键值。基本使用方法如下: var m = new Map();
var o = {p: "Hello World"};
m.set(o, "content")
m.get(o) // "content"
var map = new Map([ ["name", "张三"], ["title", "Author"]]);
map.size // 2
map.has("name") // true
map.get("name") // "张三"
如果Map的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map将其视为一个键,包括0和-0。另外,虽然NaN不严格相等于自身,但Map将其视为同一个键。
如果Map的键是对象(或数组),则只有两个对象的地址相同时,才将两者视为同一个键。
Map
结构有如下属性和方法:
size:返回成员总数 set(key, value):设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则 键值会被更新,否则就新生成该键。 get(key):读取key对应的键值,如果找不到key,返回undefined。 has(key):返回一个布尔值,表示某个键是否在Map数据结构中。 delete(key):删除某个键,返回true。如果删除失败,返回false。 clear(): 清除所有成员,没有返回值。 keys():返回键名的遍历器。 values():返回键值的遍历器。 entries():返回所有成员的遍历器。
WeakMap
结构。与Map
类似,区别是它只接受对象作为键名(null除外),且键名指向的对象不讲稿垃圾回收机制。用于存储DOM元素时,可防止内存泄漏: var wm = new WeakMap();
var element = document.querySelector(".element");
wm.set(element, "Original");
wm.get(element) // "Original"
element.removeChild(element);
element = null;
wm.get(element) // undefined
WeakMap
实例没有size
属性,且只有四个方法可用:get(),set(),has(),delete()
Iterator是一种接口规格,任何数据结构只要部署了这个接口就可以使用for...of进行遍历操作。Iterator为各种数据结构白日做梦了统一简便的接口,且使得数据结构中的成员能按照某种次序排列。
Iterator提供一个指针,默认指向数据结构的起始位置,第一次调用Iterator的next方法可以将指针指向第一个成员,第二次调用就指向第二个成员,直到指向数据结构的结束位置。ES6中的Iterator接口要求在每次调用next方法时返回一个{value: v, done: bool}
格式的对象,value表示当前成员的值,done表示遍历是否结束。下面的例子在现有数组上模拟了一个Iterator:
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
}
}
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
Symbol.iterator
上。有三类数据结构有原生的Iterator接口:数组,Set和Map,某些类似数组对象。 let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
默认的iterator接口会在以下场合中被调用:
...
Generator函数的写法类似于变通函数,有两点与普通函数不同,一是function命令与函数名之间有一个星号,二是函数内使用yield
定义遍历器的每个成员。执行它时会返回一个函数状态的遍历器。遍历器每次执行next方法时会遍历函数内部通过yield
定义的状态:
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()// { value: 'hello', done: false }
hw.next()// { value: 'world', done: false }
hw.next()// { value: 'ending', done: true }
hw.next()// { value: undefined, done: true }
yield
语句本身没有返回值,当next方法带了一个参数,这个参数就会被当作上一个yield
语句的返回值: function* f() {
for(var i=0; true; i++) {
var reset = yield i;
if(reset) { i = -1; }
}
}
var g = f();
g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false }
yield *
语句表示后面接的是跟遍历器: let delegatedIterator = (function* () {
yield 'Hello!';
yield 'Bye!';
}());
let delegatingIterator = (function* () {
yield 'Greetings!';
yield* delegatedIterator;
yield 'Ok, bye.';
}());
for(let value of delegatingIterator) {
console.log(value);
}
// "Greetings!
// "Hello!"
// "Bye!"
// "Ok, bye."
function* main() {
var result = yield request("http://some.url");
var resp = JSON.parse(result);
console.log(resp.value);
}
function request(url) {
makeAjaxCall(url, function(response){
it.next(response);
});
}
var it = main();
it.next();
//回调函数写法:
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});
//Promise写法:
Q.fcall(step1)
.then(step2)
.then(step3)
.then(step4)
.then(function (value4) {
// Do something with value4
}, function (error) {
// Handle any error from step1 through step4
})
.done();
//Generator写法:
function* longRunningTask() {
try {
var value1 = yield step1();
var value2 = yield step2(value1);
var value3 = yield step3(value2);
var value4 = yield step4(value3);
// Do something with value4
} catch (e) {
// Handle any error from step1 through step4
}
}
scheduler(longRunningTask());
function scheduler(task) {
setTimeout(function() {
var taskObj = task.next(task.value);
// 如果Generator函数未结束,就继续调用
if (!taskObj.done) {
task.value = taskObj.value
scheduler(task);
}
}, 0);
}
function* makeSimpleGenerator(array){
var nextIndex = 0;
while(nextIndex < array.length){
yield array[nextIndex++];
}
}
var gen = makeSimpleGenerator(['yo', 'ya']);
gen.next().value // 'yo'
gen.next().value // 'ya'
gen.next().done // true
//ES5:
var ticking = true;
var clock = function() {
if (ticking)
console.log('Tick!');
else
console.log('Tock!');
ticking = !ticking;
}
//ES6:
var clock = function*(_) {
while (true) {
yield _;
console.log('Tick!');
yield _;
console.log('Tock!');
}
};
ES6提供了原生的Promise对象用来方便地表达异步操作。Promise的基本用法如下:
var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(value) {
// failure
});
resolve
和reject
方法,分别用来处理异步操作成功和失败。也可以在Promise实例生成后,通过then
方法指定resolve
和reject
方法: function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
timeout(100).then(() => {
console.log('done');
});
Promise.prototype.then()
返回的是一个新的Promise对象,所以可使用链式写法。如果链式写法中前一个then
函数返回的是Promise对象,后一个回调函数会等待该Promise对象有运行结果才会执行: getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// proceed
});
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// 对comments进行处理
});
Promise.all()
和Promise.race()
用来将多个Promise实例包装成一个新的Promise实例。 var p = Promise.all([p1,p2,p3]);
Promise.all()
的规则如下:
1. 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
2. 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race()
的规则如下:
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。
Promise.resolve()
用来将现有对象转换为Promise对象 Promise.reject(reason)
返回一个新的Promise对象,该实例的状态为rejected。Promise.reject
方法的参数reason
,会被传递给实例的回调函数。 ES5中没有类的概念,而是通过构造函数来进行对象的构建以及相关的面向对象操作。ES6中新增了class
关键字来定义类,使ES更具面向对象语言的语法:
//ES5:
function Point(x,y){
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '('+this.x+', '+this.y+')';
}
//ES6:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '('+this.x+', '+this.y+')';
}
}
constructor
方法是类的默认方法,通过new命令生成实例时会调用此方法。如果没有显示定义此方法,会默认被添加。constructor
方法中显示定义在其本身(this对象)上,否则都是定义在原型对象上: class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '('+this.x+', '+this.y+')';
}
}
var point = new Point(2, 3);
point.toString() // (2, 3)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
new Foo(); // ReferenceError
class Foo {}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 等同于parent.constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 等同于parent.toString()
}
}
子类必须在constructor方法中调用super方法,否则新建实例时会报错。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: undefined is not a function
class Bar extends Foo {
}
Bar.classMethod(); // 'hello'
export
关键字来输出变量: // profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//或者如下写法:
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
使用import
关键字从其它模块加载变量:
// main.js
import {firstName, lastName, year} from './profile';
function sfirsetHeader(element) {
element.textContent = firstName + ' ' + lastName;
}
纵观本书,ES6相对于E5在多个方面进行了增强和优化。主要分为四类:
this
对象的变更。最后发表一下本人的看法:
ES6新增很多很赞的语法特性:模板字符串,解构赋值,箭头函数等等。这些特性可以使写出的代码更加简洁。块级作用域的出现
使变量的命名更加安全和规范。强大的Generator函数可以实现意想不到的功能。
新增的模块特性使语言本身能够实现静态化模块依赖,相比于使用requireJS等动态模块来说有更高的效率。只是从现有的规范的
模块移植到ES6原生的模块还需要借助模块转换 工具(或者手动更改模块的写法)。
新增的类定义方法使语言终于能像主流的面向对象语言一样使用类了。但是语言在对象上的内部机制还是没变,依然是原型式继
承,依然有构造函数。使用类的面具将背后构造函数的本质遮盖起来,虽然使语言更“面向对象”,但不了解后面本质的人在使
用类时可能出现意想不到的问题。
最后,由于ES标准基本保持向下兼容,ES6在推出到普及这期间内,JS代码风格会变得更加多样化。甚至会让人怀疑这些代码
真的是同一种语言吗。。。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有