如何编写高质量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 相关文章推荐
Javascript开发包大全整理
Dec 22 Javascript
js 程序执行与顺序实现详解
May 13 Javascript
jQuery遍历节点树方法分析
Sep 08 Javascript
关于jQuery EasyUI 中刷新Tab选项卡后一个页面变形的解决方法
Mar 02 Javascript
jQuery插件HighCharts绘制2D饼图效果示例【附demo源码下载】
Mar 21 jQuery
微信小程序三级联动地址选择器的实例代码
Jul 12 Javascript
React Native实现进度条弹框的示例代码
Jul 17 Javascript
基于node简单实现RSA加解密的方法步骤
Mar 21 Javascript
解决微信小程序云开发中获取数据库的内容为空的方法
May 15 Javascript
jQuery实现的记住帐号密码功能完整示例
Aug 03 jQuery
vue-router结合vuex实现用户权限控制功能
Nov 14 Javascript
浅谈webpack构建工具配置和常用插件总结
May 11 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中使用Sockets 从Usenet中获取文件
2008/01/10 PHP
PHP数组循环操作详细介绍 附实例代码
2013/02/03 PHP
ThinkPHP里用U方法调用js文件实例
2015/06/18 PHP
php微信开发之批量生成带参数的二维码
2016/06/26 PHP
ThinkPHP整合datatables实现服务端分页的示例代码
2018/02/10 PHP
引用外部js乱码问题分析及解决方案
2013/04/12 Javascript
JS方法调用括号的问题探讨
2014/01/24 Javascript
JavaScript调试技巧之console.log()详解
2014/03/19 Javascript
JavaScript截取字符串的2个函数介绍
2014/08/27 Javascript
用js传递value默认值的示例代码
2014/09/11 Javascript
javascript字符串循环匹配实例分析
2015/07/17 Javascript
jQuery UI设置固定日期选择特效代码分享
2015/08/27 Javascript
js实现网站最上边可关闭的浮动广告条代码
2015/09/04 Javascript
举例讲解JavaScript中关于对象操作的相关知识
2015/11/16 Javascript
js面向对象实现canvas制作彩虹球喷枪效果
2016/09/24 Javascript
jQuery之动画效果大全
2016/11/09 Javascript
javascript 初学教程及五子棋小程序的简单实现
2017/07/04 Javascript
基于LayUI实现前端分页功能的方法
2017/07/22 Javascript
Node.js爬虫如何获取天气和每日问候详解
2019/08/26 Javascript
react实现同页面三级跳转路由布局
2019/09/26 Javascript
js 计算月/周的第一天和最后一天代码
2020/02/01 Javascript
JavaScript Date对象功能与用法学习记录
2020/04/28 Javascript
如何用JS模拟实现数组的map方法
2020/07/30 Javascript
[01:55]2014DOTA2国际邀请赛快报:国土生病 紧急去医院治疗
2014/07/10 DOTA
python求素数示例分享
2014/02/16 Python
Python编程实现线性回归和批量梯度下降法代码实例
2018/01/04 Python
纯python实现机器学习之kNN算法示例
2018/03/01 Python
浅谈Python 递归算法指归
2019/08/22 Python
pyecharts绘制中国2020肺炎疫情地图的实例代码
2020/02/12 Python
ubuntu 安装pyqt5和卸载pyQt5的方法
2020/03/24 Python
李宁官方网店:中国运动品牌
2017/11/02 全球购物
世界顶级户外运动品牌折扣网站:LeftLane Sports
2019/06/12 全球购物
解释一下ArrayList Vector和LinkedList的实现和区别
2013/04/26 面试题
超市后勤自我鉴定
2014/01/17 职场文书
合同纠纷调解书
2015/05/20 职场文书
运动员加油词
2015/07/18 职场文书