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 ...
随机推荐
- windows 下odoo 不同版本安装运行问题
在开发测试不同版本odoo时,总会遇到在同一浏览器下运行出错的状况.虽然可以把不同版本分属不同的端口,但是登录标识会入写用户本地浏览器cookie,由于cookie只匹配域名及路径但是不区分端口, 所 ...
- [UWP]依赖属性1:概述
1. 概述 依赖属性(DependencyProperty)是UWP的核心概念,它是有DependencyObject提供的一种特殊的属性.由于UWP的几乎所有UI元素都是集成于DependencyO ...
- C#文本框允许使用ctrl+A
C#文本框中默认是不允许使用全选的.可以通过以下事件完成: private void textBox1_KeyDown(object sender, KeyEventArgs e) { if (e.C ...
- JavaScript嗅探执行神器-sniffer.js,你值得拥有!
一.热身--先看实战代码 a.js 文件 // 定义Wall及内部方法 ;(function(window, FUNC, undefined){ var name = 'wall'; Wall.say ...
- CentOS 6.4 升级python 2.6.6 到 python 2.7.9
1. 查看当前系统python的版本 python -V 2. 下载python软件包 cd /usr/local/src wget http://python.org/ftp/python/2.7. ...
- 初学HTML5
Document 什么是HTML5? 首先了解html:html即超文本语言,这是一种语法简单.结构清晰的语 解析型文档,他不同于其他的编程语言. html5就是html网页标记语言的第五次重大更新产 ...
- configparser配置文件模块
1.configparser的作用 mysql等很多文件的配置如下: [DEFAULT]ServerAliveInterval = 45Compression = yesCompressionLeve ...
- 关于RunLoop
首先我们要先认识一下这个RunLoop; NSRunLoop是Cocoa框架中的类,与之的Core Fundation 中CFRunLoopRef类. 这两者的区别是,前者不是线程安全的,而后者是线程 ...
- 读书笔记 effective c++ Item 26 尽量推迟变量的定义
1. 定义变量会引发构造和析构开销 每当你定义一种类型的变量时:当控制流到达变量的定义点时,你引入了调用构造函数的开销,当离开变量的作用域之后,你引入了调用析构函数的开销.对未使用到的变量同样会产生开 ...
- html中DTD
DTD 是一套关于标记符的语法规则.它是XML1.0版规格得一部分,是html文件的验证机制,属于html文件组成的一部分. DTD:三种文档类型:Strict(严格的).Transitional(过 ...