如何编写高质量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 指南/入门基础
Nov 30 Javascript
在JavaScript中,为什么要尽可能使用局部变量?
Apr 06 Javascript
JS分割字符串并放入数组的函数
Jul 04 Javascript
JavaScript高级程序设计阅读笔记(六) ECMAScript中的运算符(二)
Feb 27 Javascript
JavaScript实现找出字符串中第一个不重复的字符
Sep 03 Javascript
javascript实现随时变化着的背景颜色
Apr 02 Javascript
Nginx上传文件全部缓存解决方案
Aug 17 Javascript
jQuery右侧选项卡焦点图片轮播特效代码分享
Sep 05 Javascript
Eclipse引入jquery报错如何解决
Dec 01 Javascript
PHP7新特性简述
Jun 11 Javascript
解决vue2.0 element-ui中el-upload的before-upload方法返回false时submit()不生效问题
Aug 24 Javascript
解决vue跨域axios异步通信问题
Apr 17 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
PHP4和PHP5共存于一系统
2006/11/17 PHP
PHP注释实例技巧
2008/10/03 PHP
PHP操作Memcache实例介绍
2013/06/14 PHP
PHP生成不同颜色、不同大小的tag标签函数
2013/09/23 PHP
Laravel 5.3 学习笔记之 配置
2016/08/28 PHP
JS字符串处理实例代码
2013/08/05 Javascript
JSON序列化与解析原生JS方法且IE6和chrome测试通过
2013/09/05 Javascript
Jquery.Form 异步提交表单的简单实例
2014/03/03 Javascript
jQuery读取XML文件内容的方法
2015/03/09 Javascript
jQuery操作Table技巧大汇总
2016/01/23 Javascript
微信小程序 获取相册照片实例详解
2016/11/16 Javascript
layer弹出层框架alert与msg详解
2017/03/14 Javascript
yii form 表单提交之前JS在提交按钮的验证方法
2017/03/15 Javascript
Angular 2 ngForm中的ngModel、[ngModel]和[(ngModel)]的写法
2017/06/29 Javascript
BootStrap TreeView使用实例详解
2017/11/01 Javascript
Vue.js 使用v-cloak后仍显示变量的解决方法
2018/11/19 Javascript
微信小程序实现通过js操作wxml的wxss属性示例
2018/12/06 Javascript
关于微信小程序获取小程序码并接受buffer流保存为图片的方法
2019/06/07 Javascript
JS创建自定义对象的六种方法总结
2020/12/15 Javascript
[03:03]2014DOTA2西雅图国际邀请赛 Alliance战队巡礼
2014/07/07 DOTA
python使用scrapy解析js示例
2014/01/23 Python
python原始套接字编程示例分享
2014/02/21 Python
python 删除非空文件夹的实例
2018/04/26 Python
Python可变参数*args和**kwargs用法实例小结
2018/04/27 Python
python实现烟花小程序
2019/01/30 Python
python自动发邮件总结及实例说明【推荐】
2019/05/31 Python
pyqt5 键盘监听按下enter 就登陆的实例
2019/06/25 Python
对Django项目中的ORM映射与模糊查询的使用详解
2019/07/18 Python
Pycharm操作Git及GitHub的步骤详解
2020/10/27 Python
Servlet的生命周期
2013/08/25 面试题
个人简历中的自我评价范例
2013/10/29 职场文书
本科毕业生求职自荐信
2014/02/03 职场文书
专家推荐信怎么写
2015/03/25 职场文书
写作指导:怎么书写竞聘演讲稿?
2019/07/04 职场文书
导游词之西安骊山
2019/12/20 职场文书
Redis超详细讲解高可用主从复制基础与哨兵模式方案
2022/04/07 Redis