js高级程序设计学习之高级函数
安全的类型检测
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高级程序设计学习之高级函数的更多相关文章
- JavaScript高级程序设计学习(四)之引用类型
在javascript中也是有引用类型的,java同样如此. javascript常见也比较常用的引用类型就熟Object和Array. 一个对象和一个数组,这个在前后端分离开发中也用的最多.比如aj ...
- JavaScript高级程序设计学习(三)之变量、作用域和内存问题
这次讲的主要是变量,作用域和内存问题. 任何一门编程语言,都涉及这三个. 变量,比如全局变量,局部变量等,作用域,也分全局作用域和方法作用域,内存问题,在java中就涉及到一个垃圾回收的问题,由于ja ...
- JavaScript高级程序设计---学习笔记(一)
今天,2017.3.17开始利用课余时间仔细学习<JavaScript高级程序设计>,将需要掌握的知识点记录下来,争取把书里的所有代码敲一遍并掌握. 1.标识符命名最好是第一个字母小写,剩 ...
- javascript高级程序设计学习笔记
javascript高级程序设计,当枕头书已经好久了~zz 现在觉得自己在js的开发上遇到了一些瓶颈,归根究底还是基础太薄弱,所以重新刷一遍js高程希望有更新的认识. 一.javascript简介 ...
- JS高级程序设计学习笔记之第三章基本概念(语法,数据类型,流控制语句,函数)——查漏补缺
一.语法: 区分大小写; 2.标识符:就是指变量.函数.属性的名字,或者函数的参数 a.标志符的规则:①第一个字符必须是一个字母.下划线(_)或一个美元符号($). ...
- JS高级程序设计学习笔记——继承
我们知道,在OO语言中,继承可分为接口继承和实现继承.而ECMAScript的函数没有签名,不能实现“接口继承”,只能通过原型链实现“实现继承”. 在学习了各种继承模式之后,简单总结一下各种继承模式的 ...
- JS高级程序设计学习笔记1
javascript产生的原因: 在拨号上网时代,表单数据必须发送到服务器端才能验证输入值得有效性,JavaScript的研发就是为了解决这个问题,以便在客户端就验证输入值的有效性. ECMAScri ...
- JavaScript高级程序设计学习笔记--函数表达式
关于函数声明,它的一个重要特征就是函数声明提升,意思是在执行代码之间会读取函数声明,意思是在执行代码之前会先读取函数声明.这就意味着可以把函数声明放在调用它的语句 后面. sayHi(); funct ...
- js高级程序设计(七)函数表达式
定义函数的方式有两种:一种是函数声明,另一种就是函数表达式.函数声明的语法是这样的. function functionName(arg0, arg1, arg2) { //函数体 } Firefox ...
随机推荐
- The type or namespace name '****' could not be found
偶尔会在编译时出现“The type or namespace name '****' could not be found (are you missing a using directive or ...
- linux下读取系统内存的demo
#include <stdio.h> #define KIBIBYTE_SIZE 1024LL #define MEBIBYTE_SIZE 1048576LL #define GIBIBY ...
- 矢量切片(Vector tile)
说明:本月的主要工作都是围绕制作矢量切片这一个核心问题进行的,所以2月的主题就以这个问题为主,目前分支出来的一些内容主要包括了TMS(Tile map service),OpenLayers3中的Pr ...
- Android jni 编程4(对基本类型二维整型数组的操作)
Android jni 编程 对于整型二维数组操作: 类型一:传入二维整型数组,返回一个整型值 类型二:传入二维整型数组,返回一个二维整型数组 声明方法: private native int Sum ...
- JMeter使用简单教程
去Apache JMeter官网下载最新的Windows下的zip安装包并解压 进入JMeter安装目录下的bin目录,双击jmeter.bat,运行JMeter程序 打开测试计划主界 ...
- 学习笔记——Java字符串操作常用方法
1.创建字符串 最常用的是使用String类的构造方法:String s=new String("abcd"); 也可采用J2SE5.0添加的StringBuilder类的字符串构 ...
- SuperWebClient -一个基于CURL的.NET HTTP/HTTPS模拟神组件(1)
我们都知道,不管你是做爬虫也好,采集工具也罢,它们的HTTP/HTTPS模拟访问总是一个基础问题,我估计有很多人和我一样,虽然这样,那样的内置或是第三方类库用了很多,却总是会有一些不如意的问题存在,亦 ...
- AlloyTouch之select选择插件
原文地址:https://github.com/AlloyTeam/AlloyTouch/wiki/Simple-Select 写在前面 很多情况下,产品希望统一安卓和IOS select交互和样式. ...
- iOS回顾笔记( 01 )
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...
- Win7_x64_Oracle 安装 PL/SQL Developer
Win7逐渐成为现行主流的windows操作系统,其32和64位系统平分秋色.然而当下还没有64位的PL/SQL Developer问世,直接用32位的PL/SQL Developer连接Win7(6 ...