安全的类型检测

function isArray(value){
return Object.prototype.toString.call(value) === "[object Array]";
} function isFunction(value){
return Object.prototype.toString.call(value) === "[object Function]";
} //检测原生JSON对象
function isRegExp(value){
return Object.prototype.toString.call(value) === "[object RegExp]";
} var isNativeJson = window.JSON && Object.prototype.toString.call(JSON) === "[object JSON]";

不过要注意Object.prototype.toString本身可能被改写。

作用域安全的构造函数

当使用new调用构造函数时,构造函数内用到的this对象会指向新创建的对象实例,如:

function Person (name) {
this.name = name;
}
var person = new Person("oliver");
console.log(person.name);

问题是当没有使用new操作符,直接调用构造函数,this会映射到全局对象window上,导致错误对象属性的意外增加:

function Person (name) {
this.name = name;
}
var person = Person("oliver");
console.log(window.name); //oliver

解决办法是创建一个作用域安全的构造函数:

function Person(name) {
if (this instanceof Person) { //如果this是Person的实例
this.name = name;
} else {
return new Person(name); //否则调用new操作符
}
}
var person1 = Person("oliver");
console.log(person1.name); //oliver
var person2 = new Person("troy");
console.log(person2.name); //troy
console.log(window.name); //""

但是,如果使用构造函数窃取模式的继承且不实用原型链,那么这个继承很可能被破坏如:

function Person(name) {
if (this instanceof Person) { //如果this是Person的实例
this.name = name;
} else {
return new Person(name); //否则调用new操作符
}
}
function People (name,age) {
Person.call(this, name);
this.age = age;
}
var p = new People("Oliver", 18);
console.log(p.name); //undefined
console.log(p.age); //18

结合使用原型链或者寄生组合则可以解决这个问题:

function Person(name) {
if (this instanceof Person) { //如果this是Person的实例
this.name = name;
} else {
return new Person(name); //否则调用new操作符
}
}
function People (name,age) {
Person.call(this, name);
this.age = age;
}
People.prototype = new Person(); //关键点
var p = new People("Oliver", 18);
console.log(p.name); //Oliver
console.log(p.age); //18

惰性载入函数

惰性函数表示函数执行的分支仅会发生一次。有两种实现惰性载入函数的方式,第一种就是在函数被调用时在处理函数。在第一次调用的过程中,该函数被覆盖为另一个按合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支了。

function createXHR () {
if (typeof XMLHttpRequest !== "undefined") {
createXHR = function () { //关键点
return new XMLHttpRequest();
}
} else if (typeof ActiveXObject !== "undefined") {
createXHR = function () { //关键点
return new ActiveXObject(["MSXML2.XMLHttp"]);
}
} else {
createXHR = function () { //关键点
throw new Error("No XHR object available.");
}
}
return createXHR(); //关键点
}

第二种实现惰性载入函数的方式就是在声明函数时就指定适当的函数。这样,第一次调用函数时就不会损失性能了,而在代码首次加载时会损失一些性能。

var createXHR = (function() {
if (typeof XMLHttpRequest !== "undefined") {
return function () { //关键点
return new XMLHttpRequest();
}
} else if (typeof ActiveXObject !== "undefined") {
return function () { //关键点
return new ActiveXObject(["MSXML2.XMLHttp"]);
}
} else {
return function () { //关键点
throw new Error("No XHR object available.");
}
}
})();

这个例子中使用的技巧是创建一个匿名、自执行的函数,用以确定应该使用哪一个函数实现。

函数绑定

函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用, 以便在将函数作为变量传递的同时保留代码的执行环境。

var handler = {
message: "Event handled",
handleClick: function(event){
console.log(this.message);
}
}; handler.handleClick(); var a = handler.handleClick;
a(); var btn1 = document.getElementById('my-btn1');
btn1.addEventListener('click',handler.handleClick,false); var btn2 = document.getElementById('my-btn2');
btn2.addEventListener('click',function(){
handler.handleClick();
},false);

btn1点击后显示undefined,btn2利用闭包来修正这个问题

由于代码之中存在着this变量,而this在当前环境下指向确定的对象,但是当更改代码的执行环境时,就会出现问题了。为了解决这个问题, javascript函数库中实现了一个bind() 函数来解决这个问题。

一个简单的bind() 函数接收一个函数和一个环境, 并返回一个在给定环境中调用给定函数的函数, 并且将所有参数原封不动传递过去。 语法如下:

function bind(fn, context) {
return function() {
return fn.apply(context, arguments);
}
}

注意这里使用的arguments并不是bind() 的, 是内部函数的。

var handler = {
message: "Event handled",
handleClick: function(event) {
alert(this.message);
}
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));

ECMAScript5为所有函数定义了一个原生的bind() 方法, 进一步简化了操作。

var handler = {
message: "Event handled",
handleClick: function(event) {
alert(this.message);
}
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler));

它们主要用于事件处理程序以及setTimeout() 和setInterval()。 然而被绑定函数与普通函数相比有更多的开销, 它们需要更多内存, 同时也因为多重函数调用稍微慢一些, 所以最好只在必要时使用。

函数柯里化

它用于创建已经设置好了一个或多个参数的函数。 函数柯里化的基本方法是: 使用一个闭包返回一个函数。 当函数被调用时, 返回的函数还需要设置一些传入的参数。

柯里化函数通常由以下步骤动态的创建: 调用另一个函数并为它传入要柯里化的函数和必要参数。 下面是创建柯里化函数的通用方式:

function curry(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
}
}

函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的bind函数

function bind(fn,contenxt) {
var args = Array.prototype.slice.call(arguments,2);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.cancat(innerArgs);
return fn.apply(context,finalArgs);
}
}

当你想除了event对象再额外给事件处理程序传递参数时,这非常有用。

EventUtil.addHandler(btn, "click", bind(handler.handleClick,handler,"my-btn"));

ES5的bind方法也实现了函数柯里化,只需要在this值后再传另一个参数

EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler,"my-btn"));

函数绑定和柯里化都不应滥用,因为每个函数会带来额外的开销

js高级程序设计学习之高级函数的更多相关文章

  1. JavaScript高级程序设计学习(四)之引用类型

    在javascript中也是有引用类型的,java同样如此. javascript常见也比较常用的引用类型就熟Object和Array. 一个对象和一个数组,这个在前后端分离开发中也用的最多.比如aj ...

  2. JavaScript高级程序设计学习(三)之变量、作用域和内存问题

    这次讲的主要是变量,作用域和内存问题. 任何一门编程语言,都涉及这三个. 变量,比如全局变量,局部变量等,作用域,也分全局作用域和方法作用域,内存问题,在java中就涉及到一个垃圾回收的问题,由于ja ...

  3. JavaScript高级程序设计---学习笔记(一)

    今天,2017.3.17开始利用课余时间仔细学习<JavaScript高级程序设计>,将需要掌握的知识点记录下来,争取把书里的所有代码敲一遍并掌握. 1.标识符命名最好是第一个字母小写,剩 ...

  4. javascript高级程序设计学习笔记

    javascript高级程序设计,当枕头书已经好久了~zz  现在觉得自己在js的开发上遇到了一些瓶颈,归根究底还是基础太薄弱,所以重新刷一遍js高程希望有更新的认识. 一.javascript简介 ...

  5. JS高级程序设计学习笔记之第三章基本概念(语法,数据类型,流控制语句,函数)——查漏补缺

    一.语法: 区分大小写; 2.标识符:就是指变量.函数.属性的名字,或者函数的参数 a.标志符的规则:①第一个字符必须是一个字母.下划线(_)或一个美元符号($).                   ...

  6. JS高级程序设计学习笔记——继承

    我们知道,在OO语言中,继承可分为接口继承和实现继承.而ECMAScript的函数没有签名,不能实现“接口继承”,只能通过原型链实现“实现继承”. 在学习了各种继承模式之后,简单总结一下各种继承模式的 ...

  7. JS高级程序设计学习笔记1

    javascript产生的原因: 在拨号上网时代,表单数据必须发送到服务器端才能验证输入值得有效性,JavaScript的研发就是为了解决这个问题,以便在客户端就验证输入值的有效性. ECMAScri ...

  8. JavaScript高级程序设计学习笔记--函数表达式

    关于函数声明,它的一个重要特征就是函数声明提升,意思是在执行代码之间会读取函数声明,意思是在执行代码之前会先读取函数声明.这就意味着可以把函数声明放在调用它的语句 后面. sayHi(); funct ...

  9. js高级程序设计(七)函数表达式

    定义函数的方式有两种:一种是函数声明,另一种就是函数表达式.函数声明的语法是这样的. function functionName(arg0, arg1, arg2) { //函数体 } Firefox ...

随机推荐

  1. 算法笔记_017:递归执行顺序的探讨(Java)

    目录 1 问题描述 2 解决方案 2.1 问题化简 2.2 定位输出测试 2.3 回顾总结 1 问题描述 最近两天在思考如何使用蛮力法解决旅行商问题(此问题,说白了就是如何求解n个不同字母的所有不同排 ...

  2. Asp.net mvc 知多少(六)

    本系列主要翻译自<ASP.NET MVC Interview Questions and Answers >- By Shailendra Chauhan,想看英文原版的可访问http:/ ...

  3. Openstack容器项目之Magnum

    本文以Newton版本为例. 1.Magnum简介 Magnum项目通过Openstack API能够在Openstack中创建基于容器的服务,但它本身并不直接对容器进行操作,而是通过Kubernet ...

  4. c#入门系列——基础篇

    c#与VB的区别 刚接触c#发现c#与vb还是有所不同的--它可以在控制台显示.它比vb多出来了一些东西.代码规范上跟VB也稍有不同.....暂时就发现这么多,正在努力发现中. c#的代码结构     ...

  5. shell变量数学运算

    shell变量数学运算 #!/bin/sh # 本脚本说明shell脚本中变量运算的用法 # 错误的用法var=1var=$var+1echo $var 输出:1+1 # 第一种用法,letvar=1 ...

  6. 【12c】扩展数据类型(Extended Data Types)-- MAX_STRING_SIZE

    [12c]扩展数据类型(Extended Data Types)-- MAX_STRING_SIZE 在12c中,与早期版本相比,诸如VARCHAR2, NAVARCHAR2以及 RAW这些数据类型的 ...

  7. JVM内存

    大多数 JVM 将内存区域划分为 Method Area(Non-Heap)(方法区) ,Heap(堆) , Program Counter Register(程序计数器) ,   VM Stack( ...

  8. 在IntelliJ IDEA 13中配置OpenCV的Java开发环境

    准备工作: 下载IDEA 13(这里以版本13为例,后面简称IDEA): 下载Java JDK(用于配置基本的Java开发环境): 下载OpenCV 2.4.9(这里以版本2.4.9为例,据这篇文章说 ...

  9. 基于nginx+lua+redis高性能api应用实践

    基于nginx+lua+redis高性能api应用实践 前言 比较传统的服务端程序(PHP.FAST CGI等),大多都是通过每产生一个请求,都会有一个进程与之相对应,请求处理完毕后相关进程自动释放. ...

  10. WP8.1开发中ListView控件加载图列表的简单使用(1)

    我也是刚接触WP编程没几个月,就是在这段时间一直闲着没事,然后又比较喜欢WP这款系统,就学习了WP这方面的开发言语,自学是很困难的,掌握这方面的资料不多,很初级,就是自己在网上找资料学习过程中,看到别 ...