ES6新语法疑点简析

本文涵盖了一些ES6新语法可以形成迷惑的处所和一些发起。

1# 箭头函数

箭头函数看起来像是匿名函数表达式function(){}的简写,然则它不是。

这个例子应当很轻易看出来会有如何的题目:

function Apple(){}

Apple.prototype.check = ()=>{
    console.log(this instanceof Apple);
};

(new Apple()).check() // false

运用apply、call、bind转变箭头函数的this指向呢?

var i = 0;
var xx = ()=>{ console.log(++i, this) };
var yy = function(){ console.log(++i, this) };

xx();             // 1 window
xx.apply([]);     // 2 window
xx.bind([])();    // 3 window
yy();             // 4 window
yy.apply([]);     // 5 []
yy.bind([])();    // 6 []

明显apply、call、bind没法转变箭头函数的this指向,箭头函数的this肯定后没法变动。

在这些场景中不要运用箭头函数:

  • 当你须要一般运用this binding时,如函数组织器、prototype

  • 当你须要动态转变this的时刻

  • 针对事情报酬和代码量呈反比的顺序猿,在须要用到this binding的场景里,可以比较合适的简写情势是在新对象字面量语法里供应的:

var obj = {
    hello() { // 少写了一个function耶!
        console.log('world')
    } 
};

2# Promise

2.1# then

//1
fetch(xx, oo).then(handleResultAndReturnsAnPromise(result));
//2 
fetch(xx, oo).then(handleResultAndReturnsAnPromise);
//3 
fetch(xx, oo).then((result) => handleResultAndReturnsAnPromise(result));
//4
fetch(xx, oo).then(function(result) { handleResultAndReturnsAnPromise(result) });
  • 1与2、3、4均不等价:1同步挪用了handleResultAndReturnsAnPromise;而2~4均会致使handleResultAndReturnsAnPromise在fetch以后完成

  • 2与3/4则是运行时的挪用栈有辨别,3/4分外创建了一个匿名函数。

  • 3与4除了this binding的辨别,4的挪用返回值没有举行返回,如许将致使promise链断裂。

  • 1中须要注重的是,then(promise)内里传一个 Promise 对象是没有什么意义的,它会被当做then(null),在下面引荐的文章中,它被称作“Promise 穿透”

更多的使人殽杂的案例,请继续浏览《谈谈运用 promise 时刻的一些反形式》。

2.2# catch

在node的一些版本中,采纳Promise并遗忘给promise链增添catch(fn)then(null, fn),将致使代码中的异常被吞掉。

这个题目在新的v8中(node 6.6+,chrome最新版)会致使一个UnhandledPromiseRejectionWarning,防备开辟脱漏。

node -e 'Promise.reject()'
# UnhandledPromiseRejectionWarning: Unhandled promise rejection

2.3# resolve

Promise接口和jQuery完成的接口不一样,resolve只接收单参数,then的回调也只能拿到单参数。

在Promise范例中的单参数链式挪用场景下,可以应用解构、_.spread、接见自在变量等体式格局来处置惩罚多个过程当中获得的值:

new Promise(function(resolve, reject){
    let something = 1,
        otherstuff = 2;
    resolve({something, otherstuff});
}).then(function({something, otherstuff}){
    // handle something and otherstuff
});
Promise.all([
    Promise.resolve(40), Promise.resolve(36)
]).then(
    _.spread(function(first, second){
        // first: 40, second: 36
    })
);
let someMiddleResult;
fetch()
    .then(function(fetchResult){
        someMiddleResult = fetchResult;
    })
    .then(otherHandleFn)
    .then(function(otherHandleFnResult){
        // use both someMiddleResult and otherHandleFnResult now
    })

2.4# reject / throw

涌现reject接口,应当是第一次前端有时机拿异常处置惩罚流程做一般流程(比方)。不要如许做。

由于reject(new Error(""))throw new Error("")都能作为catch的进口,一些不可预知的毛病被抛出的时刻,如许的处置惩罚体式格局将会复杂化catch内的代码。不要用异常处置惩罚逻辑来做一般处置惩罚流程,这个划定规矩保证了代码可读性与可保护性。

throwreject都可以作为catch的进口,它们越发细致的辨别以下:

new Promise((resolve, reject) => {
    setTimeout(function(){
        reject(new Error('hello'));
    });
}).catch(() => console.log('reject'));
// reject

new Promise((resolve, reject) => {
    setTimeout(function(){
        throw new Error('hello');
    });
}).catch(() => console.log('throw'));
// Uncaught Error: hello

reject可以“穿透”回调;而throw限于函数作用域,没法“穿透”回调。

发起:

  1. 一般流程请挑选在then的时刻if..else,不要用reject替换

  2. 在须要走异常处置惩罚流程的时刻封装Error抛出,可以最大化的化简catch回调内里的处置惩罚逻辑,类似于e instanceof MyDesignedError

  3. 由于回调函数里的throw没法被自动捕获到,假如须要在回调中reject当前 promise,那末我们须要用reject而不是throw

  4. 在运用Promise接口的 polyfill 的场景,应当在reject后加一个return

3# let & const & var

看起来letconst的组合就像是一个能完整灭掉var的新特征,但对旧代码不能简朴的正则替换掉var,由于我们太习惯于滥用它的特征了——主如果声明提拔。

一些情况下会形成语法毛病:

try {
    let a = 10;

    if (a > 2) {
        throw new Error();
    }

    // ...
} catch (err) {
    console.log(a);
    // 若为var声明,不报错
    // 若为const、let声明:Uncaught ReferenceError: a is not defined
}

除了try..catch,隐式作育的块级作用域在forif..else中也将形成题目:

if(false) {
    let my = 'bad';
} else {
    console.log(my); // ReferenceError: my is not defined
}

解决计划却是很简朴,将作用域内的let放在更靠外层的位置即可。

varletconst的辨别以下(部份参考自stackoverflow):

  1. 作用域:letconst将制造一个块级作用域,在作用域以外此变量不可见,作用域外接见将致使SyntaxErrorvar遵照函数级作用域

  2. 全局影响:全局作用域下的var运用等同于设置window/global之上的内容,但letconst不会

  3. 提拔行动:var声明有提拔到当前函数作用域顶部的特征,但constlet没有,在声明前接见变量将致使SyntaxError

  4. 从新赋值:对const变量所做的从新赋值将致使TypeError,而varlet不会

  5. 从新声明:var声明的变量运用var再次声明不会涌现SyntaxError,但constlet声明的变量不能被从新声明,也不能覆蓋掉之前任何情势的声明:

var vVar = 1;
const vConst = 2;
let vLet = 3;

var vVar = 4;     // success
let vVar = 5;     // SyntaxError
const vVar = 6;   // SyntaxError

var vConst = 7;   // SyntaxError
let vConst = 8;   // SyntaxError
const vConst = 9; // SyntaxError

var vLet = 10;    // SyntaxError
let vLet = 11;    // SyntaxError
const vLet = 12;  // SyntaxError

4# 边境

本篇章集结 ES6 给予的差别边境条件,部份编译自 You don’t know JS

4.1# 函数默许参数值

function before(a) { var a = a || 1; console.log(a); }
function after(a = 1) { console.log(a); }

before(NaN) // 1
after(NaN) // NaN

新的写法的fallback逻辑只针对undefined有用。

4.2# Object.assign

Object.assign将给予一切的可罗列值,但不包含从原型链继续来的值:

let arr = [1, 2, 3],
    obj = {};
Object.assign(obj, arr);

obj[1] // 2
obj.length // undefined
Object.getOwnPropertyDescriptors(arr).length.enumerable // false

另外:Object.assign仅仅举行浅拷贝:

var orig = {
    a: [1, 2, 3]
},
    nObj = {};

Object.assign(nObj, orig);
orig.a.push(4);
nObj.a // [1, 2, 3, 4]

4.3# NaN

Number.isNaN和全局空间中的isNaN的辨别在于不存在隐式转换:

isNaN('number') // true
Number.isNaN('number') // false

Object.is除了辨别正负零这个异常小众的边境,这个接口相对===更大的意义是推断NaN:

Object.is(NaN, NaN); // true
NaN === NaN; // false
Object.is(+0, -0); // false
+0 === -0; // true

一样的,arr.includes(xx)arr.lastIndexOf(xx) > -1好的处所也包含关于NaN的处置惩罚:

[1, 2, NaN].includes(NaN); // true

4.4# Number

isFiniteNumber.isFinite的辨别也是后者不存在隐式转换:

isFinite("42");        // true
Number.isFinite("42"); // false

Number.isInteger示意一个数是不是是小数,和x === Math.floor(x)的辨别在于对Infinity的处置惩罚

Number.isInteger(Infinity);        // false
Infinity === Math.floor(Infinity); // true

Number.isSafeInteger示意传入的数值有无精度丧失,它比较的是数字是不是在Number.MIN_SAFE_INTEGERNumber.MAX_SAFE_INTEGER之间:

Number.isSafeInteger(Math.pow(2, 53) - 1); // true
Number.isSafeInteger(Math.pow(2, 53)); // false

我曾整理过Number的数轴(),也写过JavaScript中的一些数字内存模子的demo,其中有一部份值没有直接的量来示意,但现在有了。

从负无限往正无限来看,是如许的:

  • Number.NEGATIVE_INFINITY 负无限

  • -Number.MAX_VALUE 能示意的最小数字,更小被视为负无限,即是-(2^53-1)*(2^971)

  • Number.MIN_SAFE_INTEGER(新) 没有精度偏差的最小数,即是-(2^53-1)

  • 0 正负零

  • Number.EPSILON(新) IEEE 754范例下的精度位许可的最小差别值,即是2^-52

  • Number.MIN_VALUE 能示意的最小正整数,这是一个IEEE 754范例下的反规格化值,即是2^-1074

  • Number.MAX_SAFE_INTEGER(新) 没有精度偏差的最大数,,即是2^53-1

  • Number.MAX_VALUE 能示意的最大数字,更大被视为正无限,即是(2^53-1)*(2^971)

  • Number.INFINITY 正无限

比较使人殽杂的是Number.EPSILONNumber.MIN_VALUE,前者为精度位许可的最小差别值,斟酌的是浮点数的精度位;而后者斟酌的是应用到浮点数的一切位置可以示意的最小正数值。

5# 怪奇毛病展

本节收集了一些奇新鲜怪的毛病提醒,一般写出的代码不会致使它们,没有兴致可以略过。

5.1# 新接口的迭代器参数

Array.from(1, 2, 3) // Array.of(1,2,3)的误挪用
// 2 is not a function

Array.fromPromise.all接口及鸠合类组织器的参数,可以放入支撑迭代器的内容,而不局限于数组(node 0.12+兼容)。这里实在尝试去挪用了参数的迭代器Symbol.iterator

5.2# 新鸠合类容器的组织器

Array(); // []
Set(); // Uncaught TypeError: Constructor Set requires 'new'

鸠合类容器Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array Set不可以经由过程非new体式格局来组织。

5.3# Tagged Template

var x = 30
`abcdefg`
// Uncaught TypeError: 30 is not a function

模版语法多是ES6最为明显的语法,但它的扩大情势Tagged Template在极度场景可以形成一个新鲜的报错,算是对不写分号党形成的又一个暴击

6# 欺侮新来的

本篇章集结一些被滥用的特征。

6.1

解构特征很棒,它可以在promise如许的单参数链式挪用场景或是正则婚配场景中慷慨光泽,更加典范的是python作风的[y, x] = [x, y]

但假如一个人铁了心要猖獗解构,新来保护这份代码的人就要默默流下痛楚的眼泪了:

// 新人:是什么阻挠了你用 a2 = [o1[a], o1[b], o1[c]] ……
var o1 = { a: 1, b: 2, c: 3 },
    a2 = [];

( { a: a2[0], b: a2[1], c: a2[2] } = o1 );
// 白叟:看得爽吗
var { a: { b: [ c, d ], e: { f } }, g } = obj;
// 主管:写到一半这个顺序猿已被打死了
var x = 200, y = 300, z = 100;
var o1 = { x: { y: 42 }, z: { y: z } };

( { y: x = { y: y } } = o1 );
( { z: y = { y: z } } = o1 );
( { x: z = { y: x } } = o1 );

一个可以尝试的坚持代码可读性的要领,是只管保证解构的条理低。

6.2

新对象字面量也很不错,新的rest操作符也很有用,然则假如你们把它们混在一同……下面进一段代码赏析():

export const sharePostStatus = createReducer( {}, {
    [ PUBLICIZE_SHARE ]: ( state, { siteId, postId } ) => ( { ...state, [ siteId ]: { ...state[ siteId ], [ postId ]: {
        requesting: true,
    } } } ),
    [ PUBLICIZE_SHARE_SUCCESS ]: ( state, { siteId, postId } ) => ( { ...state, [ siteId ]: { ...state[ siteId ], [ postId ]: {
        requesting: false,
        success: true,
    } } } ),
    [ PUBLICIZE_SHARE_FAILURE ]: ( state, { siteId, postId, error } ) => ( { ...state, [ siteId ]: { ...state[ siteId ], [ postId ]: {
        requesting: false,
        success: false,
        error,
    } } } ),
    [ PUBLICIZE_SHARE_DISMISS ]: ( state, { siteId, postId } ) => ( { ...state, [ siteId ]: {
        ...state[ siteId ], [ postId ]: undefined
    } } ),
} );

只管的坚持代码的可读性,一行只用不凌驾2个ES6特征或许是一个可操作的计划。

    原文作者:Humphry
    原文地址: https://segmentfault.com/a/1190000008203048
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞