[Effective JavaScript 笔记]第30条:使用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;

标识壹,如果temporary的属性名在指标中已经存在,则会对目的造成机能的破坏。而且取任何名称,都没办法保障不和原来的对象名重名,恐怕那个指标并不是投机创造的。对象能够对质量进行冷冻或密封以预防止改正主义改和添加(详细见附录)任何新属性。

函数对象call方法

中央描述:用途是在特定的成效域下来执行函数,实际上等于设置函数体内this对象的值。
函数对象具备1个放到的点子call来自定义接收者。

f.call(obj,arg1,arg2,arg3)

此行为和直接调用函数自个儿很一般。

f(arg1,arg2,arg3)

分歧点在于,第贰个参数钦命了二个显式的收信人对象(即内定函数内部this的指向)

一、当调用的点子已经被去除、修改大概覆盖时,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

贰、定义高阶函数时call方法也特地实用。 高阶函数的二个惯用法是吸纳三个可选的参数作为调用该函数的收信人。
以身作则:有2个代表键值对列表的靶子,提供了名称为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的剧情复制到另3当中。

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);
}

那段代码从table第22中学领到addEntry方法,forEach方法将table二作为接收者,并1再调用该addEntry方法。

提示

  • 使用call方法自定义接收者来调用函数

  • 利用call方法能够调用在给定的靶子中不存在的办法

  • 动用call方法定义高阶函数允许使用者给回调函数钦赐接收者

附录一:对象属性

注:以下内容出自《javascript高级程序设计语言》第贰版

属性类型

ECMAScript-262第四版在概念唯有在那之中才用的风味时,描述了质量的风味。描述那几个特点是为着达成JS引擎用的,由此js中不能够从来访问它们。为了表示本性是里票面价值,该规范把它们放到了两对儿方括号中。

例如:[[Enumerable]]。

ECMAScript中有三种特性:数据属性和做客器属性。

数量属性

多少属性包含贰个数据值的职责。在那些职责能够读取和写入值。

多少属性有五个描述其一举一动的性状。

  • [[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实行改动。严苛情势下会报错。

上面是1个不行配置的言传身教:

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函数并传到新值,这么些函数负责控制如何处理多少。

走访器属性有如下5个特征:

[[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()方法,大概由此讲述符壹回定义多个属性。

接收多少个目标参数:

  • 率先个对象是要抬高和修改其属性的靶子
  • 其次个指标的习性与第四个对象中要加上或涂改的个性一1对应

读取属性的特色

Object.getOwnPropertyDescriptor()方法,能够拿走给定属性的描述符。

收取四个参数:

  • 品质所在的靶子
  • 要读取其讲述符的属性名称

再次回到值是2个对象,视属性的项目分裂,对象的本性也差别,具体参见上边的属性类型。

示例

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){...}