如何编写高质量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 相关文章推荐
js实现鼠标经过表格行变色的方法
May 12 Javascript
浅谈使用MVC模式进行JavaScript程序开发
Nov 10 Javascript
详解JavaScript基于面向对象之继承实例
Dec 16 Javascript
深入浅析Node.js 事件循环
Dec 20 Javascript
jquery实现input框获取焦点的简单实例
Jan 26 Javascript
用Vue.extend构建消息提示组件的方法实例
Aug 08 Javascript
JavaScript实现正则去除a标签并保留内容的方法【测试可用】
Jul 18 Javascript
详解如何为你的angular app构建一个第三方库
Dec 07 Javascript
JavaScript中import用法总结
Jan 20 Javascript
使用JavaScript解析URL的方法示例
Mar 01 Javascript
javascript实现计算器功能
Mar 30 Javascript
Vue 实例中使用$refs的注意事项
Jan 29 Vue.js
网页禁用右键菜单和鼠标拖动选择方法小结
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使用逆波兰式计算工资的方法
2015/07/29 PHP
老司机传授Ubuntu下Apache+PHP+MySQL环境搭建攻略
2016/03/20 PHP
Laravel给生产环境添加监听事件(SQL日志监听)
2017/06/19 PHP
PHP实现将几张照片拼接到一起的合成图片功能【便于整体打印输出】
2017/11/14 PHP
php fread函数使用方法总结
2019/05/28 PHP
PHP的静态方法与普通方法用法实例分析
2019/09/26 PHP
js身份证验证超强脚本
2008/10/26 Javascript
JavaScript 继承详解(一)
2009/07/13 Javascript
JavaScript面向对象设计二 构造函数模式
2011/12/20 Javascript
jQuery表单获取和失去焦点输入框提示效果的实例代码
2013/08/01 Javascript
js关于精确计算和数值格式化以及直接引js文件
2014/01/28 Javascript
js匿名函数的调用示例(形式多种多样)
2014/08/20 Javascript
JavaScript实现找质数代码分享
2015/03/24 Javascript
JavaScript函数使用的基本教程
2015/06/04 Javascript
基于jQuery实现拖拽图标到回收站并删除功能
2015/11/25 Javascript
zTree树形插件异步加载方法详解
2017/06/14 Javascript
解决LayUI表单获取不到data的问题
2018/08/20 Javascript
利用不到200行代码写一款属于你自己的js类库
2019/07/08 Javascript
Vue函数式组件的应用实例详解
2019/08/30 Javascript
记录vue做微信自定义分享的一些问题
2019/09/12 Javascript
JS图片懒加载技术实现过程解析
2020/07/27 Javascript
Vue-router中hash模式与history模式的区别详解
2020/12/15 Vue.js
Python实现去除列表中重复元素的方法总结【7种方法】
2019/02/16 Python
python给微信好友定时推送消息的示例
2019/02/20 Python
python数据库操作mysql:pymysql、sqlalchemy常见用法详解
2020/03/30 Python
Python基于argparse与ConfigParser库进行入参解析与ini parser
2021/02/02 Python
李维斯牛仔裤英国官方网站:Levi’s英国
2019/10/10 全球购物
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?
2016/08/18 面试题
大学生个人总结的自我评价
2013/10/05 职场文书
生产部经理岗位职责
2013/12/16 职场文书
生产部管理制度
2014/01/31 职场文书
《再见了,亲人》教学反思
2014/02/26 职场文书
2014教育局对照检查材料思想汇报
2014/09/23 职场文书
优秀员工自荐书
2015/03/06 职场文书
意外事故赔偿协议书
2016/03/22 职场文书
作文之亲情600字
2019/09/23 职场文书