如何编写高质量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 相关文章推荐
用jquery写的菜单从左往右滑动出现
Apr 11 Javascript
js锁屏解屏通过对$.ajax进行封装实现
Jul 31 Javascript
EasyUI中datagrid在ie下reload失败解决方案
Mar 09 Javascript
JS表单验证的代码(常用)
Apr 08 Javascript
jquery常用的12个小功能
Jul 22 Javascript
JavaScript中的冒泡排序法
Aug 03 Javascript
JavaScript实现简单的日历效果
Sep 25 Javascript
原生js实现简单的模态框示例
Sep 08 Javascript
Vue2.0 http请求以及loading展示实例
Mar 06 Javascript
Android 自定义view仿微信相机单击拍照长按录视频按钮
Jul 19 Javascript
使用layui监听器监听select下拉框,事件绑定不成功的解决方法
Sep 28 Javascript
vue+webpack 更换主题N种方案优劣分析
Oct 28 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下载远程文件类(支持断点续传)
2008/11/14 PHP
基于php实现长连接的方法与注意事项的问题
2013/05/10 PHP
thinkphp 中的volist标签在ajax操作中的特殊性(推荐)
2018/01/15 PHP
详解php协程知识点
2018/09/21 PHP
jQuery学习2 选择器的使用说明
2010/02/07 Javascript
js弹出的对话窗口永远保持居中显示
2012/12/15 Javascript
EXTjs4.0的store的findRecord的BUG演示代码
2013/06/08 Javascript
jQuery lazyLoad图片延迟加载插件的优化改造方法分享
2013/08/13 Javascript
详解JavaScript基于面向对象之创建对象(2)
2015/12/10 Javascript
JavaScript中Window对象的属性及事件
2015/12/25 Javascript
一步一步封装自己的HtmlHelper组件BootstrapHelper(二)
2016/09/14 Javascript
微信小程序左滑删除效果的实现代码
2017/02/20 Javascript
Bootstrap禁用响应式布局的实现方法
2017/03/09 Javascript
node.js操作mongodb简单示例分享
2017/05/25 Javascript
深入理解node.js http模块
2018/01/24 Javascript
Vue项目部署在Spring Boot出现页面空白问题的解决方案
2018/11/26 Javascript
VUE脚手架的下载和配置步骤详解
2019/04/01 Javascript
vue组件中iview的modal组件爬坑问题之modal的显示与否应该是使用v-show
2019/04/12 Javascript
JS实现拖拽元素时与另一元素碰撞检测
2020/08/27 Javascript
vue Treeselect下拉树只能选择第N级元素实现代码
2020/08/31 Javascript
python 字典(dict)遍历的四种方法性能测试报告
2014/06/25 Python
python验证码识别的实例详解
2016/09/09 Python
python实现百万答题自动百度搜索答案
2018/01/16 Python
安装python及pycharm的教程图解
2019/10/10 Python
python科学计算之narray对象用法
2019/11/25 Python
在django中实现choices字段获取对应字段值
2020/07/12 Python
python绘制趋势图的示例
2020/09/17 Python
如何利用python生成MD5并去重
2020/12/07 Python
CSS3 Calc实现滚动条出现页面不跳动问题
2017/09/14 HTML / CSS
vivo智能手机官方商城:vivo
2016/09/22 全球购物
北美最大的参茸药食商城:德成行
2020/12/06 全球购物
企业申诉管理制度
2014/01/30 职场文书
春季运动会加油词
2015/07/18 职场文书
Pygame Event事件模块的详细示例
2021/11/17 Python
美元符号 $
2022/02/17 杂记
Android实现图片九宫格
2022/06/28 Java/Android