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

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

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

(0)

相关推荐

  • 编写跨浏览器的javascript代码必备[js多浏览器兼容写法]

    序号 操作 分类 IE(6.0) FireFox(2.0) Mozilla(1.5) 当前浏览器 备注 1 "." 访问tag的固有属性 OK OK OK OK 2 "." 访问tag的用户定义属性eg: <input type="checkbox" myattr="test"> OK NO NO OK 可以用getAttribute函数 替代 3 obj.getAttribute 访问tag的固有属性 OK OK

  • 深入理解javascript学习笔记(一) 编写高质量代码

    一.变量 •全局变量 JavaScript的两个特征,不自觉地创建出全局变量是出乎意料的容易.首先,你可以甚至不需要声明就可以使用变量:第二,JavaScript有隐含的全局概念,意味着你不声明的任何变量都会成为一个全局对象属性(不是真正意义上的全局变量,可以用delete删除) 复制代码 代码如下: function sum(x,y) { // result 未声明,为隐式全局变量 result = x + y; return result; } function foo() { // 使用任

  • 在iframe里的页面编写js,实现在父窗口上创建动画效果展开和收缩的div(不变动iframe父窗口代码)

    复制代码 代码如下: <%@ page contentType="text/html; charset=GBK" language="java"%> <%@ page import="com.jstrd.mm.business.sysmgr.monitor.logic.MMStock2BudgetLogic" %> <% String query = request.getParameter("query&

  • 最佳JS代码编写的14条技巧

    写任何编程代码,不同的开发者都会有不同的见解.但参考一下总是好的,下面是来自Javascript Toolbox发布的14条最佳JS代码编写技巧. 1. 总是使用 var 在javascript中,变量不是全局范围的就是函数范围的,使用var关键词将是保持变量简洁明了的关键.当声明一个或者是全局或者是函数级(function-level)的变量,需总是前置var关键词,下面的例子将强调不这样做潜在的问题. 不使用 Var 造成的问题 var i=0; // This is good - crea

  • 编写高质量JavaScript代码的基本要点

    才华横溢的Stoyan Stefanov,在他写的由O'Reilly初版的新书<JavaScript Patterns>(JavaScript模式)中,我想要是为我们的读者贡献其摘要,那会是件很美妙的事情.具体一点就是编写高质量JavaScript的一些要素,例如避免全局变量,使用单变量声明,在循环中预缓存length(长度),遵循代码阅读,以及更多. 此摘要也包括一些与代码不太相关的习惯,但对整体代码的创建息息相关,包括撰写API文档.执行同行评审以及运行JSLint.这些习惯和最佳做法可以

  • 编写可维护面向对象的JavaScript代码[翻译]

    Writing maintainable Object-Oriented (OO) JavaScript will save you money and make you popular. Don't believe me? Odds are that either you or someone else will come back and work with your code. Making that as painless an experience as possible will s

  • 深入理解JavaScript系列(1) 编写高质量JavaScript代码的基本要点

    具体一点就是编写高质量JavaScript的一些要素,例如避免全局变量,使用单变量声明,在循环中预缓存length(长度),遵循代码阅读,以及更多. 此摘要也包括一些与代码不太相关的习惯,但对整体代码的创建息息相关,包括撰写API文档.执行同行评审以及运行JSLint.这些习惯和最佳做法可以帮助你写出更好的,更易于理解和维护的代码,这些代码在几个月或是几年之后再回过头看看也是会觉得很自豪的. 书写可维护的代码(Writing Maintainable Code ) 软件bug的修复是昂贵的,并且

  • 编写Js代码要注意的几条规则

    1.不要大量使用document.write() 2.检查客户端支持对象的能力(渐进式)而不是检查其客户端,测试要使用的对象. 3.访问既有HTML中的内容而不是通过Js添加HTML(行为层与结构层分离) 4.不要使用专有DOM对象(例如IE的document.all) 5.将脚本放进一个.js文件而不是在HTML中到处可见. 6.对运行良好而且不用客户端编程的网站进行改进,而不是首先添加脚本然后添加非脚本的备用方案. 7.代码要保持独立,不要使用可能与其他脚本冲突的全局变量.(可用对象字面量)

  • 如何编写高质量JS代码

    想写出高效的javascript类库却无从下手: 尝试阅读别人的类库,却理解得似懂给懂: 打算好好钻研js高级函数,但权威书上的内容太零散, 即使记住"用法",但到要"用"的时候却没有想"法". 也许你和我一样,好像有一顾无形的力量约束着我们的计划,让我们一再认为知识面的局限性,致使我们原地踏步,难以向前跨越. 这段时间,各种作业.课程设计.实验报告,压力倍增.难得挤出一点点时间,绝不睡懒觉,整理总结往日所看的书,只为了可以离写自己的类库近一点.

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

    继续上一篇文章<如何编写高质量JS代码>今次整理一下javascript函数知识点. 2.使用函数 函数给程序员提供了主要的抽象功能,又提供实现机制.函数可以独立实现其他语言中的多个不同的特性,例如,过程.方法.构造函数,甚至类或模块. 2.1 理解函数调用.方法调用以及构造函数调用之间的不同 针对面向对象编程,函数.方法和类的构造函数是三种不同的概念. 使用模式: 1,函数调用 复制代码 代码如下: function hello(username){     return "hel

  • 如何编写高质量 JavaScript 代码

    目录 一.易阅读的代码 1.统一代码格式 2.去除魔术数字 3.单一功能原则 二.高性能的代码 1.优化算法 2.使用内置方法 3.减少作用域链查找 4.避免做重复的代码 三.健壮性的代码 1.使用新语法 2.随时可扩展 3.避免副作用 4.整合逻辑关注点 前段时间有一个叫做"人类高质量男性"的视频火了,相信很多同学都刷到过.所以今天给大家分享下,什么叫做"人类高质量代码",哈哈,开个玩笑. 其实分享的都是一些自己平时总结的小技巧,算是抛砖引玉吧,希望能给大家带来一

  • 12条写出高质量JS代码的方法

    书写出高质量的JS代码不仅让程序员看着舒服,更加能够提高程序的运行速度,以下就是我们的小编整理方法: 一.如何书写可维护性的代码 当出现bug的时候如果你能立马修复它是最好的,此时解决问题的四路在你脑中还是很清晰的.否则,你转移到其他任务或者bug是经过一定的时间才出现的,你忘了那个特定的代码,一段时间后再去查看这些代码就 需要: 1.花时间学习和理解这个问题 2.化时间是了解应该解决的问题代码 还有个问题,特别对于大的项目或是公司,修复bug的这位伙计不是写代码的那个人(且发现bug和修复bu

  • 编写高质量代码改善C#程序——使用泛型集合代替非泛型集合(建议20)

    软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类.不管是数组还是集合类,它们都有各自的优缺点.如何使用好集合是我们在开发过程中必须掌握的技巧.不要小看这些技巧,一旦在开发中使用了错误的集合或针对集合的方法,应用程序将会背离你的预想而运行. 建议20:使用泛型集合代替非泛型集合 在建议1中我们知道,如果要让代码高效运行,应该尽量避免装箱和拆箱,以及尽量减少转型.很遗憾,在微软提供给我们的第一代集合类型中没有做到这一点,下面我们看ArrayList这个类的使用情况: Array

  • 编写高质量代码的30条黄金守则(首选隐式类型转换)

    编写高质量代码的30条黄金守则-Day 01(首选隐式类型转换),本文由比特飞原创发布,转载务必在文章开头附带链接:https://www.byteflying.com/archives/6455 该系列文章由比特飞原创发布,计划用三个月时间写完全30篇文章,为大家提供编写高质量代码的一般准则. 1.概述 隐式类型转换是微软为了 C# 支持匿名类型而加入的,使用 var 通常可以使代码的可读性更强,甚至是帮我们解决一些严重的性能问题.为了清楚的明白 var 的作用机制,我们首先来看看编译器为 v

  • 高质量PHP代码的50个实用技巧必备(下)

    接着上篇<高质量PHP代码的50个实用技巧必备(上)>继续研究. 26. 避免直接写SQL, 抽象之 不厌其烦的写了太多如下的语句: <span style="color:#333333;font-family:''Helvetica, Arial, sans-serif'';">$query = "INSERT INTO users(name , email , address , phone) VALUES('$name' , '$email' ,

  • 高质量PHP代码的50个实用技巧必备(上)

    50个高质量PHP代码的实用技巧,希望大家喜欢. 1.不要使用相对路径 常常会看到: require_once('../../lib/some_class.php'); 该方法有很多缺点: 它首先查找指定的php包含路径, 然后查找当前目录.因此会检查过多路径.如果该脚本被另一目录的脚本包含, 它的基本目录变成了另一脚本所在的目录. 另一问题, 当定时任务运行该脚本, 它的上级目录可能就不是工作目录了.因此最佳选择是使用绝对路径: view sourceprint? define('ROOT'

随机推荐