Effective JavaScript :第三章
1.函数调用、方法调用以及构造函数调用只是单个构造对象的三种不同的使用模式。
第一种函数调用模式:
function hello(username){
return ‘hello,’+ username;
}
hello(‘Keyser Soze’);
第二种模式是方法调用:
var obj = {
hello:function(){
return ‘hello,’+ this.username;
},
username : ‘Hans Gruber’;
};
obj.hello();
通过某个对象调用方法将查找该方法并将该对象作为该方法的接受者。一个非方法的函数调用会将全局对象作为接受者。
第三种模式是构造函数:就像方法和纯函数一样,构造函数也是由function运算符定义的。
function User(name,passwordHash){
this.name = name ;
this.passwordHash = passwordHash;
}
使用new操作符来调用User则视其为构造函数:
var u = new User(‘sfalken’,
‘0ef33ae791068ec64b502d6cb0191387’
);
u.name; //‘sfalken’
构造函数调用将一个全新的对象作为this变量的值,并隐式返回这个新对象作为调用结果。构造函数的主要职责是初始化该对象。
2.将函数作为参数或返回值的函数称为高阶函数:
假设有一个简单的转换字符串数组的操作,我们可以使用循环数组实现:
var names = [‘Fred’,‘Wilma’,‘Pebbles’];
var upper = [];
for(var i = 0, n = names.length; i < n ; i++){
upper[i] = names[i].toUpperCase();
}
upper;
使用数组便利的map方法,我们可以完全消除循环,仅仅使用一个局部函数就可以实现对元素的逐个转换。
var names = [‘Fred’,‘Wilma’,‘Pebbles’];
var upper = names.map(function(name){
return name.toUpperCase();
})
upper; //[‘FRED’,‘WILMA’,‘PEBBLES’];
需要引入高阶函数抽象的信号是出现重复或相似的代码。
3.使用call方法自定义接受者来调用方法
使用call方法的三种情况:
①通常情况下,函数或方法的接受者是由调用者的语法决定的。有时需要自定义接受者来调用函数,因为该函数可能并不是期望的接受者对象的属性。当然可以将方法作为一个新的属性添加到接受者对象中。但这种方式不仅让人感觉别扭而且相当危险。幸运的是,函数对象具有一个内置的方法call来自定义接收者,可以通过函数对象的call方法来调用其自身:
f.call(obj,arg1,arg2,arg3);
此行为与直接调用函数自身很类似:
f(arg1,arg2,arg3);
②当调用的方法已经被删除、修改或者覆盖时,call方法就派上用场了。使用hasOwnProperty方法的call方法使调用字典对象中的方法成为可能,即使hasOwnProperty方法并没有存储在该对象中。
var hasOwnProperty = { }.hasOwnProperty ;
dict.foo = 1 ;
delete dict.hasOwnProperty ;
hasOwnProperty.call(dict , ‘foo’);
hasOwnProperty.call(dict ,‘hasOwnProperty’);
③定义高阶函数时,call方法也特别实用。高阶函数的一个惯用方法是接收一个可选的参数作为调用该函数的接受者。例如,表示键值对列表的对象可能提供名为forEach的方法。
var table = {
entries : [];
addEntry : function(key , value){
this.entries.push({ key : key , value : value });
},
forEach : function(f, thisArg){
var entries = this.entries;
for(var i = 0 , n = entries.length ; i < n; i ++){
var entry = entries[i];
f.call(thisArg , entry.key , entry.value , i );
}
}
} ;
上述例子允许table对象的使用者将一个方法作为table.forEach的回调函数f,并为该方法提供一个合理的接收者。例如,可以方便的将一个table的内容复制到另一个中。
table1.forEach(table2.addEntry , table2) ;
4.使用apply方法通过不懂数量的参数调用函数
Apply方法需要一个参数数组,然后将数组的每一个元素作为调用的单独参数调用该函数。除了参数数组,apply方法指定第一个参数绑定到被调用函数的this变量。由于average函数没有引用this变量,因此,我们可以简单地传递null。
var scores = getAllScores();
average.apply(null,scores);
例如,如果scores有三个元素,那么以上代码的行为与average(scores[0],scores[1],scores[2])一致。
apply方法也可用于可变参数方法。例如,buffer对象包含一个可变参数的append方法,该方法添加元素到函数内部的state数组中。
var buffer = {
state : [ ] ;
append : function(){
for(var i = 0 , n = arguments.length ; i < n ; i ++){
this.state.push(arguments[i]);
}
}
};
5.使用argumen创建可变参数的函数
固定元数版本的averageOfArray函数是很容易实现的:
function averageOfArray(a){
for(var i = 0 , sum = 0, n = a.length ; i < n ; i ++){
sum += a[i] ;
}
return sum / n ;
}
averageOfArray([2,7,1,8,2,8,1,8]);
提供一个可变参数的函数,委托给固定元数版本来实现可变参数的函数:
function average(){
return averageOfArray(arguments) ;
}
这样一来,函数的使用者就无需借助apply方法,因为apply方法会降低可读性而且经常导致性能损失。
6.永远不要修改arguments对象
arguments对象可能看起来像一个数组,但它不总是表现的像数组。使用[].slice.call(arguments)将arguments对象复制到一个真正的数组中再进行修改。
7.使用变量保存arguments的引用
引用arguments时当心函数嵌套层级。绑定一个明确作用域的引用到arguments变量,从而可以在嵌套的函数中引用它。
function values(){
var i = 0 , n = arguments.length , a = arguments ;
return {
hasNext : function(){
return i < n ;
},
next : function(){
if(i >=n){
throw new Error(“end of iteration”) ;
}
return a[i++] ;
}
};
}
var it = values(1,4,1,4,2,1,3,5,6);
it.next();
it.next();
it.next();
8.使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,这些代码可以随后被执行,可是应该将代码表示为函数还是字符串?毫无疑问,应该将代码表示为函数,字符串表示代码不够灵活的一个重要原因是:它们不是闭包。
假设有一个简单的多次重复用户提供的动作的函数。
function repeat(n , action){
for(var i = 0 ; i < n ; i ++){
eval(action) ;
}
}
该函数在全局作用域会工作的很好,因为eval函数会将出现在字符串中的所有变量引用作为全局变量来解释。
9.避免使用非标准的栈检查属性
调用栈是指当前正在执行的活动函数链。在某些旧的宿主环境中,每个arguments对象都含有两个额外的属性:arguments.callee和arguments.caller。前者指向使用该arguments对象被调用的函数;后者指向调用该函数arguments对象的函数。许多环境仍然支持arguments.callee,但它除了允许匿名函数递归地引用其自身之外,就没有更多的用途了。
避免使用非标准的arguments.callee和arguments.caller属性,因为它们不具备良好的移植性。避免使用非标准的函数对象caller属性,因为在包含全栈信息方面,它是不可靠的。
Effective JavaScript :第三章的更多相关文章
- [Effective Java]第三章 对所有对象都通用的方法
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- Effective JavaScript :第二章
1.熟练掌握闭包 理解闭包要学会三个基本的事实: ①JavaScript允许你引用在当前函数以外定义的变量: 例如: function makeSandwich(){ var magicIngredi ...
- Effective JavaScript :第一章
第一章 一.严格模式与非严格模式 1.在程序中启用严格模式的方式是在程序的最开始增加一个特定的字符串字面量: ‘use strict’ 同样可以在函数体的开始处加入这句指令以启用该函数的严格模式. f ...
- JavaScript 第三章总结
Getting functional function的特点 function can be reused over and over much more readable function is p ...
- 对于所有对象都通用方法的解读(Effective Java 第三章)
这篇博文主要介绍覆盖Object中的方法要注意的事项以及Comparable.compareTo()方法. 一.谨慎覆盖equals()方法 其实平时很少要用到覆盖equals方法的情况,没有什么特殊 ...
- javascript第三章--引用类型
① Object类型 ② Array类型 ③ Date类型 ④ RegExp类型 ⑤ Function类型 ⑥ 基本包装类型 ⑦ 单体内置对象
- Javascript权威指南——第二章词法结构,第三章类型、值和变量,第四章表达式和运算符,第五章语句
第二章 词法结构 一.HTML并不区分大小写(尽管XHTML区分大小写),而javascript区分大小写:在HTML中,这些标签和属性名可以使用大写也可以使用小写,而在javascript中必须小写 ...
- [Effective JavaScript 笔记]第3章:使用函数--个人总结
前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...
- 列表的实现-----数据结构与算法JavaScript描述 第三章
实现一个列表 script var booklist = new List(); booklist.append('jsbook'); booklist.append('cssbook'); book ...
- 《JAVASCRIPT高级程序设计》第三章
<JAVASCRIPT高级程序设计>第三章主要讲述了这门语言的基础概念,内容多而浅,通过思维导图可以帮助我们很好的理清脉络. js函数使用function关键字来声明,以下是一个简单的例子 ...
随机推荐
- Python wifi掉线重连接
原理很简单,通过python执行dos命令 : ping 和 netsh 需要用到os和time模块 代码如下: >>> import os >>> print ' ...
- 【CSS学习笔记】字体的控制
关于font-family: "无衬线体"(Sans-serif),比如黑体,微软雅黑 "有衬线体"(Serif),比如宋体,新宋 程序员设置的字体,客户电脑上 ...
- InnoDB的Named File Formats
随着InnoDB存储引擎的发展,新的页数据结构有时用来支持新的功能特性.比如前面提到的InnoDB Plugin,提供了新的页数据结构来支持表压缩功能,完全溢出的(Off page)大变长字符类型字段 ...
- 前端——HTML笔记-One
一.HTML简介 html即超文本标记语言,指页面内可包含图片.链接,甚至音乐.程序等非文字元素,而标记语言:即标记(标签)构成的语言. 什么是标签: 标签中单词不可以数字开头. 标签不区分大小写.& ...
- CentOS 6.5 安装MySQL5.7 RPM
一.新特性 MySQL 5.7可谓是一个令人激动的里程碑,在默认了InnoDB引擎的基础上,新增了ssl.json.虚拟列等新特性.相对于postgreSQL和MariaDB而言,MySQL5.7做了 ...
- c# 读取ACCESS 数据库
using System; using System.Collections.Generic; using System.Data.OleDb; using System.IO; using Syst ...
- 来看看CBIS 2017中国(上海)大数据产业创新峰会有哪些大师出席
CBIS 2017中国(上海)大数据产业创新峰会,围绕"数据+产业.企业+数据.技术+人才.品牌+市场.应用+发展"相继展开话题讨论. CBIS 2017中国(上海)大数据产业创新 ...
- IOS多线程加锁
注意:加锁位置不同产生结果不同 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { / ...
- linode开通Paypal付款方式
vps服务器品牌linode近期新闻不断.今天是linode成立13周年,全部套餐免费升级翻倍内存,所以现在linode最低配置套餐内存是2GB,每月2TB流量,40Gb机房带宽,非常超值. 长期以来 ...
- Scala分号推断
看这样段代码,Scala会把它当作两个语句,x 和 +y,如果想把它作为一个语句,可以把它们放在括号里(x+y) x +y 或者也可以把 + 放在行末,也正因为此,串接类似于 + 这样的中缀操作符的时 ...