ES6容许直接写入变量和函数,做为对象的属性和方法。这样的书写更加简洁。javascript
function f( x, y ) { return { x, y }; }// 等同于function f( x, y ) { return { x: x, y: y }; }
示例:java
var Person = { name: '张三', birth:'1990-01-01',// 等同于hello: function ()...hello() { document.write('个人名字是', this.name); } }; Person.hello();
这种写法用于函数的返回值,将会很是方便。node
function getPoint() { var x = 1; var y = 10; return {x, y}; } getPoint() // {x:1, y:10}
JavaScript语言定义对象的属性,有两种方法。angularjs
let obj = {};// 方法一obj.foo = true;// 方法二obj['a'+'bc'] = 123; document.write(obj);
上面代码的方法一是直接用标识符做为属性名,方法二是用表达式做为属性名,这时要将表达式放在方括号以内。es6
若是使用字面量方式定义对象(使用大括号),在ES5中只能使用方法一(标识符)定义属性。数组
var obj = { foo: true, abc: 123};
ES6容许字面量定义对象时,用方法二(表达式)做为对象的属性名,即把表达式放在方括号内。浏览器
let propKey = 'foo'; let obj = { [propKey]: true, ['a'+'bc']: 123};
表达式还能够用于定义方法名。app
let obj = { ['h'+'ello']() { return 'hi'; } }; document.write(obj.hello()); // hi
Object.is()用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致,不一样之处只有两个:一是+0不等于-0,二是NaN等于自身。函数
+0 === -0 //trueNaN === NaN // false Object.is(+0, -0) // falseObject.is(NaN, NaN) // true
Object.assign方法用来将源对象(source)的全部可枚举属性,复制到目标对象(target)。它至少须要两个对象做为参数,第一个参数是目标对象,后面的参数都是源对象。只要有一个参数不是对象,就会抛出TypeError错误。工具
var target = { a: 1 }; var source1 = { b: 2 };var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
注意,若是目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
var target = { a: 1, b: 1 }; var source1 = { b: 2, c: 2 };var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
proto属性,用来读取或设置当前对象的prototype对象。该属性一度被正式写入ES6草案,但后来又被移除。目前,全部浏览器(包括IE11)都部署了这个属性。
// es6的写法 var obj = { __proto__: someOtherObj, method: function() { ... } } // es5的写法 var obj = Object.create(someOtherObj); obj.method = function() { ... }
ES6引入了一种新的原始数据类型Symbol,表示独一无二的ID。凡是属性名属于Symbol类型,就都是独一无二的,能够保证不会与其余属性名产生冲突。
let s = Symbol(); typeof s// "symbol"
typeof运算符的结果,代表变量s是Symbol数据类型,而不是字符串之类的其余类型。
注意,Symbol函数前不能使用new命令,不然会报错。这是由于生成的Symbol是一个原始类型的值,不是对象。
Symbol类型的值不能与其余类型的值进行运算,会报错。
var sym = Symbol('My symbol'); "your symbol is " + sym// TypeError: can't convert symbol to string`your symbol is ${sym}`// TypeError: can't convert symbol to string
可是,Symbol类型的值能够转为字符串。
var sym = Symbol('My symbol'); String(sym) // 'Symbol(My symbol)'sym.toString() // 'Symbol(My symbol)'
Proxy 内置的一个代理工具,使用他能够在对象处理上加一层屏障:
S6原生提供Proxy构造函数,用来生成Proxy实例。
var proxy = new Proxy(target, handler)new Proxy()表示生成一个Proxy实例,它的target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。var plain = { name : "hubwiz"};var proxy = new Proxy(plain, { get: function(target, property) {return property in target ? target[property] : "汇智网"; } }); proxy.name // "hubwiz"proxy.title // "汇智网"
Proxy(target, handler), 这里的 handler有以下的方法:
get(target, propKey, receiver):拦截对象属性的读取,好比proxy.foo和proxy['foo'],返回类型不限。最后一个参数receiver可选,当target对象设置了propKey属性的get函数时,receiver对象会绑定get函数的this对象。
set(target, propKey, value, receiver):拦截对象属性的设置,好比proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
has(target, propKey):拦截propKey in proxy的操做,返回一个布尔值。
deleteProperty(target, propKey) :拦截delete proxy[propKey]的操做,返回一个布尔值。
enumerate(target):拦截for (var x in proxy),返回一个遍历器。
hasOwn(target, propKey):拦截proxy.hasOwnProperty('foo'),返回一个布尔值。
ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一个数组。该方法返回对象全部自身的属性,而Object.keys()仅返回对象可遍历的属性。
getOwnPropertyDescriptor(target, propKey) :拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target) :拦截Object.getPrototypeOf(proxy),返回一个对象。
isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。
若是目标对象是函数,那么还有两种额外操做能够拦截。
apply(target, object, args):拦截Proxy实例做为函数调用的操做,好比proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args, proxy):拦截Proxy实例做为构造函数调用的操做,好比new proxy(...args)。
如今能够在定义函数的时候指定参数的默认值了,而不用像之前那样经过逻辑或操做符来达到目的了。
function sayHello(name){//传统的指定默认参数的方式var name = name||'hubwiz'; document.write('Hello '+name); } //运用ES6的默认参数function sayHello2(name='hubwiz'){ document.write(`Hello ${name}`); } sayHello(); //输出:Hello hubwizsayHello('汇智网'); //输出:Hello 汇智网sayHello2(); //输出:Hello hubwizsayHello2('汇智网'); //输出:Hello 汇智网
rest参数(形式为“...变量名”)能够称为不定参数,用于获取函数的多余参数,这样就不须要使用arguments对象了。
rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(1, 2, 3) // 6
不定参数的格式是三个句点后跟表明全部不定参数的变量名。好比以上示例中,...values 表明了全部传入add函数的参数。
扩展运算符(spread)是三个点(...)。它比如rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。该运算符主要用于函数调用。
它容许传递数组或者类数组直接作为函数的参数而不用经过apply。
var people=['张三','李四','王五']; //sayHello函数原本接收三个单独的参数people1,people2和people3function sayHello(people1,people2,people3){ document.write(`Hello ${people1},${people2},${people3}`); } //可是咱们将一个数组以拓展参数的形式传递,它能很好地映射到每一个单独的参数sayHello(...people); //输出:Hello 张三,李四,王五 //而在之前,若是须要传递数组当参数,咱们须要使用函数的apply方法sayHello.apply(null,people); //输出:Hello 张三,李四,王五
箭头函数是使用=>语法的函数简写形式。这在语法上与 C#、Java 8 和 CoffeeScript 的相关特性很是类似。
var array = [1, 2, 3];//传统写法array.forEach(function(v, i, a) { document.write(v); });//ES6array.forEach(v => document.write(v));
它们同时支持表达式体和语句体。与(普通的)函数所不一样的是,箭头函数和其上下文中的代码共享同一个具备词法做用域的this。
var evens = [1,2,3,4,5];var fives = [];// 表达式体var odds = evens.map(v => v + 1);var nums = evens.map((v, i) => v + i);var pairs = evens.map(v => ({even: v, odd: v + 1})); // 语句体nums.forEach(v => { if (v % 5 === 0) fives.push(v); }); document.write(fives); // 具备词法做用域的 thisvar bob = { _name: "Bob", _friends: ["Amy", "Bob", "Cinne", "Dylan", "Ellen"], printFriends() { this._friends.forEach(f => document.write(this._name + " knows " + f)); } } bob.printFriends();
函数体内的this对象,绑定定义时所在的对象,而不是使用时所在的对象。
不能够看成构造函数,也就是说,不可使用new命令,不然会抛出一个错误。
不可使用arguments对象,该对象在函数体内不存在。
上面三点中,第一点尤为值得注意。this对象的指向是可变的,可是在箭头函数中,它是固定的。
函数绑定运算符是并排的两个双引号(::),双引号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,做为上下文环境(即this对象),绑定到右边的函数上面。
let log = ::console.log;// 等同于var log = console.log.bind(console); foo::bar;// 等同于bar.call(foo); foo::bar(...arguments); i// 等同于bar.apply(foo, arguments);
尾调用的概念很是简单,一句话就能说清楚,就是指某个函数的最后一步是调用另外一个函数。
function f(x){ return g(x); }
上面代码中,函数f的最后一步是调用函数g,这就叫尾调用。
如下三种状况,都不属于尾调用。
// 状况一function f(x){ let y = g(x); return y; } // 状况二function f(x){ return g(x) + 1; } // 状况三function f(x){ g(x); }
以上的示例中,状况1、二是调用函数g以后,有其余操做。状况三等同于下面的代码。
function f(x){ g(x); return undefined; }
尾调用因为是函数的最后一步操做,因此不须要保留外层函数的调用记录,由于调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用记录,取代外层函数的调用记录就能够了。
function f() { let m = 1; let n = 2; return g(m + n); } f(); // 等同于function f() { return g(3); } f(); // 等同于g(3);
上面代码中,若是函数g不是尾调用,函数f就须要保存内部变量m和n的值、g的调用位置等信息。但因为调用g以后,函数f就结束了,因此执行到最后一步,彻底能够删除 f(x) 的调用帧,只保留g(3) 的调用帧。
“尾调用优化”(Tail call optimization),即只保留内层函数的调用帧,这样能够节省内存。
标签: nodejs, es6, javascript, angularjs