安全的类型检测

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. 我也来写:数据库访问类DBHelper

    一.前言 相信许多人都百度过:“.net 数据库访问类”.然后就出来一大堆SqlHelper.我也用过这些SqlHelper,也自己写过,一堆静态方法,开始使用起来感觉很不错,它们也确实在很多时候可以 ...

  2. SQL Server 的表数据简单操作(表数据查询)

    --表数据查询----数据的基本查询-- --数据简单的查询--select * | 字段名[,字段名2, ...] from 数据表名 [where 条件表达式] 例: use 商品管理数据库 go ...

  3. Struts2 Interceptors

    Alias Interceptor : 别名拦截器 <action name="someAction" class="com.examples.SomeAction ...

  4. centos6.5_x86_64安装Adobe Flash Player

    对x86_64的CentOS6.5系统,安装FireFox的Adobe Flash Player插件 安装插件,终端下输入命令:#wget http://linuxdownload.adobe.com ...

  5. 使用WMIC永久设置你的环境变量

    关于wmic,引用一下这位哥们儿的话http://technet.microsoft.com/en-us/library/bb742610.aspx: WMIC扩展WMI(Windows Manage ...

  6. 利用HTML5开发Android(5)---HTML5地理位置服务在Android中的应用

    Android中 Java代码 //启用地理定位 webSettings.setGeolocationEnabled(true); //设置定位的数据库路径 webSettings.setGeoloc ...

  7. spatialite-android-library 环境搭建

    spatialite-android-library项目介绍 搭建NDK开发环境 下载spatialite-android-library项目 搭建spatialite-android-library ...

  8. jQuery Capty 图片标题插件

    jQuery Capty是可以为图片添加漂亮的字幕的插件 文件包含: <link type="text/css" rel="stylesheet" hre ...

  9. 面试之路(13)-android apk之间共享数据的方式以及shareUserId详解

    1.通过content Provider/sharedPreferrence 2.通过shareUserId 我们详细介绍一下shareUserId: Android App Sandbox(andr ...

  10. 学习ELK之----01.建立ELK测试项目

    1. 建立一个4G内存的虚拟机,安装Linux系统(具体的版本以能运行Docker为准,我用的是Ubuntu-Server) 2. 安装Docker 3. 下载elk容器:# docker pull ...