如何编写高质量JS代码(续)


Posted in Javascript onFebruary 25, 2015

继续上一篇文章《如何编写高质量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

以上就是本文的全部内容了,希望大家能够喜欢。

Javascript 相关文章推荐
求解开jscript.encode代码的asp函数
Feb 28 Javascript
基于Bootstrap实现Material Design风格表单插件 附源码下载
Apr 18 Javascript
String字符串截取的四种方式总结
Nov 28 Javascript
12306 刷票脚本及稳固刷票脚本(防挂)
Jan 04 Javascript
浅谈js中的变量名和函数名重名
Feb 13 Javascript
浅谈$_FILES数组为空的原因
Feb 16 Javascript
微信小程序实现移动端滑动分页效果(ajax)
Jun 13 Javascript
bootstrap fileinput实现文件上传功能
Aug 23 Javascript
JQuery EasyUI 结合ztrIee的后台页面开发实例
Sep 01 jQuery
Javascript将图片的绝对路径转换为base64编码的方法
Jan 11 Javascript
vue-cli3环境变量与分环境打包的方法示例
Feb 18 Javascript
js实现直播点击飘心效果
Aug 19 Javascript
网页禁用右键菜单和鼠标拖动选择方法小结
Feb 25 #Javascript
javascript实现点击按钮让DIV层弹性移动的方法
Feb 24 #Javascript
JS+CSS实现仿新浪微博搜索框的方法
Feb 24 #Javascript
JS实现让访问者自助选择网页文字颜色的方法
Feb 24 #Javascript
JS给超链接加确认对话框的方法
Feb 24 #Javascript
javascript实现图片循环渐显播放的方法
Feb 24 #Javascript
javascript感应鼠标图片透明度显示的方法
Feb 24 #Javascript
You might like
php preg_replace替换实例讲解
2013/11/04 PHP
php简单处理XML数据的方法示例
2017/05/19 PHP
javascript 词法作用域和闭包分析说明
2010/08/12 Javascript
JavaScript 对象模型 执行模型
2010/10/15 Javascript
js变量、作用域及内存详解
2014/09/23 Javascript
JavaScript中的闭包(Closure)详细介绍
2014/12/30 Javascript
JavaScript如何自定义trim方法
2015/07/28 Javascript
jQuery实现导航滚动到指定内容效果完整实例【附demo源码下载】
2016/09/20 Javascript
javascript兼容性(实例讲解)
2017/08/15 Javascript
vue实现在表格里,取每行的id的方法
2018/03/09 Javascript
webpack4.x CommonJS模块化浅析
2018/11/09 Javascript
js实现详情页放大镜效果
2020/10/28 Javascript
[29:16]完美世界DOTA2联赛决赛日 Inki vs LBZS 第三场 11.08
2020/11/10 DOTA
python访问sqlserver示例
2014/02/10 Python
python使用循环实现批量创建文件夹示例
2014/03/25 Python
Django框架中数据的连锁查询和限制返回数据的方法
2015/07/17 Python
Python使用三种方法实现PCA算法
2017/12/12 Python
python3大文件解压和基本操作
2017/12/15 Python
python选取特定列 pandas iloc,loc,icol的使用详解(列切片及行切片)
2019/08/06 Python
Python partial函数原理及用法解析
2019/12/11 Python
Python虚拟环境库virtualenvwrapper安装及使用
2020/06/17 Python
浅谈Python xlwings 读取Excel文件的正确姿势
2021/02/26 Python
CSS3实现粒子旋转伸缩加载动画
2016/04/22 HTML / CSS
CSS3 display知识详解
2015/11/25 HTML / CSS
德国帽子专家:Hutshopping
2019/11/03 全球购物
日本酒店、民宿、温泉旅馆、当地旅行团中文预订:e路东瀛
2019/12/09 全球购物
2014年元旦促销活动方案
2014/02/22 职场文书
基层领导干部“四风”问题批评与自我批评
2014/09/23 职场文书
工商行政处罚决定书
2015/06/24 职场文书
捐款仪式主持词
2015/07/04 职场文书
新郎父母婚礼致辞
2015/07/27 职场文书
小学主题班会教案
2015/08/17 职场文书
导游词之镇江-金山寺
2019/10/14 职场文书
Python+Appium实现自动抢微信红包
2021/05/21 Python
python Tkinter模块使用方法详解
2022/04/07 Python
详解Flutter网络请求Dio库的使用及封装
2022/04/14 Java/Android