2018-01-05 arrow 函数(ES6)、测量一个JavaScript代码块性能的技巧、ES6中参数处理、提升、检查一个对象是否有属性、模板字符串、将节点列表转换为数组

1 给回调函数传递参数

在默认情况下,你无法将参数传给回调函数,如下:

function callback() {
console.log('Hi human');
}
document.getElementById('someelem').addEventListener('click', callback);

你可以采取JavaScript闭包的优点来给回调函数传参,案例如下:

function callback(a, b) {
return function() {
console.log('sum = ', (a+b));
}
}
var x = 1, y = 2;
document.getElementById('someelem').addEventListener('click', callback(x, y));

什么是闭包呢?闭包是指一个针对独立的(自由)变量的函数。换句话说,闭包中定义的函数会记住它被创建的环境。了解更多请参阅MDN所以这种方式当被调用的时候,参数X/Y存在于回调函数的作用域内。
另一种方法是使用绑定方法。例如:

var alertText = function(text) {
alert(text);
};
document.getElementById('someelem').addEventListener('click', alertText.bind(this, 'hello'));

两种方法在性能上有一些略微区别,详情参阅jsperf

2 使用更简单的类似indexOf的包含判断方式

原生的JavaScript没有contains方法。对检查字符串或字符串数组项中是否存在某值,你可以这样做:

var someText = 'JavaScript rules';
if (someText.indexOf('JavaScript') !== -1) {
}
// 或者
if (someText.indexOf('JavaScript') >= 0) {
}

但是我们再看看这些ExpressJs代码片段。

// examples/mvc/lib/boot.js
for (var key in obj) {
// "reserved" exports
if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue;
 
// examples/lib/utils.js
exports.normalizeType = function(type){
return ~type.indexOf('/')
? acceptParams(type)
: { value: mime.lookup(type), params: {} };
};
 
// examples/web-service/index.js
// key is invalid
if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key'));

问题是~位运算符。"运算符执行操作这样的二进制表达式,但他们返回标准的JavaScript的数值."
他们将-1转换为0,而0在JavaScript中又是false。

var someText = 'text';
!!~someText.indexOf('tex'); // someText 包含 "tex" - true
!~someText.indexOf('tex'); // someText 不包含 "tex" - false
~someText.indexOf('asd'); // someText 不包含 "asd" - false
~someText.indexOf('ext'); // someText 包含 "ext" - true
String.prototype.includes()

在ES6(ES 2015)中介绍了includes()方法可以用来确定是否一个字符串包含另一个字符串:

'something'.includes('thing'); // true 

在ECMAScript 2016 (ES7)中,甚至数组都可以这样操作,如indexOf:
!!~[1, 2, 3].indexOf(1); // true
[1, 2, 3].includes(1); // true
不幸的是,这只是在Chrome,Firefox,Safari 9或以上的浏览器中被支持。

3 arrow 函数(ES6)

介绍下ES6里的新功能,arrow函数可能会是个很方便的工具,用更少行数写更多代码。他的名字来源于他的语法,=>和小箭头->比就像一个“胖胖的箭头”。可能有些人知道,这种函数类型和其他静态语言如lambda表达式的匿名函数。它被称为匿名,因为这些箭头函数没有一个描述性的函数名。

那么这样有什么好处呢?

语法:更少的LOC,不用一次次的键入函数关键字。
语义:从上下文中捕捉关键字this。
简单语法案例:
看看下面的两段代码片段,他们做的是一样的工作。你能很快的理解arrow函数的功能。

// arrow函数的日常语法
param => expression
// 可能也会写在括号中
// 括号是多参数要求
(param1 [, param2]) => expression
 
// 使用日常函数
var arr = [5,3,2,9,1];
var arrFunc = arr.map(function(x) {
return x * x;
});
console.log(arr)
 
// 使用arrow函数
var arr = [5,3,2,9,1];
var arrFunc = arr.map((x) => x*x);
console.log(arr)

正如你所看到的,这个例子中的arrow函数可以节省你输入括号内参数和返回关键字的时间。建议把圆括号内的参数输入,如 (x,y) => x+y 。在不同的使用情况下,它只是用来应对遗忘的一种方式。但是上面的代码也会这样执行:x => x*x.目前看来,这些仅仅是导致更少的LOC和更好的可读性的句法改进。

this 绑定

还有一个更好的理由使用arrow函数。那就是在会出现this问题的背景下。使用arrow函数,你就不用担心.bind(this)和 that=this 了。因为arrow函数会从上下文中找到this。
看下面的例子:

// 全局定义this.i
this.i = 100;
var counterA = new CounterA();
var counterB = new CounterB();
var counterC = new CounterC();
var counterD = new CounterD();
 
// 不好的示例
function CounterA() {
// CounterA's `this` 实例 (!! 忽略这里)
this.i = 0;
setInterval(function () {
// `this` 指全局对象,而不是 CounterA's `this`
// 因此,开始计数与100,而不是0 (本地的 this.i)
this.i++;
document.getElementById("counterA").innerHTML = this.i;
}, 500);
}
// 手动绑定 that = this
function CounterB() {
this.i = 0;
var that = this;
setInterval(function() {
that.i++;
document.getElementById("counterB").innerHTML = that.i;
}, 500);
}
// 使用 .bind(this)
function CounterC() {
this.i = 0;
setInterval(function() {
this.i++;
document.getElementById("counterC").innerHTML = this.i;
}.bind(this), 500);
}
// 使用 arrow函数
function CounterD() {
this.i = 0;
setInterval(() => {
this.i++;
document.getElementById("counterD").innerHTML = this.i;
}, 500);
}
4 测量一个JavaScript代码块性能的技巧

快速测量一个JavaScript块的性能,我们可以使用控制台的功能像console.time(label)和console.timeEnd(label)

console.time("Array initialize");
var arr = new Array(100),
len = arr.length,
i;
for (i = 0; i < len; i++) {
arr[i] = new Object();
};
console.timeEnd("Array initialize"); // 输出: Array initialize: 0.711ms
5 ES6中参数处理

在许多编程语言中,函数的参数是默认的,而开发人员必须显式定义一个参数是可选的。在JavaScript中的每个参数是可选的,但我们可以这一行为而不让一个函数利用ES6的默认值作为参数。

const _err = function( message ){
throw new Error( message );
}
const getSum = (a = _err('a is not defined'), b = _err('b is not defined')) => a + b
getSum( 10 ) // throws Error, b is not defined
getSum( undefined, 10 ) // throws Error, a is not defined

_err是立即抛出一个错误的函数。如果没有一个参数作为值,默认值是会被使用,_err将被调用,将抛出错误。你可以在Mozilla开发者网络看到的更多默认参数的例子。

6 提升

理解提升将帮助你组织你的function。只需要记住,变量声明和定义函数会被提升到顶部。变量的定义是不会的,即使你在同一行中声明和定义一个变量。此外,变量声明让系统知道变量存在,而定义是将其赋值给它。

function doTheThing() {
// 错误: notDeclared is not defined
console.log(notDeclared);
// 输出: undefined
console.log(definedLater);
var definedLater;
definedLater = 'I am defined!'
// 输出: 'I am defined!'
console.log(definedLater)
// Outputs: undefined
console.log(definedSimulateneously);
var definedSimulateneously = 'I am defined!'
// 输出: 'I am defined!'
console.log(definedSimulateneously)
// 输出: 'I did it!'
doSomethingElse();
function doSomethingElse(){
console.log('I did it!');
}
// 错误: undefined is not a function
functionVar();
var functionVar = function(){
console.log('I did it!');
}
}

为了使事情更容易阅读,在函数作用域内提升变量的声明将会让你明确该变量的声明是来自哪个作用域。在你需要使用变量之前定义它们。在作用域底部定义函数,确保代码清晰规范。

7 检查一个对象是否有属性

当你要检查一个对象是否存在某个属性时,你可能会这样做 :

name: '@tips_js'
};
if (myObject.name) { ... }

这是可以的,但你必须知道这个还有两原生的方式,in operator 和 object.hasownproperty,每个对象是对象,既可用方法。每个object都继承自Object,这两个方法都可用。

两个方法的一些不同点:

var myObject = {
name: '@tips_js'
};
myObject.hasOwnProperty('name'); // true
'name' in myObject; // true
myObject.hasOwnProperty('valueOf'); // false, valueOf 是从原型链继承的
'valueOf' in myObject; // true

他们之间的不同在于检查的性质,换句话说,当该对象本身有查找的属性时hasOwnProperty返回yrue,然而,in operator不区分属性创建的对象和属性继承的原型链。

这里有另外一个例子:

var myFunc = function() {
this.name = '@tips_js';
};
myFunc.prototype.age = '10 days';
var user = new myFunc();
user.hasOwnProperty('name'); // true
user.hasOwnProperty('age'); // false, 因为age是原型链上的
8 模板字符串

截至ES6,JS已经有模板字符串作为替代经典的结束引用的字符串。
案例:普通字符串

var firstName = 'Jake';
var lastName = 'Rawr';
console.log('My name is ' + firstName + ' ' + lastName);
// My name is Jake Rawr 
模板字符串:
var firstName = 'Jake';
var lastName = 'Rawr';
console.log(`My name is ${firstName} ${lastName}`);
// My name is Jake Rawr

在模板字符串中${}中,你可以写不用写/n或者简单逻辑来实现多行字符串。

您还可以使用函数来修改模板字符串的输出,它们被称为模板字符串的标记。你可能还想读到更多的理解模板字符串相关信息。

9 将节点列表转换为数组

querySelectorAll 方法返回一个和数组类似的节点列表对象。这些数据结构类似数组,因为经常以数组形式出现,但是又不能用数组的方法,比如map和foreach。这里是一个快速、安全、可重用的方式将一个节点列表到一个DOM元素数组:

const nodelist = document.querySelectorAll('div');
const nodelistToArray = Array.apply(null, nodelist);
//later on ..
nodelistToArray.forEach(...);
nodelistToArray.map(...);
nodelistToArray.slice(...);
//etc...

apply方法是将一系列数组格式的参数传递给一个给定this的函数。MDN指出,apply将会调用类似数组的对象,而这正是querySelectorAll所返回的。因为我们不需要在函数的上下文中指定this,所以我们传入null或0。返回的结果是一组能使用数组方法的DOM元素数组。

如果你使用的是es2015可以利用...(spread operator)

const nodelist = [...document.querySelectorAll('div')]; // 返回的是个真实的数组
//later on ..
nodelist.forEach(...);
nodelist.map(...);
nodelist.slice(...);
//etc...
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,212评论 0 4
  • 三,字符串扩展 3.1 Unicode表示法 ES6 做出了改进,只要将码点放入大括号,就能正确解读该字符。有了这...
    eastbaby阅读 1,514评论 0 8
  • [TOC] 参考阮一峰的ECMAScript 6 入门参考深入浅出ES6 let和const let和const都...
    郭子web阅读 1,771评论 0 1
  • 可能总有人愿意对别人评头论足指指点点,可是人干不干净,难道真的只是靠会说话的一张嘴吗?你要是这样觉得,我只想和你说...
    GDragon信仰阅读 414评论 0 0
  • 我的生命有什么可能?古典 我们的生命到底有什么可能?多少种追寻方向?这些追寻之间到底谁优谁劣? 追寻改变世界的人,...
    一枚冰儿阅读 234评论 2 0