ES6的6个小特性(二)

继上一篇的 《Six Tiny But Awesome ES6 Features》,这次我再分享6个可以减少代码和最大化效率的方法。

1.Object Shorthand

新的对象声明方法允许我们可以不声明对象的 key :

var x = 12;
var y = yes;
var z = {one:'1',two:'2'};

// The old way
var obj = {
    x:x,
    y:y,
    z:z
}
// The new way
var obj = {x,y,z};

2.Method Properties

避免 function 关键字声明函数:

var davidwalsh = {
    makeItHappen(param){
        // do stuff
    }
}

必须承认去除掉 function 关键字确实使代码简洁、更好维护。

3.Blocks vs Immediately Executed Functions

下面创建立即执行方法的模式有点难看:

(function(){
    // do stuff
})();

通过ES6我们可以通过 {} 和 let 来创建块级作用于,完成立即执行函数的作用:

{
    let j = 12;
    let divs = document.querySelectorAll('div');

    // do stuff
}

j; // ReferenceError: j is not defined...

如果在 Block 内部声明函数,它将会被外部访问到。但你如果使用 let 关键字声明函数自变量,将不使用括号的情况下实现 IEF 的功能。

4. for loops and let

因为在JS里面会存在变量提升,我们经常会在作用域前面声明一些”无用”的迭代变量,例如(for var x = …)。ES6 使用 let 解决了此恼人的问题:

for(let x = 0; x < len; i++){
    //do stuff
}

x; // ReferenceError: x is not defined

不久以后 let 会被应用的更多。

5.get and set for Classes

class Cart{
    constructor(total){
        this._total = total;
    }
    get total(){return this._total;}
    set total(v){this._total = Number(v);}
}

var cart = new Cart(100);

cart.total // 100

能为属性设置 get、set 是这部分最棒的。不需要使用函数来进行特殊的设定–当执行 obj.prop = {value} 时,一切都会自动执行。

6.startsWith,endsWith and includes

"MooTools".startsWith("Moo"); // true;
"MooTools".startsWith("moo"); // false;

"MooTools".endsWith("Tools"); // true;

"MooTools".includes("oo"); // true;

注:includes 方法兼容性还是很多,曾有一个线上bug,就是因为不支持此方法导致的。

原文:https://davidwalsh.name/es6-features-ii/amp

ES6的6个小特性

JS社区的每个人都喜欢新的API、语法以及一些简单、明了更高效的完成重要任务的新特性。过去一年ES6带来了十足的进步,下面是6个我最喜欢的JS新增特性。

1.Object[key]

有时候不能在对象变量声明时设置所有的key/value,所以得再声明之后添加key/value。

let myKey = 'key3';
let obj = {
    key1: 'One',
    key2: 'Two'
};
obj[myKey] = 'Three';

往好的说这有点不方便,往坏的说这种方式令人疑惑而且有点丑陋。ES6提供给开发者一种更优雅的方式:

let myKey = 'variableKey';
let obj = {
    key1: 'One',
    key2: 'Two',
    [myKey]: 'Three' /* yay! */
};

开发者可以使用[]包裹变量从而使用一条语句完成所有的功能。

2.Arrow Functions

你不需要跟上ES6的所有改变,箭头函数已经是许多讨论的话题并且也给JS开发者带来了一些困惑。即使我可以写很多博文来说箭头函数的特点,但是我想指出箭头函数是如何提供一个为简单函数压缩代码的方法。

// Adds a 10% tax to total
let calculateTotal = total => total * 1.1;
calculateTotal(10) // 11

// Cancel an event -- another tiny task
let brickEvent = e => e.preventDefault();
document.querySelector('div').addEventListener('click', brickEvent);

functionsreturn关键词,有时甚至不需要添加(),箭头函数为写函数提供了一种简短的代码书写方式。

find/findIndex

JS为开发者提供了Array.prototype.indexOf方法来获取数组中的指定元素下标,但是indexOf并没有提供一个根据判断条件来获取指定元素的方法,findfindIndex两个方法提供了取出第一个满足计算条件的元素和下标。

let age = [12,19,6,4];

let firstAdult = ages.find(age => age >= 18); // 19
let firstAdultIndex = ages.findIndex(age => age >= 19); // 1

…扩展修饰符

扩展修饰符表示数组和可迭代对象在调用的时候应该拆分成单个参数:

// Pass to function that expects separate multiple arguments
// Much like Function.prototype.apply() does
let numbers = [9, 4, 7, 1];
Math.min(...numbers); // 1

// Convert NodeList to Array
let divsArray = [...document.querySelectorAll('div')];

// Convert Arguments to Array
let argsArray = [...arguments];

这个特定的另一个红利可以把可迭代对象(NodeListarguments)变成真的数组,以前我们经常使用Array.from或其他方法实现的。

Template Literals

JS里多行字符起初通过+\来完成的,但是都很难维护。许多开发者甚至一些框架使用<script>标签来容纳模板,然后使用DOM方法的outerHTML来获取HTML字符。

ES6提供了Template Literals使用反引号来容易的创建多行字符串:

// Multiline String
let myString = `Hello

I'm a new line`;

//Basic interpolations
let obj = {x:1,y:2};

console.log(`Your total is: ${obj.x + obj.y}`); // Your total is 3

Default Argument Values

为函数参数提供默认值在服务端语言已经提供(python、php),现在JS也有此能力:

//Basic usage

function great( name = 'Anon' ){
    console.log(`Hello ${name}`);
}

great(); // Hello Anon!

//You can have a function too!

function greet( name = 'Anon',callback = function(){} ){
    console.log(`Hello ${name}!`);
    // No more "callback && callback()" (no conditional)
    callback();
}

// Only set a default for one parameter
function greet(name, callback = function(){}) {}

以上列出的6个特性就是ES6提供给开发者,当然还有许多特性。

评论里提供的:

1 .

const isRequired = () => {throw new Error('param is required');};

const hello = (name = isRequired()) => { console.log(`hello ${name}`) };

2 .

const is = {
    get required(){
        throw new Error('Required argument')
    }
}

import {is} from 'utils'

const foo(name = is.required) => Array.from(name)

原文:https://davidwalsh.name/es6-features

静默一切console.log和alert

开发的时候可以打开静默,上线可以静默:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 备份以备后用
var __log = console.log;
var __alert = window.alert;
//静默 console.log alert
function mute() {
console.log = function(){};
window.alert = function(){};
}
//打开静默
function unmute() {
console.log = __log;
window.alert = __alert;
}

移动端调试

PC端调试两大利器:Firefox的Firebug和Chrome的devtools,有这两个东西PC端的调试基本没有什么问题。但是随着前端的移动开发需求越来越多,移动端的调试也渐渐被大家重视。可是移动端没有像PC那样方便,但是解决方法还是有的。

我大体分三种移动端调试需求:

1.浏览器的页面调试(mobile web page)

1.1 iOS的Safari

手机端:设置 → Safari → 高级 → Web 检查器 → 开。

mac端:Safari → 偏好设置 → 高级 → 在菜单栏中显示“开发”菜单。

ios-debugger
ios-debugger
ios-debugger

最后数据线连接电脑和手机或者打开模拟器,Mac里的Safari(如下图)都可以看到连接的设备,点到对应的页面就可以调试:

ios-debugger

1.2 iOS设备模拟器

ios-debugger
ios-debugger
ios-debugger

注:iOS模拟设备里的Safari打开调试模式和上面一样。

以前搞Swift开发时,遇到模拟器无法弹起键盘:

ios-debugger

1.2 Android

直接复制官网的一些调试条件:

  • Chrome 32 or later installed on your development machine.
  • A USB cable to connect your Android device.
  • For browser debugging: Android 4.0+ and Chrome for Android.
  • For app debugging: Android 4.4+ and a WebView configured for debugging.

以上可知不能调试低版本Android或者安装的其它浏览器,解决版本和微信里面相同。

2.微信内嵌页面

调试微信内嵌页和低版本安卓使用weinre

npm -g install weinre

控制台使用下面方法启动:

weinre –boundHost -all-

具体调试方法看官网。

参考:

  1. https://www.smashingmagazine.com/2014/09/testing-mobile-emulators-simulators-remote-debugging/
  2. https://github.com/nupthale/weinre
  3. https://github.com/jieyou/remote_inspect_web_on_real_device
  4. https://segmentfault.com/a/1190000000459296

throttle与debounce的区别

以前写过一篇文章《“节流函数”提高性能》,里面讲到用函数“节流”来减少执行次数(不影响体验的情况下),其实实现的代码并没有问题,但是第二个方法的函数名有问题。前几天看到一篇文章,我的公众号里也分享了《一次发现underscore源码bug的经历以及对学术界拿来主义的思考》具体文章详见,微信公众号:img

文中讲了大家对throttle和debounce存在误解,同时提到了《高程3》中实现节流方法存在一些问题,为了更好的理解这两个概念,搜了很多相关文章,详见文章底部。

throttle与debounce是两个类似的概念,目的都是随着时间的推移控制执行函数的次数,但是有些细微的差别。

当我们为DOM事件关联方法时,若我们有一个debounced和throttled函数将会很方便,为何?因为这样我们可以在事件和执行函数之间添加一层控制,注意我们并没有去控制DOM事件触发的次数。

例如,我们谈一下scroll事件,看下面的例子:

See the Pen Scroll events counter by ghostcode (@ghostcode) on CodePen.

当你在触控板或者鼠标滚动时,每次最少会达到30次,在手机上更多。可是你的滚动事件处理函数对这个频率是否应付的过来?

在2011年,Twitter网站曾爆出一个问题:当你在主页往下滚动时,页面会变得缓慢以致没有响应。John Resig发表了一篇文章《 a blog post about the problem》指出直接在scroll事件上面绑定高消耗的事件是一个多么愚蠢的想法。

在那个时候John建议使用一个独立于scroll事件且每250ms执行的轮询方法。这样的话处理方法就不会耦合于事件。通过这个简单的技术,我们可以提高用户体验。

现在有一些更先进的事件处理方法,让我来给你介绍:Debounce,Throttle和requestAnimationFrame,同时会介绍一些适用的场景。

Debounce

Debounce技术使我们可以将一个连续的调用归为一个。

img

想象你在电梯的场景,当电梯门开始要关闭的时候,突然一个人进来,此时电梯并不会关闭并且也不会执行改变楼层的方法,如果还有人进来同样的事情会发生:电梯延迟执行它的方法(改变楼层),优化了它的资源。

自己尝试一下,在按钮上点击或者移动鼠标:

See the Pen Debounce. Trailing by ghostcode (@ghostcode) on CodePen.

你可以看到快速连续的事件是如何通过一个debounce事件来表示的。

Leading edge (or “immediate”)

你可以发现事件结束的时候,debounce的事件并没有立即执行而是等待了一些时间才触发。为何不立即触发,就像开始没有使用debounce事件处理?直到在连续执行的事件中有一个暂停,才会再次触发。

你可以通过一个leading的参数做到:

img

在underscore.js中,这个参数叫immediate。

自己尝试一下:

See the Pen Debounce. Leading by ghostcode (@ghostcode) on CodePen.

Debounce Implementations

2009年在John Hann的文章中第一次看到debounce的实现方法。

在那之后不久,Ben Alman写了一个jQuery插件(现在不在维护),一年以后Jeremy Ashkenas把此方法添加到underscore.js中,不久又被添加到lodash中。

See the Pen debounce-click by ghostcode (@ghostcode) on CodePen.

这三种实现方法内部不同,但是接口几乎一致。

有段时间underscore采用了Lodash的实现方法,但是在我发现了一个bug之后,自此两个库的实现开始分道扬镳。

Lodash在.debounce和.throttle中添加了许多特性。immediate标示替代了leading和trailing。你可以二选一或者都选,默认情况下,只有trailing是开启的。

Debounce Examples

当改变浏览器窗口时,resize事件会触发多次。

See the Pen Debounce Resize Event Example by ghostcode (@ghostcode) on CodePen.

如你所见,我们使用了trailing参数,因为我们只对用户停止改变浏览器大小时最后一次事件感兴趣。

AutoComplete中的Ajax请求使用的keypress

当用户仍旧在输入的时候,为何每隔50ms发送Ajax请求? _.debounce 可以帮助我们避免额外的工作,只在用户停止输入的时候发送请求。

See the Pen Debouncing keystrokes Example by ghostcode (@ghostcode) on CodePen.

另一个使用场景是在进行input校验的时候,“你的密码太短”等类似的信息。

如何使用debounce和throttle以及常见的陷阱?

可以自己实现这两个方法或者随便复制别人blog中的实现方法,我的建议是直接使用underscore和lodash中的方法。如果你只需要这两个方法,可以定制输出lodash方法:

1
2
npm i -g lodash-cli
lodash-cli include=debounce,throttle

一个常见的陷阱:

1
2
3
4
5
6
7
// WRONG
$(window).on('scroll', function() {
_.debounce(doSomething, 300);
});
// RIGHT
$(window).on('scroll', _.debounce(doSomething, 200));

debounce方法赋值给一个变量之后允许我们调用一个私有方法:debounced_version.cancel()

1
2
3
4
5
var debounced_version = _.debounce(doSomething, 200);
$(window).on('scroll', debounced_version);
// If you need it
debounced_version.cancel();

Throttle

使用 _.throttle ,我们不允许方法在每Xms间执行超过一次。

和debounce的主要区别是throttle保证方法每Xms有规律的执行。

Throttling Examples

一个相当常见的例子,用户在你无限滚动的页面上向下拖动,你需要判断现在距离页面底部多少。如果用户快接近底部时,我们应该发送请求来加载更多内容到页面。

在此 _.debounce 没有用,因为它只会在用户停止滚动时触发,但我们需要用户快到达底部时去请求。通过 _.throttle 我们可以不间断的监测距离底部多远。

See the Pen Infinite scrolling throttled by ghostcode (@ghostcode) on CodePen.

requestAnimationFrame (rAF)

requestAnimationFrame是另一个频率限制的方法。

它可以通过 _.throttle(dosomething, 16)实现,但为了更加精准浏览器提供了内置API。

我们可以使用rAF API作为throttle方法的替代,考虑一下利弊:

利:

  • 目标60fps(16ms每贞),但是内部使用最优的时间间隔来渲染
  • 使用简单并且是标准API,以后不会变动,不需要维护

弊:

  • rAF的开始或者取消需要我们自己处理,不像.debounce和.throttle内部实现
  • 浏览器Tag没有激活,它就不会执行
  • 即使多数现代浏览器支持,但是IE9,Opera Mini以及老版本Android依旧不支持。A polyfill到现在依旧需要
  • rAF在node.js中不支持

根据经验,我建议在JS执行”painting”或”animating”中直接操作属性和重新计算元素位置时使用rAF。

发送Ajax请求或者是否添加/删除class(触发一个CSS动画)时,我会考虑debounce和throttle,此时你可以降低执行频率(200ms而不是16ms)。

rAF的例子

Paul Lewis的文章激发下,我只在scroll事件中提供例子。

我一步步的调throttle到16ms,希望给一个类似的体验,但是rAF在复杂场景下或许会提供更好的结果。

See the Pen Scroll comparison requestAnimationFrame vs throttle by ghostcode (@ghostcode) on CodePen.

一个更好的例子我是在headroom.js中看到的,这里通过一个对象封装,进行了逻辑解藕

总结:
使用debounce,throttle和requestAnimationFrame优化你的事件处理函数。每一个方法有一些细微的差别,三个都很有用而且互相弥补。

  • debounce:把突然涌进的事件(键盘事件)归位一个
  • throttle:保证持续执行方法分隔为每Xms执行一次。就像每200ms监测滚动位置来触发css动画。
  • requestAnimationFrame:throttle的替代方案,当你的方法需要重新计算和渲染元素同时你需要更平滑的变动或动画。注意:IE9- 不支持。
  1. https://blog.coding.net/blog/the-difference-between-throttle-and-debounce-in-underscorejs
  2. https://css-tricks.com/the-difference-between-throttling-and-debouncing/
  3. http://stackoverflow.com/questions/25991367/difference-between-throttling-and-debouncing-a-function
  4. http://demo.nimius.net/debounce_throttle/

Promise reject and throw Error

以Promise为基础的方法应该reject的Promise,而不该抛出异常。

原因是如果这样做将需要两种错误处理方式:

1
2
3
4
5
6
7
8
9
function asyncFunc() {
return doSomethingAsync() // (A)
.then(result => {
···
})
.catch(error => { // (B)
···
});
}

若在A行执行的异步方法抛出异常,但是B行并不会做出反应。

Promise方法中处理异常

如果异常在then和catch的回调函数中抛出并不会有问题,因为这两个方法会转变异常为reject。
可是若在异步方法中添加了同步方法:

1
2
3
4
5
6
7
function asyncFunc() {
doSomethingSync(); // (A)
return doSomethingAsync()
.then(result => {
···
});
}

如果在A行抛出异常,整个方法都会抛出一个异常,有两个方法可以处理此问题:

1.返回rejected Promise

可以捕获异常,然后作为rejected Promises返回:

1
2
3
4
5
6
7
8
9
10
11
function asyncFunc() {
try {
doSomethingSync();
return doSomethingAsync()
.then(result => {
···
});
} catch (err) {
return Promise.reject(err);
}
}

2.在回调中执行同步代码

以Promise.resolve()开启一个then的链式调用然后在回调中执行同步代码:

1
2
3
4
5
6
7
8
9
10
function asyncFunc() {
return Promise.resolve()
.then(() => {
doSomethingSync();
return doSomethingAsync();
})
.then(result => {
···
});
}

译:http://www.2ality.com/2016/03/promise-rejections-vs-exceptions.html

读《你的知识需要管理》感

上周间隔的读了《你的知识需要管理》这本书,作者田志刚,这里是他的一些文章。现在这个社会最重要的是人,人最重要的是有自己的能力,能力体现在对知识的最佳利用。

现在不在是文革时期,稍微有点知识(文化)就可以沾沾自喜,现在信息不对称性在慢慢降低,靠一条信息就可以致富的时代一去不返了。现在我们老家的人,通过手机上网就很方便的获取信息和知识。所以在现在如何更好的利用信息,更好的管理自己的知识显得尤为重要。

在现在信息泛滥的时代,如果没有好的信息和知识管理方案,就会迷失在这个信息流中。不要用个人有限的生命去获取无限的信息和知识。

本书提出了五步打造个人知识力

  1. 学习:在知识社会,我们每个人只有持续不断地高效学习。
  2. 保存:普通人的大脑容量有限,充分利用工具保存知识。
  3. 共享:让别人知道你知道,提高个人的影响力。
  4. 使用:将知识应用生活/工作中。
  5. 创新:站巨人的肩膀,看的更远。

我前几点都没有做好,更不要谈创新。有些人可能会疑问学习都不会,学校白呆了。有句话说的很好:听过许多道理,依旧过不好这一生。虽然上了好多年学,可是学习这种技能并不是每个人都能习的。书中提到一个典型的例子:数据/信息和知识的区别?

首先给一个数字:37.5,然后赋予这个数字一些信息:37.5度,最后这个根据医学知识判断37.5度是否超过人体正常温度。

这个演变的过程就是:数据->信息->知识

刚开始37.5这个数字是没有任何意义的,慢慢的有了一些意义表示提问,然后根据科学知识给出决策和行动。

现在大家应该都玩知乎/微博,每天刷的不亦乐乎,当时是不是感觉自己懂了很多?充实很多?事后想想多数都是没有用的信息,真正有用的没用多少。这就是把自己淹没在信息流中。

在很好的区分这三者之后,就会面临一系列问题:

为何学习?

这个每个人有不同的动力,更好的工作,更好的生活等

如何学习知识?

大家肯定会立马想到:认真 努力 持之以恒等这些积极向上的词语,这些都没有错,但是可能忽略了两个问题:方向 专注。方向很重要,如果南辕北辙,学了又有什么用,在一个错误的方向使劲还不如原地踏步。好,你说方向是吧,我选择了很多方法,今天学一下这个明天看看这个。那这会造成什么呢?知识的深度不够,若不够深知识就会变成常识,常识怎么会提高个人的竞争力呢?

如何保存知识?

想必大家都有这样的经历,在网上看到一个免费的PDF文件,感觉对自己有点用,然后立马下在下来,然后随便翻翻就丢在那里。或者微博或知乎看到一个很不错的东西就立马转发或者关注,再或者看到一个很好的博客就添加到浏览器收藏夹再也没有打开过。或许你在用的时候感觉曾经看到过相关的东西,也确实收藏了,但保存到哪里却不记得了。

这都是没有做好知识的保存工作,第一种就是无限制的保存,硬盘全部占满也无济于事,第二种没有分门别类。

大脑容量有限,我们要善于借助工具,要深度或者回看的文章都用Pocket保存,浏览器书签分了好多类别(其实类别一多也不行,不易查找),日常工作用有道笔记,资料存储用百度云盘(里面该整理一下)。存的资料多其实有时候会给自己一种错觉:我知道的很多,会很多东西。其实不然,那些知识并没有融合到自己脑子里,看到这里翻翻自己以前存的资料(都有系统时间,看是多久之前的)。

如何共享知识?

共享知识其实就是让别人知道你知道,提高自己的影响力以及实现个人的价值。其实途径很多,看个人性格爱好,如果比较内向,可以选择博客,微信公众号;如果外向,善于交际,可以选择做演讲,做视频或者直播代码。最简单的就是在工作中,用自己的知识去解决自己和同事遇到的问题,慢慢提升自己的团队影响力。共享知识也是一种考验自己对相关知识理解是否到位?若对一个知识点不能用简单明了的语言表达出来,说明掌握的还是不够到位。

如何使用知识?

上面也说了应用到工作中,才能去除掉满腹经纶,无法施展的郁闷心情。

如何创新知识?

普通人,做到前面四点已经不容易。在这里就不谈了,如果有兴趣就看看上面说的那本书吧。

上面五点的关系如下图:

img

在附一张学习体系总结图:

img

DOM小结

DOM是针对HTML和XML文档的一个API(应用程序编程入口)。DOM描绘了一个层次化的节点树,允许开发人员添加/移除和修改页面某一部分。

注:从上面的引述中可以得知,DOM不仅存在于HTML中,DOM也不是JS的一部分。DOM只是提供了一些规范,具体JS就是根据这些规范去实现它提出的一些接口 。

这里有个问题, IE中所有的DOM对象都是以COM对象实现的,所以DOM对象和JS原生对象行为或活动特点不一致。这也导致JS对象引用IE DOM对象会内存泄漏。

JS中实现了一个Node类型,所有节点都集成它,所以会共享相同的属性和方法。

下面就用个思维导图总结下:

img