前段时间看了几道关于前端javascript的面试题目,方觉函数调用模式等基础的重要性。于是,下定决心,好好补补基础,即便不能深入语言的内部设计模式,也要对基本面向对象概念有比较深入的理解。

继续上一篇博文《编写高质量JS代码上》今次整理一下javascript函数知识点。

2.使用函数

函数给程序员提供了主要的抽象功能,又提供实现机制。函数可以独立实现其他语言中的多个不同的特性,例如,过程、方法、构造函数,甚至类或模块。

2.1 理解函数调用、方法调用以及构造函数调用之间的不同

针对面向对象编程,函数、方法和类的构造函数是三种不同的概念。

使用模式:1,函数调用

function hello(username){
return "hello" + username;
}

2,方法调用

var obj = {
hello : function(){
return "hello , " + this.username;
},
username : "floraLam"
};
ohj.hello();//"hello , floraLam"

this变量被绑定到对象是由于hello方法被定义在obj对象中,我们也可以在另外一个对象中赋值一份相同的函数引用,并得到相同的答案。

var obj2 = {
hello : obj.hello(),
username : "floraLam"
};

3,构造函数使用

function User(name,passwordHash){
this.name = name;
this.passwordHash = passwordHash;
}

使用new操作符来调用User则视为构造函数。

var u  = new User("floraLam","123");

与函数调用和方法调用不同的是,构造函数调用将一个全新的对象作为this变量的值,并隐式返回这个新对象作为调用结果。构造函数的主要职责是初始化该新对象。

2.2 熟练掌握高阶函数

高阶函数无非是那些将函数作为参数或返回值的函数,将函数作为参数(通常称为回调函数,因为高阶函数"随后调用"它)是一种特别强大、富有表现力的惯用法,也在js程序中被大量使用。

考虑数组的标准sort方法,为了对所有数组都能工作,sort方法需要调用者决定如何比较数组中的任意两个元素。

function compareNumber(x,y){
if(x < y){
return -1;
}
if(x > y){
return 1;
}
return 0;
}
[3,1,4,1,5,9].sort(compareNumbers);//[1,1,3,4,5,9]
[3,1,4,1,5,9].sort(function(x,y){
if(x < y){
return -1;
}
if(x > y){
return 1;
}
return 0;
});//[1,1,3,4,5,9]

上述例子使用一个匿名函数进一步简化。

学会使用高阶函数通常可以简化代码并消除繁琐的样板代码。简单的转换字符串数组的操作我们可以使用循环这样实现:

var names = ["Fred","Wilma","Pebbles"];
var upper = [];
for(var i = 0,n = names.length ;i< n;i++){
upper[i] = names[i].toUpperCase();
}
upper;//["FRED","WILMA","PEBBLES"];

使用数组便利的map方法,可以消除循环,仅仅使用一个局部函数就可以对元素的逐个转换。

var names = ["Fred","Wilma","Pebbles"];
var upper = names.map(function(name){
return name.toUpperCase();
}); upper;//["FRED","WILMA","PEBBLES"];

另外,例如我们想创建若干个方法创建不同的字符串,具有共同的实现逻辑,每个循环通过连接每个独立部分的计算结果来创建一个字符串。

 function bulidString(n,callback){
var result = "";
for(var i = 0 ; i < n ;i++){
result += callback(i);
}
return result;
} var alphabet = bulidString(26,function(i){
return String.fromCharCode(aIndex + i);
});
alphabet;//"abcdefghijklmnopqrxtuvwxyz"; var digits = buildString(10,function(i){ return i;})
digits;//"0123456789" var random = buildString(9,function(){
random += String.fromCharCode(Math.floor(Math.random()*26)+aIndex
});
random;//"yefjmcef"(随机)

这样能够使得读者更清晰了解该代码能做什么,无须深入实现细节。

备注

   javascript返回指定范围的随机数(m-n之间)的公式:Math.random()*(n-m)+m

  同时要注意题目要求,是否要求返回正整数

2.3调用模式

调用一个函数将会暂停当前函数的执行,传递控制权与参数给新的函数。 除了声明时定义的形式参数,每个函数会接收到两个新的附加参数:this和arguments。

this是个很重要的参数,并且它的值是由调用模式决定的。

以下是JavaScript中很重要的4个调用模式:

a. 方法调用模式the method invocation pattern

b. 函数调用模式the function invocation pattern

c. 构造器调用模式the constructor invocation pattern

d. Apply调用模式the apply invocation pattern

这些模式在如何初始化关键参数this上存在差异

1. 方法调用模式the method invocation method

当函数作为对象的方法的时候,我们就叫函数为方法。当一个方法被调用的时候,this绑定到调用的对象。

var myObj={

    val:0,
increment:function(inc){
this.val+=typeof inc ==="number"? inc:1;
},
get_val:function(){return this.val;} } myObj.increment();// 1 myObj["increment"](2);//3

小结:

1、通过this可取得它们所属对象的上下文的方法称为公共方法

2、当用 .或者下标表达式 来使用一个函数的时候,就是方法调用模式,this对象绑定到前面的对象。

3,一个函数可以使用this来访问对象,所以它能检索对象的值或者更改对象的值。绑定this到对象发生在调用的时候。

2. 函数调用模式the function invocation pattern

当一个函数不是一个对象的属性,那么它就是作为函数来调用的。当一个函数作为函数调用模式来调用的时候,this绑定到全局对象。这是JavaScript设计时的错误并延续了下来。

function add(x,y){
  return x+y;
} myObj.double=function(){ var that=this;
var helper=function(){
that.val=add(that.value,that.value);
//错误的写法可能是这样,为什么错呢?因为函数作为内部函数调用的时候,this已经绑定到了错误的对象,全局对象并没有val属性,所以返回不正确的值。
//this.val = this.val+this.val;
  }
  helper();
} myObj.double();//6

3. 构造器调用模式the constructor invocation pattern

JavaScript是一门基于原型继承的语言,这意味着对象可以直接继承属性从其它的对象,该语言是无类别的。

如果在一个函数前面带上new来调用,那么将得到一个隐藏连接到该函数的prototype成员的新对象,同时this也将会绑定到该新对象。

new前缀也会改变return语句的行为。这也不是推荐的编程方式。

var Foo = function(status){
this.status = status;
} Foo.prototype.get_status = function(){
return this.status;
} //构造一个Foo实例 var myFoo = new Foo("bar");
myFoo.get_status();//"bar"

4. Apply调用模式the apply invocation pattern

因为JavaScript是一个函数式的面向对象语言,所以函数可以拥有方法。

Apply方法拥有两个参数,第一个是将绑定到this的值,第二个是参数数组,也就是说Apply方法让我们构建一个数组并用其去调用函数,即允许我们选择this的值,也允许我们选择数组的值。

var array = [3,4];

var sum = add.apply(null,array); // 7

var statusObj = {status:"ABCDEFG"};

Foo.prototype.pro_get_status = function(prefix){
return prefix +"-"+this.status;
} var status = Foo.prototype.get_status.apply(statusObj);// "ABCDEFG" var pro_status = Foo.prototype.get_status.apply(statusObj,["prefix"]);// "prefix -ABCDEFG"

通常情况下,函数或方法的接收者(级绑定到特殊关键字this的值)是由调用者的语法决定性的。特别地,方法调用语法将方法被查找对象绑定到this变量。然而,有时需要使用自定义接收者来调用函数。这时候就需要使用call方法或者bind方法自定义接收者来调用方法

2.4 使用bind方法提取具有确定接受者的方法

由于方法与值为函数的属性没有区别,因此也容易提取对象的方法并提取出函数作为回调函数直接传递给高阶函数。

但这也很容易忘记将提取出来的函数的接受着绑定到该函数被提取出的对象上。

var buffer = {
entries: [],
add :function(s){
this.entries.push(s);
}
}
var source = ["867","-","5309"];
source.forEach(butter.add);//error:entries is undefined

这个时候butter.add的接受者不是butter对象。函数的接收者取决于它是如何被调用的,forEach方法在全局作用域中被调用,因此forEach方法的实现使用全局对象作为默认的接收者,由于全局对象中没有entries属性,因此这段代码抛出错误。

forEach方法允许调用者提供一个可选的参数作为回调函数的接收者。

var source = ["867","-","5309"];
source.forEach(butter.add,butter);

但并非所有高阶函数都细心周到为使用者提供回调函数的接收者。

解决方法有两种:

1)创建一个显式地一buffer对象方法的方式调用add的封装函数。不管封装函数如何被调用,它总能确保将其参数推送到目标数组中。

var source = ["867","-","5309"];
source.forEach(function(s){
butter.add(s);
});

2)函数对象的bind方法需要一个接收者对象,并产生一个以该接收者对象的方法调用的方法调用原来的函数的封装函数。

var source = ["867","-","5309"];
source.forEach(butter.add.bind(buffer));

备注

  buffer.add.bind(buffer)创建一个新函数而不是修改buffer.add函数:

  buffer.add === buffer.add.bind(buffer); //false

编写高质量JS代码中的更多相关文章

  1. 编写高质量JS代码的68个有效方法(八)

    [20141227]编写高质量JS代码的68个有效方法(八) *:first-child { margin-top: 0 !important; } body>*:last-child { ma ...

  2. 编写高质量JS代码的68个有效方法(七)

    [20141220]编写高质量JS代码的68个有效方法(七) *:first-child { margin-top: 0 !important; } body>*:last-child { ma ...

  3. 编写高质量JS代码的68个有效方法(六)

    [20141213]编写高质量JS代码的68个有效方法(六) *:first-child { margin-top: 0 !important; } body>*:last-child { ma ...

  4. 编写高质量JS代码的68个有效方法(四)

    [20141129]编写高质量JS代码的68个有效方法(四) *:first-child { margin-top: 0 !important; } body>*:last-child { ma ...

  5. 编写高质量JS代码的68个有效方法(三)

    [20141030]编写高质量JS代码的68个有效方法(三) *:first-child { margin-top: 0 !important; } body>*:last-child { ma ...

  6. 编写高质量JS代码的68个有效方法(二)

    [20141011]编写高质量JS代码的68个有效方法(二) *:first-child { margin-top: 0 !important; } body>*:last-child { ma ...

  7. JavaScript手札:《编写高质量JS代码的68个有效方法》(一)(1~5)

    编写高质量JS代码的68个有效方法(一) *:first-child { margin-top: 0 !important; } body>*:last-child { margin-botto ...

  8. 编写高质量JS代码的68个有效方法(十三)

    No.61.不要阻塞I/O事件队列 Tips: 异步API使用回调函数来延缓处理代价高昂的操作以避免阻塞主应用程序 JavaScript并发的接收事件,但会使用一个事件队列按序地处理事件处理程序 在应 ...

  9. 编写高质量JS代码的68个有效方法(九)

    No.41.将原型视为实现细节 Tips: 对象是接口,原型是实现 避免检查你无法控制的对象的原型结构 避免检查实现在你无法控制的对象内部的属性 我们可以获取对象的属性值和调用其方法,这些操作都不是特 ...

随机推荐

  1. UNIX网络编程——socket的keep-alive(转)

    第一部分 [需求] 不影响服务器处理的前提下,检测客户端程序是否被强制终了. [现状] 服务器端和客户端的Socket都设定了keepalive属性. 服务器端设定了探测次数等参数,客户端.服务器只是 ...

  2. ios常用空间UIScrollViewIndicator的一些属性

    UIScrollView属性: 1  alwaysBounceHorizontal         BOOL值,当水平滚条到达终点,总是(视图)弹跳 2  alwaysBounceVertical   ...

  3. iOS学习之iOS沙盒(sandbox)机制和文件操作之NSFileManager

    我们看看NSFileManager如何使用.包括创建文件,目录,删除,遍历目录等. 1.在Documents里创建目录 创建一个叫test的目录,先找到Documents的目录, [cpp] view ...

  4. 微信公众号token 验证

    1. 首先给出测试项目的整体目录: 2. CoreServlet类: 当get请求的时候会执行get方法,post请求的时候会执行post方法,分别来处理不同的请求 package com.zjn.s ...

  5. 在Yii Framework中利用PHPMailer发送邮件(2011-06-02 14:06:23)

    转载▼ 标签: it 分类: 技术共享 官方扩展链接:http://www.yiiframework.com/extension/mailer/这个扩展配置十分方便,如果有问题的话,可以打开Debug ...

  6. FreeSWITCH 使用SSL-WebSocket-WebRTC

    阿里上买的域名, 申请了个免费ssl, 然后开始折腾,,,, 申请了ssl证书, 但是不提供 .pem 格式的下载(*/ω\*) 然后 把一堆 提供的 都下载下来了,  然后 又到网上 搜 crt/c ...

  7. 浅析 python中的 print 和 input 的底层区别!!!

    近期的项目中 涉及到相关知识 就来总结一下 ! 先看源码: def print(self, *args, sep=' ', end='\n', file=None): # known special ...

  8. [cogs2638]数列操作ψ(双标记线段树)

    题目大意:给定一个数列a,你需要支持的操作:区间and,区间or,询问区间最大值 解题关键: 1.双标记线段树,注意优先级(超时) 当涉及多重标记时,定义出标记的优先级,修改操作时用优先级高(先下放) ...

  9. 《Android Studio实用指南》7.1 AndroidStudio代码检查工具概述

    本文节选自<Android Studio实用指南> 作者: 毕小朋 目前本书已上传到百度阅读, 在百度中搜索[Anroid Studio实用指南]便可以找到本书. Android Stud ...

  10. Mask_rcnn openpose realsense

    cd /home/luo/Desktop/MyFile/Mask_RCNN_Openpose_Realsense python realsense_mask_openpose_2019032601.p ...