[Effective JavaScript 笔记]第20条:使用call方法自定义接收者来调用方法
不好的实践
函数或方法的接收者(即绑定到特殊关键字this的值)是由调用者的语法决定的。
方法调用语法将方法被查找的对象绑定到this变量,(可参阅之前文章《理解函数调用、方法调用及构造函数调用之间的不同》)。
有时需要使用自定义接收者来调用函数,因为该函数可能并不是期望的接收者对象的属性。
可以将方法作为一个新的属性添加到接收者对象中。使用如下代码:
var obj={
temporary:function(a,b,c){console.log('obj')}
}
var f=function(arg1,arg2,arg3){console.log('f')};
var arg1=0,arg2=1,arg3=2;
obj.temporary=f;//标识1
var result=obj.temporary(arg1,arg2,arg3);
delete obj.temporary;
标识1,如果temporary的属性名在对象中已经存在,则会对对象造成功能的破坏。而且取任何名称,都没法确保不和原来的对象名重名,可能这个对象并不是自己创建的。对象可以对属性进行冻结或密封以防止修改和添加(详细见附录)任何新属性。
函数对象call方法
基本描述:用途是在特定的作用域下来执行函数,实际上等于设置函数体内this对象的值。
函数对象具有一个内置的方法call来自定义接收者。
f.call(obj,arg1,arg2,arg3)
此行为和直接调用函数自身很相似。
f(arg1,arg2,arg3)
不同点在于,第一个参数指定了一个显式的接收者对象(即指定函数内部this的指向)
1、当调用的方法已经被删除、修改或者覆盖时,call方法就可以派上用场了。
以hasOwnProperty方法为例,因为这个方法是Object的方法,所有对象都可以访问调用这个方法。
var obj={
foo:'good'
};
obj.hasOwnProperty('foo');//true
然后当这个方法被覆盖时
obj.hasOwnProperty=1;
obj.hasOwnProperty('foo');//Uncaught TypeError: obj.hasOwnProperty is not a function(…)
这时,call方法就可以派上用场了
var hasOwnProperty={}.hasOwnProperty;
delete obj.hasOwnProperty;
hasOwnProperty.call(obj,'foo');//true
hasOwnProperty.call(obj,'hasOwnProperty');//false
2、定义高阶函数时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);
我们把上面的代码直接带入上面的代码大家可以晰看到它的执行过程。
var entries=table1.entries;
for(var i=0,n=entries.length;i<n;i++){
var entry=entries[i];
table2.addEntry.call(table2,entry.key,entry.value,i);
}
这段代码从table2中提取addEntry方法,forEach方法将table2作为接收者,并反复调用该addEntry方法。
提示
使用call方法自定义接收者来调用函数
使用call方法可以调用在给定的对象中不存在的方法
使用call方法定义高阶函数允许使用者给回调函数指定接收者
附录一:对象属性
注:以下内容出自《javascript高级程序设计语言》第3版
属性类型
ECMAScript-262第5版在定义只有内部才用的特性时,描述了属性的特征。描述这些特性是为了实现JS引擎用的,因此js中不能直接访问它们。为了表示特性是内部值,该规范把它们放到了两对儿方括号中。
例如:[[Enumerable]]。
ECMAScript中有两种属性:数据属性和访问器属性。
数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。
数据属性有4个描述其行为的特性。
- [[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
- [[Enumerable]]:表示能否通过for-in循环返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
- [[Writable]]:表示能否修改属性的值。
- [[Value]]:包含这个属性的数据值。读取属性值的时候,这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为undefined。
示例:
var person={
name:'Nicholas'
}
这里创建的一个名为name的属性,为它指定的值是'Nicholas'。
也就是说person对象的name属性的[[Value]]特性将被设置为'Nicholas',对这个值的任何修改都将反映在这个位置。
修改属性的方法
修改属性默认特性的方法,必须使用 ES5的Object.defineProperty()方法。
这个方法接收三个参数:
- 属性所在的对象
- 属性的名字
- 一个描述符对象
描述符对象的属性必须是:configurable,enumerable,writable和value。设置其中的一或多个值,可以修改对应的特性值。
例如:
var person={};
Object.defineProperty(preson,'name',{
writable:false,
value:'li lei'
});
person.name;//"li lei"
person.name='han mei mei';
person.name;//"li lei"
这里把name属性设置为只读的属性,所以无法对name进行修改。严格模式下会报错。
下面是一个不可配置的示例:
var person={};
Object.defineProperty(preson,'name',{
configurable:false,
value:'li lei'
});
person.name;//"li lei"
delete person.name;
person.name;//"li lei"
把configurable设置为false,表示不能从对象中删除属性。如果在严格模式下,上面的代码会报错。一旦把属性定义为不可配置的,就不能再把它变回为可配置的。此时再调用Object.defineProperty()方法修改除writable之外的特性,都会导致错误。
注意:在调用Object.defineProperty()方法时,如果不指定,configurable,enumerable,writable特性的默认值都是false。多数情况下,都没必要利用Object.defineProperty()方法提供的这些的高级功能。对于理解js对象非常有用。
访问器属性
访问器属性不包含数据值;它们包含一对儿getter和setter函数(不过,这两个函数都不是必需的)。
读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据。
访问器属性有如下4个特性:
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为true。
[[Enumerable]]:表示能否通过for-in循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为true。
[[Get]]:在读取属性时调用的函数。默认值为undefined。
[[Set]]:在写入属性时调用的函数。默认值为undefined。
访问器属性不能直接定义,必须使用Object.defineProperty()来定义。
示例:
var book={
_year:2004,
edition:1
};
Object.defineProperty(book,'year',{
get:function(){
return this._year;
},
set:function(newVal){
if(newVal>2004){
this._year=newVal;
this.edition+=newVal-2004;
}
}
});
book.year=2005;
book.edition;//2
book._year;//2005
book.year;//2005
定义多个属性
直接上代码
var book={};
Object.defineProperties(book,{
_year:{
value:2004
},
edition:{
value:1
},
year:{
get:function(){
return this._year;
},
set:function(newVal){
if(newVal>2004){
this._year=newVal;
this.edition+=newVal-2004;
}
}
}
})
Object.defineProperties()方法,可能通过描述符一次定义多个属性。
接收两个对象参数:
- 第一个对象是要添加和修改其属性的对象
- 第二个对象的属性与第一个对象中要添加或修改的属性一一对应
读取属性的特性
Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符。
接收两个参数:
- 属性所在的对象
- 要读取其描述符的属性名称
返回值是一个对象,视属性的类型不同,对象的属性也不同,具体参见上面的属性类型。
示例
var book={};
Object.defineProperties(book,{
_year:{
value:2004
},
edition:{
value:1
},
year:{
get:function(){
return this._year;
},
set:function(newVal){
if(newVal>2004){
this._year=newVal;
this.edition+=newVal-2004;
}
}
}
})
var descriptor=Object.getOwnPropertyDescriptor(book,'_year');
descriptor;//Object {value: 2004, writable: false, enumerable: false, configurable: false}
descriptor=Object.getOwnPropertyDescriptor(book,'year');
descriptor;//Object {configurable:false,enumerable:false,get:function(){...},set:function(newVal){...}
[Effective JavaScript 笔记]第20条:使用call方法自定义接收者来调用方法的更多相关文章
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记]第38条:在子类的构造函数中调用父类的构造函数
示例 场景类 场景图(scene)是在可视化的过程中(如游戏或图形仿真场景)描述一个场景的对象集合.一个简单的场景包含了在该场景中的所有对象(称角色),以及所有角色的预加载图像数据集,还包含一个底层图 ...
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第22条:使用arguments创建可变参数的函数
第21条讲述使用可变参数的函数average.该函数可处理任意数量的参数并返回这些参数的平均值. 如何创建可变参数的函数 1.实现固定元数的函数 书上的版本 function averageOfArr ...
- [Effective JavaScript 笔记]第42条:避免使用轻率的猴子补丁
41条对违反抽象原则行为的讨论之后,下面聊一聊终极违例.由于对象共享原型,因此每一个对象都可以增加.删除或修改原型的属性.这个有争议的实践通常称为猴子补丁. 猴子补丁示例 猴子补丁的吸引力在于其强大. ...
- [Effective JavaScript 笔记]第44条:使用null原型以防止原型污染
第43条中讲到的就算是用了Object的直接实例,也无法完全避免,Object.prototype对象修改,造成的原型污染.防止原型污染最简单的方式之一就是不使用原型.在ES5之前,并没有标准的方式创 ...
- [Effective JavaScript 笔记]第51条:在类数组对象上复用通用的数组方法
前面有几条都讲过关于Array.prototype的标准方法.这些标准方法被设计成其他对象可复用的方法,即使这些对象并没有继承Array. arguments对象 在22条中提到的函数argument ...
随机推荐
- 网页音乐突破金币(RMB)下载限制
我平时有时间会跳跳舞 跳舞肯定要有音乐呀 于是在网上找音乐 好不容易找到了一个网站,里面有很多很全的音乐 正准备下载呢,尼玛居然要金币! 在这里解释一下,金币你可以通过回复帖子或者发帖子得到,但是数量 ...
- 类似IOS的滑动返回上一级,SwipeBackLayout-android的滑动返回类库
最近,公司在开发App的需求中增加了一个新的需求,要在android的页面中增加向右滑动的时候返回上一级页面.我刚知道这个需求的时候,感觉有点坑,可能设计那边最近接触到知乎的客户端或者是IOS的滑动可 ...
- DOM系列---DOM获取尺寸和位置
内容提纲: 1.获取元素CSS大小 2.获取元素实际大小 3.获取元素周边大小 本篇我们主要讨论一下页面中的某一个元素它的各种大小和各种位置的计算方式. 一.获取元素CSS大小 1.通过style获取 ...
- Office2010 pro附+激活工具
office2003经典的办公,office2010,很不错的办公工具配合Win7,就绝配! office2010,我也近几天接手她,慢慢熟悉... 00安装: 01激活: 内含office2010安 ...
- Spring-MVC流程图
Spring MVC工作流程图 图一 图二 Spring工作流程描述 1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获: ...
- href的参数含有中文在IE下乱码的解决
这是在使用kendo grid的自定义链接时遇到的一个坑,链接如下: var TempStr = "<a href='" + Url.Action("EditUse ...
- BZOJ-1875 HH去散步 DP+矩阵乘法快速幂
1875: [SDOI2009]HH去散步 Time Limit: 20 Sec Memory Limit: 64 MB Submit: 1196 Solved: 553 [Submit][Statu ...
- BZOJ-1036 树的统计Count 链剖线段树(模板)=(树链剖分+线段树)
潇爷昨天刚刚讲完...感觉得还可以...对着模板打了个模板...还是不喜欢用指针.... 1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Lim ...
- 【poj2891】 Strange Way to Express Integers
http://poj.org/problem?id=2891 (题目链接) 题意 求解线性同余方程组,不保证模数一定两两互质. Solotion 一般模线性方程组的求解,详情请见:中国剩余定理 细节 ...
- Codevs1299 切水果
题目描述 Description 简单的说,一共N个水果排成一排,切M次,每次切[L,R]区间的所有水果(可能有的水果被重复切),每切完一次输出剩下水果数量 数据已重新装配,不会出现OLE错误 时限和 ...